diff --git a/Ide/Code/FindSymbol.cpp b/Ide/Code/FindSymbol.cpp --- a/Ide/Code/FindSymbol.cpp +++ b/Ide/Code/FindSymbol.cpp @@ -1,661 +1,661 @@ #include #include #include "Lgi.h" #include "LgiIde.h" #include "FindSymbol.h" #include "LList.h" #include "GProcess.h" #include "GToken.h" #include "GEventTargetThread.h" #include "GTextFile.h" #include "ParserCommon.h" #if 1 #include "GParseCpp.h" #endif #include "resdefs.h" #define MSG_TIME_MS 1000 #define DEBUG_FIND_SYMBOL 0 #define DEBUG_NO_THREAD 1 // #define DEBUG_FILE "dante_config_common.h" int SYM_FILE_SENT = 0; class FindSymbolDlg : public GDialog { AppWnd *App; LList *Lst; FindSymbolSystem *Sys; public: FindSymResult Result; FindSymbolDlg(GViewI *parent, FindSymbolSystem *sys); ~FindSymbolDlg(); int OnNotify(GViewI *v, int f); void OnCreate(); bool OnViewKey(GView *v, GKey &k); GMessage::Result OnEvent(GMessage *m); }; int ScoreCmp(FindSymResult **a, FindSymResult **b) { return (*b)->Score - (*a)->Score; } #define USE_HASH 1 struct FindSymbolSystemPriv : public GEventTargetThread { struct FileSyms { GString Path; int Platforms; GString::Array *Inc; GArray Defs; bool IsSource; bool IsHeader; bool IsPython; bool Parse(GAutoWString Source) { IsSource = false; IsHeader = false; IsPython = false; Defs.Length(0); bool Status = false; char *Ext = LgiGetExtension(Path); if (Ext) { IsSource = !_stricmp(Ext, "c") || !_stricmp(Ext, "cpp") || !_stricmp(Ext, "cxx"); IsHeader = !_stricmp(Ext, "h") || !_stricmp(Ext, "hpp") || !_stricmp(Ext, "hxx"); IsPython = !_stricmp(Ext, "py"); if (IsSource || IsHeader) Status = BuildCppDefnList(Path, Source, Defs, DefnNone); else if (IsPython) Status = BuildPyDefnList(Path, Source, Defs, DefnNone); } return Status; } }; int hApp; GArray IncPaths; #if USE_HASH LHashTbl, FileSyms*> Files; #else GArray Files; #endif - uint32 Tasks; + uint32_t Tasks; uint64 MsgTs; bool DoingProgress; FindSymbolSystemPriv(int appSinkHnd) : hApp(appSinkHnd), GEventTargetThread("FindSymbolSystemPriv") { Tasks = 0; MsgTs = 0; DoingProgress = false; } ~FindSymbolSystemPriv() { // Wait for the queue of messages to complete... while (GetQueueSize()) LgiSleep(1); // End the thread... EndThread(); // Clean up mem Files.DeleteObjects(); } void Log(const char *Fmt, ...) { va_list Arg; va_start(Arg, Fmt); GString s; s.Printf(Arg, Fmt); va_end(Arg); if (s.Length()) GEventSinkMap::Dispatch.PostEvent(hApp, M_APPEND_TEXT, (GMessage::Param)NewStr(s), AppWnd::BuildTab); } #if !USE_HASH int GetFileIndex(GString &Path) { for (unsigned i=0; iPath) return i; } return -1; } #endif bool AddFile(GString Path, int Platforms) { // Already added? #if USE_HASH FileSyms *f = Files.Find(Path); if (f) { if (Platforms && f->Platforms == 0) f->Platforms = Platforms; return true; } #else int Idx = GetFileIndex(Path); #ifdef DEBUG_FILE bool Debug = false; if ((Debug = Path.Find(DEBUG_FILE) >= 0)) printf("%s:%i - AddFile(%s) = %i\n", _FL, Path.Get(), Idx); #endif if (Idx >= 0) { if (Platforms && Files[Idx]->Platforms == 0) Files[Idx]->Platforms = Platforms; return true; } FileSyms *f; #endif if (!FileExists(Path)) return false; LgiAssert(!LgiIsRelativePath(Path)); // Setup the file sym data... f = new FileSyms; if (!f) return false; f->Path = Path; f->Platforms = Platforms; f->Inc = IncPaths.Length() ? IncPaths.Last() : NULL; #if USE_HASH Files.Add(Path, f); #else Files.Add(f); #endif // Parse for headers... GTextFile Tf; if (!Tf.Open(Path, O_READ) || Tf.GetSize() < 4) { LgiTrace("%s:%i - Error: GTextFile.Open(%s) failed.\n", _FL, Path.Get()); return false; } GAutoString Source = Tf.Read(); GArray Headers; GArray EmptyInc; if (BuildHeaderList(Source, Headers, f->Inc ? *f->Inc : EmptyInc, false)) { for (unsigned i=0; iPath.Get(), StrlenW(f->Source)); #endif return f->Parse(GAutoWString(Utf8ToWide(Source))); } bool ReparseFile(GString Path) { #if USE_HASH FileSyms *f = Files.Find(Path); int Platform = f ? f->Platforms : 0; #else int Idx = GetFileIndex(Path); int Platform = Idx >= 0 ? Files[Idx]->Platforms : 0; #endif if (!RemoveFile(Path)) return false; return AddFile(Path, Platform); } bool RemoveFile(GString Path) { #if USE_HASH FileSyms *f = Files.Find(Path); if (!f) return false; Files.Delete(Path); delete f; #else int Idx = GetFileIndex(Path); if (Idx < 0) return false; delete Files[Idx]; Files.DeleteAt(Idx); #endif return true; } GMessage::Result OnEvent(GMessage *Msg) { switch (Msg->Msg()) { case M_FIND_SYM_REQUEST: { GAutoPtr Req((FindSymRequest*)Msg->A()); bool AllPlatforms = (bool)Msg->B(); if (Req && Req->SinkHnd >= 0) { GString::Array p = Req->Str.SplitDelimit(" \t"); if (p.Length() == 0) break; GArray ClassMatches; GArray HdrMatches; GArray SrcMatches; // For each file... #if USE_HASH for (auto it : Files) { FileSyms *fs = it.value; #else for (unsigned f=0; fPath.Find(DEBUG_FILE) >= 0; if (Debug) printf("%s:%i - Searching '%s' with %i syms...\n", _FL, fs->Path.Get(), fs->Defs.Length()); #endif // Check platforms... if (!AllPlatforms && (fs->Platforms & PLATFORM_CURRENT) == 0) continue; // For each symbol... for (unsigned i=0; iDefs.Length(); i++) { DefnInfo &Def = fs->Defs[i]; // For each search term... bool Match = true; int ScoreSum = 0; for (unsigned n=0; nScore = ScoreSum; r->File = Def.File.Get(); r->Symbol = Def.Name.Get(); r->Line = Def.Line; if (Def.Type == DefnClass) ClassMatches.Add(r); else if (fs->IsHeader) HdrMatches.Add(r); else SrcMatches.Add(r); } } } } } ClassMatches.Sort(ScoreCmp); Req->Results.Add(ClassMatches); Req->Results.Add(HdrMatches); Req->Results.Add(SrcMatches); int Hnd = Req->SinkHnd; PostObject(Hnd, M_FIND_SYM_REQUEST, Req); } break; } case M_FIND_SYM_FILE: { uint64 Now = LgiCurrentTime(); GAutoPtr Params((FindSymbolSystem::SymFileParams*)Msg->A()); if (Params) { GString::Array Mime = LGetFileMimeType(Params->File).Split("/"); if (!Mime[0].Equals("image")) { if (Params->Action == FindSymbolSystem::FileAdd) AddFile(Params->File, Params->Platforms); else if (Params->Action == FindSymbolSystem::FileRemove) RemoveFile(Params->File); else if (Params->Action == FindSymbolSystem::FileReparse) ReparseFile(Params->File); } } if (Now - MsgTs > MSG_TIME_MS) { MsgTs = Now; DoingProgress = true; - uint32 Remaining = (uint32) (Tasks - GetQueueSize()); + uint32_t Remaining = (uint32_t) (Tasks - GetQueueSize()); if (Remaining > 0) Log("FindSym: %i of %i (%.1f%%)\n", Remaining, Tasks, (double)Remaining * 100.0 / MAX(Tasks, 1)); } else if (GetQueueSize() == 0 && MsgTs) { if (DoingProgress) { Log("FindSym: Done.\n"); DoingProgress = false; } MsgTs = 0; Tasks = 0; } SYM_FILE_SENT--; break; } case M_FIND_SYM_INC_PATHS: { GAutoPtr Paths((GString::Array*)Msg->A()); if (Paths) IncPaths.Add(Paths.Release()); break; } default: { LgiAssert(!"Implement handler for message."); break; } } return 0; } }; int AlphaCmp(LListItem *a, LListItem *b, NativeInt d) { return stricmp(a->GetText(0), b->GetText(0)); } FindSymbolDlg::FindSymbolDlg(GViewI *parent, FindSymbolSystem *sys) { Lst = NULL; Sys = sys; SetParent(parent); if (LoadFromResource(IDD_FIND_SYMBOL)) { MoveToCenter(); GViewI *f; if (GetViewById(IDC_STR, f)) f->Focus(true); GetViewById(IDC_RESULTS, Lst); } } FindSymbolDlg::~FindSymbolDlg() { } void FindSymbolDlg::OnCreate() { RegisterHook(this, GKeyEvents); } bool FindSymbolDlg::OnViewKey(GView *v, GKey &k) { switch (k.vkey) { case VK_UP: case VK_DOWN: case VK_PAGEDOWN: case VK_PAGEUP: { return Lst->OnKey(k); break; } } return false; } GMessage::Result FindSymbolDlg::OnEvent(GMessage *m) { switch (m->Msg()) { case M_FIND_SYM_REQUEST: { GAutoPtr Req((FindSymRequest*)m->A()); if (Req) { GString Str = GetCtrlName(IDC_STR); if (Str == Req->Str) { Lst->Empty(); List Ls; GString s; int CommonPathLen = 0; for (unsigned i=0; iResults.Length(); i++) { FindSymResult *r = Req->Results[i]; if (i) { char *a = s.Get(); char *a_end = strrchr(a, DIR_CHAR); char *b = r->File.Get(); char *b_end = strrchr(b, DIR_CHAR); int Common = 0; while ( *a && a <= a_end && *b && b <= b_end && ToLower(*a) == ToLower(*b)) { Common++; a++; b++; } if (i == 1) CommonPathLen = Common; else CommonPathLen = MIN(CommonPathLen, Common); } else s = r->File; } for (unsigned i=0; iResults.Length(); i++) { FindSymResult *r = Req->Results[i]; LListItem *it = new LListItem; if (it) { GString Ln; Ln.Printf("%i", r->Line); it->SetText(r->File.Get() + CommonPathLen, 0); it->SetText(Ln, 1); it->SetText(r->Symbol, 2); Ls.Insert(it); } } Lst->Insert(Ls); Lst->ResizeColumnsToContent(); } } break; } } return GDialog::OnEvent(m); } int FindSymbolDlg::OnNotify(GViewI *v, int f) { switch (v->GetId()) { case IDC_STR: { if (f != VK_RETURN) { char *Str = v->Name(); if (Str && strlen(Str) > 2) { // Create a search Sys->Search(AddDispatch(), Str, true); } } break; } case IDC_RESULTS: { if (f == GNotifyItem_DoubleClick) { // Fall throu } else break; } case IDOK: { if (Lst) { LListItem *i = Lst->GetSelected(); if (i) { Result.File = i->GetText(0); Result.Line = atoi(i->GetText(1)); } } EndModal(true); break; } case IDCANCEL: { EndModal(false); break; } } return GDialog::OnNotify(v, f); } /////////////////////////////////////////////////////////////////////////// FindSymbolSystem::FindSymbolSystem(int AppHnd) { d = new FindSymbolSystemPriv(AppHnd); } FindSymbolSystem::~FindSymbolSystem() { delete d; } FindSymResult FindSymbolSystem::OpenSearchDlg(GViewI *Parent) { FindSymbolDlg Dlg(Parent, this); Dlg.DoModal(); return Dlg.Result; } bool FindSymbolSystem::SetIncludePaths(GString::Array &Paths) { GString::Array *a = new GString::Array; if (!a) return false; *a = Paths; return d->PostEvent(M_FIND_SYM_INC_PATHS, (GMessage::Param)a); } bool FindSymbolSystem::OnFile(const char *Path, SymAction Action, int Platforms) { if (d->Tasks == 0) d->MsgTs = LgiCurrentTime(); d->Tasks++; GAutoPtr Params(new FindSymbolSystem::SymFileParams); Params->File = Path; Params->Action = Action; Params->Platforms = Platforms; if (d->PostObject(d->GetHandle(), M_FIND_SYM_FILE, Params)) { SYM_FILE_SENT++; } return false; } void FindSymbolSystem::Search(int ResultsSinkHnd, const char *SearchStr, bool AllPlat) { FindSymRequest *Req = new FindSymRequest(ResultsSinkHnd); if (Req) { Req->Str = SearchStr; d->PostEvent(M_FIND_SYM_REQUEST, (GMessage::Param)Req, (GMessage::Param)AllPlat); } } diff --git a/Ide/Code/GDebugContext.cpp b/Ide/Code/GDebugContext.cpp --- a/Ide/Code/GDebugContext.cpp +++ b/Ide/Code/GDebugContext.cpp @@ -1,809 +1,809 @@ #include "Lgi.h" #include "LgiIde.h" #include "IdeProject.h" #include "GTextLog.h" #include "LList.h" enum DebugMessages { M_RUN_STATE = M_USER + 100, M_ON_CRASH, M_FILE_LINE, }; class GDebugContextPriv : public LMutex { public: GDebugContext *Ctx; AppWnd *App; IdeProject *Proj; bool InDebugging; GAutoPtr Db; GAutoString Exe, Args; GString SeekFile; int SeekLine; bool SeekCurrentIp; GString MemDumpAddr; NativeInt MemDumpStart; - GArray MemDump; + GArray MemDump; GDebugContextPriv(GDebugContext *ctx) : LMutex("GDebugContextPriv") { Ctx = ctx; MemDumpStart = 0; App = NULL; Proj = NULL; InDebugging = false; SeekLine = 0; SeekCurrentIp = false; } ~GDebugContextPriv() { #if DEBUG_SESSION_LOGGING LgiTrace("~GDebugContextPriv freeing debugger...\n"); #endif Db.Reset(); #if DEBUG_SESSION_LOGGING LgiTrace("...done.\n"); #endif } void UpdateThreads() { if (!Db || !Ctx->Threads || !InDebugging) { LgiTrace("%s:%i - No debugger.\n", _FL); return; } GArray Threads; int CurrentThread = -1; if (!Db->GetThreads(Threads, &CurrentThread)) { LgiTrace("%s:%i - Failed to get threads from debugger.\n", _FL); return; } Ctx->Threads->Empty(); for (unsigned i=0; iSetText(f, 0); it->SetText(Sp, 1); Ctx->Threads->Insert(it); it->Select(ThreadId == CurrentThread); } } } Ctx->Threads->SendNotify(); } void UpdateCallStack() { if (Db && Ctx->CallStack && InDebugging) { GArray Stack; if (Db->GetCallStack(Stack)) { Ctx->CallStack->Empty(); for (int i=0; iSetText(f, 0); it->SetText(Sp, 1); } else { it->SetText(Stack[i], 1); } } else { it->SetText(Stack[i], 1); } Ctx->CallStack->Insert(it); } Ctx->CallStack->SendNotify(); } } } void Log(const char *Fmt, ...) { if (Ctx->DebuggerLog) { va_list Arg; va_start(Arg, Fmt); GStreamPrintf(Ctx->DebuggerLog, 0, Fmt, Arg); va_end(Arg); } } int Main() { return 0; } }; GDebugContext::GDebugContext(AppWnd *App, IdeProject *Proj, const char *Exe, const char *Args, bool RunAsAdmin) { Watch = NULL; Locals = NULL; DebuggerLog = NULL; ObjectDump = NULL; Registers = NULL; Threads = NULL; d = new GDebugContextPriv(this); d->App = App; d->Proj = Proj; d->Exe.Reset(NewStr(Exe)); if (d->Db.Reset(CreateGdbDebugger())) { GFile::Path p = Exe; p--; if (!d->Db->Load(this, Exe, Args, RunAsAdmin, p)) { d->Log("Failed to load '%s' into debugger.\n", d->Exe.Get()); d->Db.Reset(); } } } GDebugContext::~GDebugContext() { DeleteObj(d); } GMessage::Param GDebugContext::OnEvent(GMessage *m) { switch (MsgCode(m)) { case M_ON_CRASH: { #if DEBUG_SESSION_LOGGING LgiTrace("GDebugContext::OnEvent(M_ON_CRASH)\n"); #endif d->UpdateCallStack(); break; } case M_FILE_LINE: { #if DEBUG_SESSION_LOGGING LgiTrace("GDebugContext::OnEvent(M_FILE_LINE)\n"); #endif GString File; { LMutex::Auto a(d, _FL); File = d->SeekFile; } // printf("%s:%i - %s %i\n", _FL, File.Get(), d->SeekLine); if (File && d->SeekLine > 0) d->App->GotoReference(File, d->SeekLine, d->SeekCurrentIp); break; } } return 0; } bool GDebugContext::SetFrame(int Frame) { return d->Db ? d->Db->SetFrame(Frame) : false; } bool GDebugContext::DumpObject(const char *Var, const char *Val) { if (!d->Db || !Var || !ObjectDump || !d->InDebugging) return false; ObjectDump->Name(NULL); if (Val && *Val == '{') { // Local parse the value into the dump int Depth = 0; const char *Start = NULL; char Spaces[256]; memset(Spaces, ' ', sizeof(Spaces)); int IndentShift = 2; #define Emit() \ if (Start) \ { \ auto bytes = s - Start; \ const char *last = s-1; while (last > Start && strchr(WhiteSpace, *last)) last--; \ ObjectDump->Print("%.*s%.*s%s\n", Depth<Print("%.*s%c\n", Depth<Print("%.*s%c\n", Depth<Db->PrintObject(Var, ObjectDump); } bool GDebugContext::UpdateRegisters() { if (!d->Db || !Registers || !d->InDebugging) return false; return d->Db->GetRegisters(Registers); } bool GDebugContext::UpdateLocals() { if (!Locals || !d->Db || !d->InDebugging) return false; GArray Vars; if (!d->Db->GetVariables(true, Vars, false)) return false; Locals->Empty(); for (int i=0; iSetText("local", 0); break; case GDebugger::Variable::Global: it->SetText("global", 0); break; case GDebugger::Variable::Arg: it->SetText("arg", 0); break; } char s[256]; switch (v.Value.Type) { case GV_BOOL: { it->SetText(v.Type ? v.Type.Get() : "bool", 1); sprintf_s(s, sizeof(s), "%s", v.Value.Value.Bool ? "true" : "false"); break; } case GV_INT32: { it->SetText(v.Type ? v.Type.Get() : "int32", 1); sprintf_s(s, sizeof(s), "%i (0x%x)", v.Value.Value.Int, v.Value.Value.Int); break; } case GV_INT64: { it->SetText(v.Type ? v.Type.Get() : "int64", 1); sprintf_s(s, sizeof(s), LPrintfInt64, v.Value.Value.Int64); break; } case GV_DOUBLE: { it->SetText(v.Type ? v.Type.Get() : "double", 1); sprintf_s(s, sizeof(s), "%f", v.Value.Value.Dbl); break; } case GV_STRING: { it->SetText(v.Type ? v.Type.Get() : "string", 1); sprintf_s(s, sizeof(s), "%s", v.Value.Value.String); break; } case GV_WSTRING: { it->SetText(v.Type ? v.Type.Get() : "wstring", 1); #ifdef MAC GAutoString tmp(WideToUtf8(v.Value.Value.WString)); sprintf_s(s, sizeof(s), "%s", tmp.Get()); #else sprintf_s(s, sizeof(s), "%S", v.Value.Value.WString); #endif break; } case GV_VOID_PTR: { it->SetText(v.Type ? v.Type.Get() : "void*", 1); sprintf_s(s, sizeof(s), "%p", v.Value.Value.Ptr); break; } default: { sprintf_s(s, sizeof(s), "notimp(%s)", GVariant::TypeToString(v.Value.Type)); it->SetText(v.Type ? v.Type : s, 1); s[0] = 0; break; } } it->SetText(v.Name, 2); it->SetText(s, 3); Locals->Insert(it); } } Locals->ResizeColumnsToContent(); return true; } bool GDebugContext::UpdateWatches() { GArray Vars; for (GTreeItem *i = Watch->GetChild(); i; i = i->GetNext()) { GDebugger::Variable &v = Vars.New(); v.Name = i->GetText(0); v.Type = i->GetText(1); } printf("Update watches %i\n", (int)Vars.Length()); if (!d->Db->GetVariables(false, Vars, false)) return false; int Idx = 0; for (GTreeItem *i = Watch->GetChild(); i; i = i->GetNext(), Idx++) { GDebugger::Variable &v = Vars[Idx]; WatchItem *wi = dynamic_cast(i); if (!wi) { LgiTrace("%s:%i - Error: not watch item.\n", _FL); continue; } if (v.Name == (const char*)i->GetText(0)) { i->SetText(v.Type, 1); wi->SetValue(v.Value); } else { LgiTrace("%s:%i - Error: Not the same name.\n", _FL); } } return true; } void GDebugContext::UpdateCallStack() { d->UpdateCallStack(); } void GDebugContext::UpdateThreads() { d->UpdateThreads(); } bool GDebugContext::SelectThread(int ThreadId) { if (!d->Db) { LgiTrace("%s:%i - No debugger.\n", _FL); return false; } return d->Db->SetCurrentThread(ThreadId); } bool GDebugContext::ParseFrameReference(const char *Frame, GAutoString &File, int &Line) { if (!Frame) return false; const char *At = NULL, *s = Frame; while ((s = stristr(s, "at"))) { At = s; s += 2; } if (!At) return false; At += 3; if (!File.Reset(LgiTokStr(At))) return false; char *Colon = strchr(File, ':'); if (!Colon) return false; *Colon++ = 0; Line = atoi(Colon); return Line > 0; } bool GDebugContext::OnCommand(int Cmd) { #if DEBUG_SESSION_LOGGING LgiTrace("GDebugContext::OnCommand(%i)\n", Cmd); #endif switch (Cmd) { case IDM_START_DEBUG: { if (d->Db) { d->App->LoadBreakPoints(d->Db); d->Db->SetRunning(true); } break; } case IDM_CONTINUE: { if (d->Db) d->Db->SetRunning(true); break; } case IDM_ATTACH_TO_PROCESS: { break; } case IDM_STOP_DEBUG: { if (d->Db) return d->Db->Unload(); else return false; break; } case IDM_PAUSE_DEBUG: { if (d->Db) d->Db->SetRunning(false); break; } case IDM_RESTART_DEBUGGING: { if (d->Db) d->Db->Restart(); break; } case IDM_RUN_TO: { break; } case IDM_STEP_INTO: { if (d->Db) d->Db->StepInto(); break; } case IDM_STEP_OVER: { if (d->Db) d->Db->StepOver(); break; } case IDM_STEP_OUT: { if (d->Db) d->Db->StepOut(); break; } case IDM_TOGGLE_BREAKPOINT: { break; } default: return false; } return true; } void GDebugContext::OnUserCommand(const char *Cmd) { if (d->Db) d->Db->UserCommand(Cmd); } -void NonPrintable(uint64 ch, uint8 *&out, ssize_t &len) +void NonPrintable(uint64 ch, uint8_t *&out, ssize_t &len) { if (ch == '\r') { if (len > 1) { *out++ = '\\'; *out++ = 'r'; len -= 2; } else LgiAssert(0); } else if (ch == '\n') { if (len > 1) { *out++ = '\\'; *out++ = 'n'; len -= 2; } else LgiAssert(0); } else if (len > 0) { *out++ = '.'; len--; } } void GDebugContext::FormatMemoryDump(int WordSize, int Width, bool InHex) { if (!MemoryDump) { LgiTrace("%s:%i - No MemoryDump.\n", _FL); return; } if (d->MemDump.Length() == 0) { MemoryDump->Name("No data."); return; } if (!Width) Width = 16 / WordSize; if (!WordSize) WordSize = 1; // Format output to the mem dump window GStringPipe p; int LineBytes = WordSize * Width; GPointer ptr; ptr.u8 = &d->MemDump[0]; for (NativeInt i = 0; i < d->MemDump.Length(); i += LineBytes) { // GPointer Start = ptr; ssize_t DisplayBytes = MIN(d->MemDump.Length() - i, LineBytes); ssize_t DisplayWords = DisplayBytes / WordSize; NativeInt iAddr = d->MemDumpStart + i; p.Print("%p ", iAddr); char Char[256] = ""; - uint8 *ChPtr = (uint8*)Char; + uint8_t *ChPtr = (uint8_t*)Char; ssize_t Len = sizeof(Char); for (int n=0; nName(a); } void GDebugContext::OnMemoryDump(const char *Addr, int WordSize, int Width, bool IsHex) { if (MemoryDump && d->Db) { MemoryDump->Name(NULL); GString ErrMsg; if (d->Db->ReadMemory(d->MemDumpAddr = Addr, 1024, d->MemDump, &ErrMsg)) { d->MemDumpStart = (int)d->MemDumpAddr.Int(16); FormatMemoryDump(WordSize, Width, IsHex); } else { MemoryDump->Name(ErrMsg ? ErrMsg : "ReadMemory failed."); } } } void GDebugContext::OnState(bool Debugging, bool Running) { #if DEBUG_SESSION_LOGGING LgiTrace("GDebugContext::OnState(%i, %i)\n", Debugging, Running); #endif if (d->InDebugging != Debugging && d->Db) { d->InDebugging = Debugging; } if (d->App) { d->App->OnDebugState(Debugging, Running); } #if DEBUG_SESSION_LOGGING LgiTrace("GDebugContext::OnState(%i, %i) ###ENDED###\n", Debugging, Running); #endif // This object may be deleted at this point... don't access anything. } void GDebugContext::OnFileLine(const char *File, int Line, bool CurrentIp) { if (!d->App) { printf("%s:%i - No app.\n", _FL); return; } if (!File || Line < 1) { LgiTrace("%s:%i - Error: No File or Line... one or both must be valid.\n", _FL); LgiAssert(!"Invalid Param"); return; } if (d->App->InThread()) { d->App->GotoReference(File, Line, CurrentIp); } else { { LMutex::Auto a(d, _FL); if (File) d->SeekFile = File; d->SeekLine = Line; d->SeekCurrentIp = CurrentIp; // printf("%s:%i - %s %i, %i\n", _FL, d->SeekFile.Get(), d->SeekLine, d->SeekCurrentIp); } d->App->PostEvent(M_FILE_LINE); } } ssize_t GDebugContext::Write(const void *Ptr, ssize_t Size, int Flags) { if (DebuggerLog && Ptr) { // LgiTrace("Write '%.*s'\n", Size, Ptr); return DebuggerLog->Write(Ptr, Size, 0); } return -1; } void GDebugContext::OnError(int Code, const char *Str) { if (DebuggerLog) DebuggerLog->Print("Error(%i): %s\n", Code, Str); } void GDebugContext::OnCrash(int Code) { d->App->PostEvent(M_ON_CRASH); } bool GDebugContext::OnBreakPoint(GDebugger::BreakPoint &b, bool Add) { if (!d->Db) { LgiTrace("%s:%i - No debugger loaded.\n", _FL); return false; } if (Add) return d->Db->SetBreakPoint(&b); else return d->Db->RemoveBreakPoint(&b); } diff --git a/Ide/Code/GDebugger.cpp b/Ide/Code/GDebugger.cpp --- a/Ide/Code/GDebugger.cpp +++ b/Ide/Code/GDebugger.cpp @@ -1,1486 +1,1486 @@ #include "Lgi.h" #include "GDebugger.h" #include "GSubProcess.h" #include "GToken.h" #include "GDocView.h" #include "GStringClass.h" #include "GString.h" #ifdef POSIX #include #include #include #include #endif #define DEBUG_SHOW_GDB_IO 0 #define ECHO_GDB_OUTPUT 0 const char sPrompt[] = "(gdb) "; class Callback { public: virtual GString GetResponse(const char *c) = 0; }; class Visualizer { public: virtual ~Visualizer() {} virtual bool Match(GString s) = 0; virtual bool Transform(GString name, GString val, Callback *Cb, GVariant &Value, GString &Detail) = 0; }; class GStringVis : public Visualizer { public: bool Match(GString s) { return s == "GString"; } bool Transform(GString name, GString val, Callback *Cb, GVariant &Value, GString &Detail) { GString::Array a = val.SplitDelimit("{} \t\r\n"); if (a.Length() == 3 && a[1] == "=") { void *Ptr = (void*)htoi64(a[2].Get()); if (Ptr == NULL) { Value = "NULL"; } else { GString cmd; cmd.Printf("p (char*)%s.Str->Str", name.Get()); GString r = Cb->GetResponse(cmd); auto Pos = r.Find("="); if (Pos >= 0) Value = r(Pos, r.Length()).Strip().Get(); else Value = r.Get(); } } return true; } }; class Gdb : public GDebugger, public LThread, public Callback { GDebugEvents *Events; GAutoPtr Sp; GString Exe, Args, InitDir; bool RunAsAdmin; bool AtPrompt; char Line[256], *LinePtr; int CurFrame; bool SetAsmType; bool SetPendingOn; GArray BreakPoints; int BreakPointIdx; int ProcessId; bool SuppressNextFileLine; GArray Vis; LMutex StateMutex; bool DebuggingProcess; bool Running; // Current location tracking GString CurFile; int CurLine; GString::Array Untagged; // Parse state enum ParseType { ParseNone, ParseBreakPoint, } ParseState; GString::Array BreakInfo; // Various output modes. GStream *OutStream; GString::Array *OutLines; enum ThreadState { Init, Looping, Exiting, ProcessError } State; void OnFileLine(const char *File, int Line, bool CurrentIp) { if (SuppressNextFileLine) { // printf("%s:%i - SuppressNextFileLine\n", _FL); SuppressNextFileLine = false; } else if (Events) { if (File) CurFile = File; if (Line > 0) CurLine = Line; if (CurFile && CurLine > 0) Events->OnFileLine(CurFile, CurLine, CurrentIp); /* else printf("%s:%i - Error: Cur loc incomplete: %s %i.\n", _FL, CurFile.Get(), CurLine); */ } } bool ParseLocation(GString::Array &p) { for (int i=0; i 0) { int At = 0; for (; At < a.Length() && stricmp(a[At], "at") != 0; At++) ; if (At < a.Length() - 1) // Found the 'at' { GString::Array ref = a[At+1].Split(":"); if (ref.Length() == 2) { OnFileLine(NativePath(ref[0]), (int)ref[1].Int(), true); return true; } } else { int Line = (int)a[0].Int(); if (Line) { OnFileLine(NULL, Line, true); return true; } } } } return false; } void SetState(bool is_debug, bool is_run) { if (StateMutex.Lock(_FL)) { if (is_debug != DebuggingProcess || is_run != Running) { DebuggingProcess = is_debug; Running = is_run; StateMutex.Unlock(); if (Events) { #if DEBUG_SESSION_LOGGING LgiTrace("Gdb::SetRunState(%i,%i) calling OnState...\n", is_debug, is_run); #endif Events->OnState(DebuggingProcess, Running); #if DEBUG_SESSION_LOGGING LgiTrace("Gdb::SetRunState(%i,%i) OnState returned.\n", is_debug, is_run); #endif } } else { StateMutex.Unlock(); } } } void Log(const char *Fmt, ...) { if (Events) { va_list Arg; va_start(Arg, Fmt); char Buf[512]; int Ch = vsprintf_s(Buf, sizeof(Buf), Fmt, Arg); va_end(Arg); Events->Write(Buf, Ch); } } void OnExit() { SetState(false, false); } char *NativePath(char *p) { for (char *c = p; *c; c++) { if (*c == '/' || *c == '\\') *c = DIR_CHAR; } return p; } void OnBreakPoint(GString f) { if (!f.Get() || ProcessId < 0) { // printf("Error: Param error: %s, %i (%s:%i)\n", f.Get(), ProcessId, _FL); return; } GString File, Line; GString::Array a = f.Split("at"); /* printf("%s:%i - a.len=%i\n", _FL, a.Length()); for (unsigned n=0; n 0) { e++; while (e < k.Length() && IsDigit(k(e))) e++; // printf("%s:%i - e=%i\n", _FL, e); GString::Array b = k(0, e).RSplit(":", 1); // printf("%s:%i - b.len=%i\n", _FL, b.Length()); if (b.Length() == 2) { File = b[0]; Line = b[1]; } } else printf("Error: no ':' in '%s'. (%s:%i)\n", k.Get(), _FL); } } else printf("Error: %i parts (%s:%i).\n", (int)a.Length(), _FL); if (File && Line > 0) { OnFileLine(NativePath(File), (int)Line.Int(), true); } else { printf("%s:%i - No file='%s' or line='%s'\n%s\n", _FL, File.Get(), Line.Get(), f.Get()); } } void OnLine(const char *Start, int Length) { #if DEBUG_SHOW_GDB_IO LgiTrace("Receive: '%.*s' ParseState=%i, OutLine=%p, OutStream=%p\n", Length-1, Start, ParseState, OutLines, OutStream); #endif // Send output if (OutLines) { OutLines->New().Set(Start, Length - 1); return; } else if (OutStream) { OutStream->Write(Start, Length); return; } else { Untagged.New().Set(Start, Length); #if !ECHO_GDB_OUTPUT Events->Write(Start, Length); #endif } #if ECHO_GDB_OUTPUT Events->Write(Start, Length); #endif if (ParseState == ParseBreakPoint) { if (Length > 0 && IsDigit(*Start)) { // printf("ParsingBp.Parse=%s\n", Start); GString Bp = GString(" ").Join(BreakInfo).Strip(); OnBreakPoint(Bp); ParseState = ParseNone; BreakInfo.Length(0); } else { // printf("ParsingBp.Add=%s\n", Start); BreakInfo.New().Set(Start, Length); } } if (ParseState == ParseNone) { if (stristr(Start, "received signal SIGSEGV")) { Events->OnCrash(0); } else if (*Start == '[') { if (stristr(Start, "Inferior") && stristr(Start, "exited")) { OnExit(); } else if (stristr(Start, "New Thread")) { GString s(Start, Length); GString::Array a = s.SplitDelimit("[] ()"); int ThreadId = -1; for (unsigned i=0; i 0) { // Ok so whats the process ID? #ifdef POSIX int Pid = getpgid(ThreadId); if (Pid > 0 && ProcessId < 0) { // LgiTrace("Got the thread id: %i, and pid: %i\n", ThreadId, Pid); ProcessId = Pid; } /* else LgiTrace("Not setting pid: pid=%i, processid=%i\n", Pid, ProcessId); */ #else LgiAssert(!"Impl me."); #endif } else LgiTrace("%s:%i - No thread id?\n", _FL); } } else if (strncmp(Start, "Breakpoint ", 11) == 0 && IsDigit(Start[11])) { ParseState = ParseBreakPoint; // printf("ParseState=%i\n", ParseState); BreakInfo.New().Set(Start, Length); } else { // Untagged file/line? if (ParseLocation(Untagged)) { Untagged.Length(0); } } } } void OnRead(const char *Ptr, ssize_t Bytes) { // Parse output into lines const char *p = Ptr; const char *End = p + Bytes; char *LineEnd = Line + sizeof(Line) - 2; while (p < End) { if (*p == '\n') { *LinePtr++ = *p; *LinePtr = 0; OnLine(Line, (int) (LinePtr - Line)); LinePtr = Line; } else if (LinePtr < LineEnd) { *LinePtr++ = *p; } p++; } *LinePtr = 0; // Check for prompt auto bytes = LinePtr - Line; if (bytes > 0) { if (bytes == 6) { AtPrompt = !_strnicmp(Line, sPrompt, bytes); // LgiTrace("%I64i: AtPrompt=%i\n", LgiCurrentTime(), AtPrompt); if (AtPrompt) { if (Running ^ !AtPrompt) { SetState(DebuggingProcess, !AtPrompt); } if (OutStream) OutStream = NULL; if (OutLines) OutLines = NULL; Events->Write(Line, bytes); } } } } int Main() { #ifdef WIN32 const char *Shell = "C:\\Windows\\System32\\cmd.exe"; const char *Path = "C:\\MinGW\\bin\\gdb.exe"; #else const char *Path = "gdb"; #endif GString p; if (RunAsAdmin) p.Printf("pkexec %s --args \"%s\"", Path, Exe.Get()); else p.Printf("%s --args \"%s\"", Path, Exe.Get()); if (Args) { p += " "; p += Args; } GString::Array a = p.Split(" ", 1); printf("Starting Debugger: %s %s\n", a[0].Get(), a[1].Get()); if (!Sp.Reset(new GSubProcess(a[0], a[1]))) return false; if (InitDir) Sp->SetInitFolder(InitDir); LgiTrace("Starting gdb subprocess...\n"); if (!Sp->Start(true, true, false)) { State = ProcessError; GAutoString ErrMsg = LgiErrorCodeToString(Sp->GetErrorCode()); char s[256]; sprintf_s(s, sizeof(s), "Failed to start gdb, error: 0x%x (%s)\n", Sp->GetErrorCode(), ErrMsg.Get()); Events->OnError(Sp->GetErrorCode(), s); return -1; } #if DEBUG_SESSION_LOGGING LgiTrace("Gdb::Main - entering loop...\n"); #endif State = Looping; char Buf[513]; bool IsRun; while (State == Looping && (IsRun = Sp->IsRunning())) { #ifdef _DEBUG ZeroObj(Buf); #endif auto Rd = Sp->Read(Buf, sizeof(Buf)-1, 50); if (Rd > 0) { #if 0 // DEBUG_SESSION_LOGGING printf("GDB: %.*s\n", Rd, Buf); #endif OnRead(Buf, Rd); } } Break(); Cmd("q"); #if DEBUG_SESSION_LOGGING LgiTrace("Gdb::Main - exited loop.\n"); #endif SetState(false, false); Log("Debugger exited.\n"); return 0; } bool WaitPrompt() { if (State == Init) { uint64 Start = LgiCurrentTime(); while (State == Init) { uint64 Now = LgiCurrentTime(); if (Now - Start < 5000) { LgiSleep(10); } else { LgiTrace("%s:%i - WaitPrompt init wait failed.\n", _FL); return false; } } } uint64 Start = LgiCurrentTime(); uint64 Now = Start; while (!AtPrompt && Now - Start < 2000 && State == Looping) { Now = LgiCurrentTime(); LgiSleep(50); uint64 After = LgiCurrentTime(); if (After - Now > 65) { printf("Sleep took=%i\n", (int)(After - Now)); } } if (!AtPrompt) { Log("Error: Not at prompt...\n"); return false; } return true; } bool Cmd(const char *c, GStream *Output = NULL, GString::Array *Arr = NULL) { if (!ValidStr(c)) { LgiTrace("%s:%i - Not a valid command.\n", _FL); LgiAssert(!"Not a valid command."); return false; } if (!WaitPrompt()) { return false; } char str[256]; int ch = sprintf_s(str, sizeof(str), "%s\n", c); #if DEBUG_SHOW_GDB_IO LgiTrace("Send: '%s'\n", c); #endif Events->Write(str, ch); LinePtr = Line; OutStream = Output; OutLines = Arr; AtPrompt = false; // uint64 Start = LgiCurrentTime(); auto Wr = Sp->Write(str, ch); if (Wr != ch) return false; if (OutStream || OutLines) { /* uint64 Wait0 = LgiCurrentTime(); */ WaitPrompt(); /* uint64 Wait1 = LgiCurrentTime(); LgiTrace("Cmd timing "LGI_PrintfInt64" "LGI_PrintfInt64"\n", Wait0-Start, Wait1-Wait0); */ LgiAssert(OutStream == NULL && OutLines == NULL); } return true; } public: Gdb() : LThread("Gdb") { Events = NULL; State = Init; AtPrompt = false; LinePtr = Line; RunAsAdmin = false; OutStream = NULL; OutLines = NULL; CurFrame = 0; SetAsmType = false; SetPendingOn = false; DebuggingProcess = false; BreakPointIdx = -1; ProcessId = -1; SuppressNextFileLine = false; ParseState = ParseNone; Vis.Add(new GStringVis); } ~Gdb() { if (State == Looping) { #if DEBUG_SESSION_LOGGING LgiTrace("Gdb::~Gdb - waiting for thread to exit...\n"); #endif State = Exiting; while (!IsExited()) { LgiSleep(1); } #if DEBUG_SESSION_LOGGING LgiTrace("Gdb::~Gdb - thread has exited.\n"); #endif } } bool Load(GDebugEvents *EventHandler, const char *exe, const char *args, bool runAsAdmin, const char *initDir) { Events = EventHandler; Exe = exe; Args = args; RunAsAdmin = runAsAdmin; InitDir = initDir; Running = false; Run(); return true; } bool SetCurrentThread(int ThreadId) { if (ThreadId < 1) return false; GString c; c.Printf("thread %i", ThreadId); if (!Cmd(c)) return false; return true; } bool GetThreads(GArray &Threads, int *pCurrentThread) { GString::Array t; if (!Cmd("info threads", NULL, &t)) return false; GString *Cur = NULL; for (int i=0; iGet(), l); *Cur = s; } } return true; } bool GetCallStack(GArray &Stack) { GString::Array Bt; if (!Cmd("bt", NULL, &Bt)) return false; for (int i=0; i 0) { // Append to the last line.. GAutoString &Prev = Stack.Last(); char *End = Prev + strlen(Prev); while (End > Prev && strchr(WhiteSpace, End[-1])) *(--End) = 0; GString s; s.Printf("%s%s", Prev.Get(), l); Prev.Reset(NewStr(s)); } } return true; } bool GetFrame(int &Frame, GAutoString &File, int &Line) { LgiAssert(0); return false; } bool SetFrame(int Frame) { if (CurFrame != Frame) { CurFrame = Frame; char c[256]; sprintf_s(c, sizeof(c), "frame %i", Frame); return Cmd(c); } return true; } bool Restart() { if (Running) Break(true); GString a; if (Args) a.Printf("r %s", Args.Get()); else a = "r"; bool Status = Cmd(a); if (Status) { SetState(true, false); } return Status; } bool Unload() { if (Running) Break(true); Cmd("q"); SetState(false, false); State = Exiting; return false; } bool GetRunning() { return Running; } bool SetRunning(bool Run) { if (Run) { if (!SetAsmType) { SetAsmType = true; Cmd("set disassembly-flavor intel"); Cmd("handle SIGTTIN nostop"); } GString a; if (DebuggingProcess) a = "c"; else if (Args) a.Printf("r %s", Args.Get()); else a = "r"; if (a(0) == 'r' && ProcessId < 0) { BreakPoint bp; bp.Symbol = "main"; if (SetBreakPoint(&bp)) { if (!Cmd(a)) return false; if (!WaitPrompt()) return false; RemoveBreakPoint(&bp); // Get process info GStringPipe p; if (Cmd("info inferiors", &p)) { GString::Array Ln = p.NewGStr().SplitDelimit("\r\n"); if (Ln.Length() >= 2) { GString::Array a = Ln[1].SplitDelimit(" \t"); for (unsigned i=0; i= 0) { ProcessId = Id; } break; } } } } bool Status = Cmd("c"); if (Status) SetState(true, true); Log("[ProcessId=%i]\n", ProcessId); return Status; } } if (Cmd(a)) { SetState(true, true); return true; } } else { if (Break()) { return true; } } return false; } bool AddBp(BreakPoint &bp) { bool Ret = false; if (!bp.Added) { if (!SetPendingOn) { Cmd("set breakpoint pending on"); SetPendingOn = true; } char cmd[MAX_PATH]; char *File = bp.File.Get(); if (File) { char *Last = strrchr(File, DIR_CHAR); sprintf_s(cmd, sizeof(cmd), "break %s:" LPrintfSSizeT, Last ? Last + 1 : File, bp.Line); } else if (bp.Symbol) { sprintf_s(cmd, sizeof(cmd), "break %s", bp.Symbol.Get()); } else return false; BreakPointIdx = 0; GString::Array Lines; Ret = Cmd(cmd, NULL, &Lines); WaitPrompt(); for (unsigned i=0; i= 2 && !_stricmp(p[0], "breakpoint")) { int Idx = (int)p[1].Int(); if (Idx) { bp.Index = Idx; } } } BreakPointIdx = -1; if (Ret) bp.Added = true; } return Ret; } bool SetBreakPoint(BreakPoint *bp) { if (!bp) { LgiTrace("%s:%i - SetBreakPoint failed, param error.\n", _FL); return false; } // Make sure the child 'gdb' is running... uint64 Start = LgiCurrentTime(); while (State == Init) { LgiSleep(5); if (LgiCurrentTime()-Start > 3000) { LgiTrace("%s:%i - SetBreakPoint init wait failed...\n", _FL); return false; } } bp->Added = false; if (Running) { LgiTrace("%s:%i - Can't add break point while running.\n", _FL); return false; } else { if (AddBp(*bp)) { BreakPoint &n = BreakPoints.New(); n = *bp; } } return true; } bool RemoveBreakPoint(BreakPoint *bp) { if (!bp) return false; // Make sure the child 'gdb' is running... uint64 Start = LgiCurrentTime(); while (State == Init) { LgiSleep(5); if (LgiCurrentTime()-Start > 3000) { LgiTrace("%s:%i - SetBreakPoint init wait failed...\n", _FL); return false; } } if (Running) { LgiTrace("%s:%i - Can't add break point while running.\n", _FL); return false; } else { unsigned i; for (i=0; iFile.Get(), bp->Line); return false; } } return true; } bool GetBreakPoints(GArray &bps) { bps = BreakPoints; return false; } void ParseVariables(const char *a, GArray &vars, GDebugger::Variable::ScopeType scope, bool Detailed) { GToken t(a, "\r\n"); GString CurLine; for (int i=0; i 0) { char *val = CurLine.Get() + EqPos + 1; while (*val && strchr(WhiteSpace, *val)) val++; Variable &v = vars.New(); v.Scope = scope; v.Name = CurLine(0, EqPos).Strip(); if (!strnicmp(val, "0x", 2)) { v.Value.Type = GV_VOID_PTR; v.Value.Value.Ptr = (void*) htoi64(val); } else if (IsDigit(*val)) { int64 tmp = atoi64(val); if (tmp & 0xffffffff00000000L) v.Value = tmp; else v.Value = (int)tmp; } else { v.Value.OwnStr(TrimStr(val)); } if (Detailed) { GStringPipe p; char c[256]; sprintf_s(c, sizeof(c), "p %s", v.Name.Get()); if (Cmd(c, &p)) { GAutoString tmp(p.NewStr()); for (char *s = tmp; s && *s; ) { if (*s == '\"') { char *e = strchr(++s, '\"'); if (!e) break; v.Value.OwnStr(NewStr(s, e - s)); break; } else if (*s == '(' && !v.Type) { char *e = strchr(++s, ')'); if (!e) break; if (strnicmp(s, "gdb", 3)) v.Type.Set(s, e - s); s = e + 1; continue; } s = LgiSkipDelim(s, WhiteSpace, true); s = LgiSkipDelim(s, WhiteSpace); } } } } } } bool GetVariables(bool Locals, GArray &vars, bool Detailed) { // GProfile Prof("GetVars"); GStringPipe p(256); if (vars.Length()) { GString c; for (unsigned i=0; iMatch(v.Type)) { if (vs->Transform(v.Name, Val, this, v.Value, v.Detail)) break; } } if (i >= Vis.Length()) v.Value = Val.Get(); } else printf("%s:%i - Cmd failed '%s'\n", _FL, c.Get()); } return true; } else { if (!Cmd("info args", &p)) return false; // Prof.Add("ParseArgs"); GAutoString a(p.NewStr()); ParseVariables(a, vars, Variable::Arg, Detailed); // Prof.Add("InfoLocals"); if (!Cmd("info locals", &p)) return false; // Prof.Add("ParseLocals"); a.Reset(p.NewStr()); ParseVariables(a, vars, Variable::Local, Detailed); } return true; } bool GetRegisters(GStream *Out) { if (!Out) return false; return Cmd("info registers", Out); } bool PrintObject(const char *Var, GStream *Output) { if (!Var || !Output) return false; char c[256]; sprintf_s(c, sizeof(c), "p *%s", Var); GMemQueue q; if (!Cmd(c, &q)) return false; GAutoString a((char*)q.New(1)); if (!a) return false; int Depth = 0; char *Start = NULL; char Spaces[256]; memset(Spaces, ' ', sizeof(Spaces)); int IndentShift = 2; #define Emit() \ if (Start) \ { \ auto bytes = s - Start; \ char *last = s-1; while (last > Start && strchr(WhiteSpace, *last)) last--; \ Output->Print("%.*s%.*s%s\n", Depth<Print("%.*s%c\n", Depth<Print("%.*s%c\n", Depth< &OutBuf, GString *ErrorMsg) + bool ReadMemory(GString &BaseAddr, int Length, GArray &OutBuf, GString *ErrorMsg) { if (!BaseAddr) { if (ErrorMsg) *ErrorMsg = "No base address supplied."; return false; } BaseAddr = BaseAddr.Strip(); GString::Array Out; char c[256]; int words = Length >> 2; // int bytes = Length % 4; if (BaseAddr.Find("0x") >= 0) { // Looks like an literal address... LiteralAddr: sprintf_s(c, sizeof(c), "x/%iw %s", words, BaseAddr.Get()); } else { // Maybe it's a ptr variable? GString c; GString::Array r; c.Printf("p %s", BaseAddr.Get()); if (Cmd(c, NULL, &r)) { GString::Array p = r[0].SplitDelimit(" \t"); for (unsigned i=0; i= 0) { BaseAddr = p[i]; goto LiteralAddr; } /* GString Msg; Msg.Printf("%s\n", p[i].Get()); Events->Write(Msg, Msg.Length()); */ } if (ErrorMsg) *ErrorMsg = "No address in variable value response."; return false; } else { if (ErrorMsg) *ErrorMsg = "Couldn't convert variable to address."; return false; } } if (!Cmd(c, NULL, &Out)) { if (ErrorMsg) *ErrorMsg = "Gdb command failed."; return false; } if (!OutBuf.Length(words << 2)) { if (ErrorMsg) ErrorMsg->Printf("Failed to allocate %i bytes.", words << 2); return false; } - uint32 *buf = (uint32*) &(OutBuf)[0]; - uint32 *ptr = buf; - uint32 *end = ptr + (OutBuf.Length() / sizeof(*buf)); + uint32_t *buf = (uint32_t*) &(OutBuf)[0]; + uint32_t *ptr = buf; + uint32_t *end = ptr + (OutBuf.Length() / sizeof(*buf)); for (int i=0; i= end) break; } } OutBuf.Length((ptr - buf) << 2); return true; } bool GetLocation(GAutoString &File, int &Line) { LgiAssert(0); return false; } bool SetLocation(const char *File, int Line) { LgiAssert(0); return false; } bool StepInto() { bool Status = Cmd("step"); if (Status) SetState(DebuggingProcess, true); return Status; } bool StepOver() { bool Status = Cmd("next"); if (Status) SetState(DebuggingProcess, true); return Status; } bool StepOut() { bool Status = Cmd("finish"); if (Status) SetState(DebuggingProcess, true); return Status; } bool Break(bool SuppressFL = false) { #ifdef POSIX if (ProcessId < 0) { LgiTrace("%s:%i - No process ID (yet?).\n", _FL); return false; } SuppressNextFileLine = SuppressFL; LgiTrace("%s:%i - sending SIGINT to %i(0x%x)...\n", _FL, ProcessId, ProcessId); int result = #ifdef __GTK_H__ Gtk:: #endif kill(ProcessId, SIGINT); if (!result) { LgiTrace("%s:%i - success... waiting prompt\n", _FL); return WaitPrompt(); } LgiTrace("%s:%i - kill failed with %i(0x%x)\n", _FL, errno, errno); return false; #else LgiAssert(!"Impl me"); return false; #endif } bool UserCommand(const char *cmd) { char c[256]; sprintf_s(c, sizeof(c), "%s", cmd); return Cmd(c); } GString GetResponse(const char *c) { GString r; GStringPipe p; if (Cmd(c, &p)) r = p.NewGStr(); return r; } }; GDebugger *CreateGdbDebugger() { return new Gdb; } diff --git a/Ide/Code/GDebugger.h b/Ide/Code/GDebugger.h --- a/Ide/Code/GDebugger.h +++ b/Ide/Code/GDebugger.h @@ -1,113 +1,113 @@ #ifndef _GDEBUGGER_H_ #define _GDEBUGGER_H_ #include "GVariant.h" #include "GStringClass.h" #define DEBUG_SESSION_LOGGING 0 class GDebugEvents : public GStream { public: virtual ~GDebugEvents() {} virtual void OnState(bool Debugging, bool Running) = 0; virtual void OnFileLine(const char *File, int Line, bool CurrentIp) = 0; virtual void OnError(int Code, const char *Str) = 0; virtual void OnCrash(int Code) = 0; }; class GDebugger { public: struct BreakPoint { int Index; bool Added; // Use File:Line GString File; ssize_t Line; // -or- // A symbol reference GString Symbol; BreakPoint() { Index = 0; Line = 0; Added = false; } BreakPoint &operator =(const BreakPoint &b) { Index = b.Index; File = b.File; Line = b.Line; Symbol = b.Symbol; return *this; } bool operator ==(const BreakPoint &b) { if (File == b.File && Line == b.Line && Symbol == b.Symbol) return true; return false; } }; struct Variable { enum ScopeType { Local, Arg, Global } Scope; GString Name; GString Type; GVariant Value; GString Detail; }; virtual ~GDebugger() {} virtual bool Load(GDebugEvents *EventHandler, const char *Exe, const char *Args, bool RunAsAdmin, const char *InitDir) = 0; virtual bool Restart() = 0; virtual bool Unload() = 0; virtual bool GetCallStack(GArray &Stack) = 0; virtual bool GetThreads(GArray &Threads, int *CurrentThread) = 0; virtual bool SetCurrentThread(int ThreadId) = 0; virtual bool GetFrame(int &Frame, GAutoString &File, int &Line) = 0; virtual bool SetFrame(int Frame) = 0; virtual bool SetBreakPoint(BreakPoint *bp) = 0; virtual bool RemoveBreakPoint(BreakPoint *bp) = 0; virtual bool GetBreakPoints(GArray &bps) = 0; virtual bool GetVariables(bool Locals, GArray &vars, bool Detailed) = 0; virtual bool PrintObject(const char *Var, GStream *Output) = 0; - virtual bool ReadMemory(GString &BaseAddr, int Length, GArray &OutBuf, GString *ErrorMsg = NULL) = 0; + virtual bool ReadMemory(GString &BaseAddr, int Length, GArray &OutBuf, GString *ErrorMsg = NULL) = 0; virtual bool GetRegisters(GStream *Out) = 0; virtual bool GetLocation(GAutoString &File, int &Line) = 0; virtual bool SetLocation(const char *File, int Line) = 0; virtual bool GetRunning() = 0; virtual bool SetRunning(bool Run) = 0; virtual bool StepInto() = 0; virtual bool StepOver() = 0; virtual bool StepOut() = 0; virtual bool Break(bool SuppressFileLine = false) = 0; virtual bool UserCommand(const char *Cmd) = 0; }; extern GDebugger *CreateGdbDebugger(); #endif diff --git a/Ide/Code/IdeProject.cpp b/Ide/Code/IdeProject.cpp --- a/Ide/Code/IdeProject.cpp +++ b/Ide/Code/IdeProject.cpp @@ -1,3853 +1,3853 @@ #if defined(WIN32) #include #else #include #endif #include #include "Lgi.h" #include "LgiIde.h" #include "GDragAndDrop.h" #include "GToken.h" #include "resdefs.h" #include "GProcess.h" #include "GCombo.h" #include "INet.h" #include "LListItemCheckBox.h" #include "FtpThread.h" #include "GClipBoard.h" #include "GDropFiles.h" #include "GSubProcess.h" #include "ProjectNode.h" #include "WebFldDlg.h" #include "GCss.h" #include "GTableLayout.h" #include "GTextLabel.h" #include "GButton.h" extern const char *Untitled; const char SourcePatterns[] = "*.c;*.h;*.cpp;*.cc;*.java;*.d;*.php;*.html;*.css"; const char *AddFilesProgress::DefaultExt = "c,cpp,cc,cxx,h,hpp,hxx,html,css,json,js,jsx,txt,png,jpg,jpeg,rc,xml,mk,paths,makefile,py"; #define USE_OPEN_PROGRESS 1 #define STOP_BUILD_TIMEOUT 2000 #ifdef WINDOWS #define LGI_STATIC_LIBRARY_EXT "lib" #else #define LGI_STATIC_LIBRARY_EXT "a" #endif const char *PlatformNames[] = { "Windows", "Linux", "Mac", "Haiku", 0 }; int PlatformCtrlId[] = { IDC_WIN32, IDC_LINUX, IDC_MAC, IDC_HAIKU, 0 }; char *ToUnixPath(char *s) { if (s) { char *c; while ((c = strchr(s, '\\'))) { *c = '/'; } } return s; } const char *CastEmpty(char *s) { return s ? s : ""; } bool FindInPath(GString &Exe) { GString::Array Path = GString(getenv("PATH")).Split(LGI_PATH_SEPARATOR); for (unsigned i=0; i SubProc; GString::Array BuildConfigs; GString::Array PostBuild; enum CompilerType { DefaultCompiler, VisualStudio, MingW, Gcc, CrossCompiler, PythonScript, IAR, Nmake } Compiler; enum ArchType { DefaultArch, ArchX32, ArchX64, ArchArm6, ArchArm7, } Arch; public: BuildThread(IdeProject *proj, char *makefile, bool clean, bool release, bool all, int wordsize); ~BuildThread(); ssize_t Write(const void *Buffer, ssize_t Size, int Flags = 0) override; GString FindExe(); GAutoString WinToMingWPath(const char *path); int Main() override; }; class IdeProjectPrivate { public: AppWnd *App; IdeProject *Project; bool Dirty, UserFileDirty; GString FileName; IdeProject *ParentProject; IdeProjectSettings Settings; GAutoPtr Thread; LHashTbl, ProjectNode*> Nodes; int NextNodeId; // Threads GAutoPtr CreateMakefile; // User info file GString UserFile; LHashTbl,int> UserNodeFlags; IdeProjectPrivate(AppWnd *a, IdeProject *project) : Project(project), Settings(project) { App = a; Dirty = false; UserFileDirty = false; ParentProject = 0; NextNodeId = 1; } void CollectAllFiles(GTreeNode *Base, GArray &Files, bool SubProjects, int Platform); }; class MakefileThread : public LThread, public LCancel { IdeProjectPrivate *d; IdeProject *Proj; IdePlatform Platform; bool BuildAfterwards; public: MakefileThread(IdeProjectPrivate *priv, IdePlatform platform, bool Build) : LThread("MakefileThread") { d = priv; Proj = d->Project; Platform = platform; BuildAfterwards = Build; Run(); } ~MakefileThread() { Cancel(); while (!IsExited()) LgiSleep(1); } int Main() { const char *PlatformName = PlatformNames[Platform]; const char *PlatformLibraryExt = NULL; const char *PlatformStaticLibExt = NULL; const char *PlatformExeExt = ""; GString LinkerFlags; const char *TargetType = d->Settings.GetStr(ProjTargetType, NULL, Platform); const char *CompilerName = d->Settings.GetStr(ProjCompiler); GString CCompilerBinary = "gcc"; GString CppCompilerBinary = "g++"; GStream *Log = d->App->GetBuildLog(); bool IsExecutableTarget = TargetType && !stricmp(TargetType, "Executable"); bool IsDynamicLibrary = TargetType && !stricmp(TargetType, "DynamicLibrary"); LgiAssert(Log); if (!Log) return false; Log->Print("CreateMakefile for '%s'...\n", PlatformName); if (Platform == PlatformWin32) { LinkerFlags = ",--enable-auto-import"; } else { if (IsDynamicLibrary) { LinkerFlags = ",-soname,$(TargetFile)"; } LinkerFlags += ",-export-dynamic,-R."; } char Buf[256]; GAutoString MakeFile = Proj->GetMakefile(); if (!MakeFile) { MakeFile.Reset(NewStr("../Makefile")); } // LGI_LIBRARY_EXT switch (Platform) { case PlatformWin32: PlatformLibraryExt = "dll"; PlatformStaticLibExt = "lib"; PlatformExeExt = ".exe"; break; case PlatformLinux: case PlatformHaiku: PlatformLibraryExt = "so"; PlatformStaticLibExt = "a"; break; case PlatformMac: PlatformLibraryExt = "dylib"; PlatformStaticLibExt = "a"; break; default: LgiAssert(0); break; } if (CompilerName) { if (!stricmp(CompilerName, "cross")) { GString CBin = d->Settings.GetStr(ProjCCrossCompiler, NULL, Platform); if (CBin && !FileExists(CBin)) FindInPath(CBin); if (CBin && FileExists(CBin)) CCompilerBinary = CBin; else Log->Print("%s:%i - Error: C cross compiler '%s' not found.\n", _FL, CBin.Get()); GString CppBin = d->Settings.GetStr(ProjCppCrossCompiler, NULL, Platform); if (CppBin && !FileExists(CppBin)) FindInPath(CppBin); if (CppBin && FileExists(CppBin)) CppCompilerBinary = CppBin; else Log->Print("%s:%i - Error: C++ cross compiler '%s' not found.\n", _FL, CppBin.Get()); } } GFile m; if (!m.Open(MakeFile, O_WRITE)) { Log->Print("Error: Failed to open '%s' for writing.\n", MakeFile.Get()); return false; } m.SetSize(0); m.Print("#!/usr/bin/make\n" "#\n" "# This makefile generated by LgiIde\n" "# http://www.memecode.com/lgi.php\n" "#\n" "\n" ".SILENT :\n" "\n" "CC = %s\n" "CPP = %s\n", CCompilerBinary.Get(), CppCompilerBinary.Get()); // Collect all files that require building GArray Files; d->CollectAllFiles ( Proj, Files, false, 1 << Platform ); if (IsExecutableTarget) { GString Exe = Proj->GetExecutable(Platform); if (Exe) { if (LgiIsRelativePath(Exe)) m.Print("Target = %s\n", Exe.Get()); else { GAutoString Base = Proj->GetBasePath(); GAutoString RelExe = LgiMakeRelativePath(Base, Exe); if (Base && RelExe) { m.Print("Target = %s\n", RelExe.Get()); } else { Log->Print("%s:%i - Error: Missing path (%s, %s).\n", _FL, Base.Get(), RelExe.Get()); return false; } } } else { Log->Print("%s:%i - Error: No executable name specified (%s, %s).\n", _FL, TargetType, d->FileName.Get()); return false; } } else { GString Target = Proj->GetTargetName(Platform); if (Target) m.Print("Target = %s\n", Target.Get()); else { Log->Print("%s:%i - Error: No target name specified.\n", _FL); return false; } } // Output the build mode, flags and some paths int BuildMode = d->App->GetBuildMode(); char *BuildModeName = BuildMode ? (char*)"Release" : (char*)"Debug"; m.Print("ifndef Build\n" " Build = %s\n" "endif\n", BuildModeName); GString sDefines[2]; GString sLibs[2]; GString sIncludes[2]; const char *ExtraLinkFlags = NULL; const char *ExeFlags = NULL; if (Platform == PlatformWin32) { ExtraLinkFlags = ""; ExeFlags = " -mwindows"; m.Print("BuildDir = $(Build)\n" "\n" "Flags = -fPIC -w -fno-inline -fpermissive\n"); const char *DefDefs = "-DWIN32 -D_REENTRANT"; sDefines[0] = DefDefs; sDefines[1] = DefDefs; } else { char PlatformCap[32]; strcpy_s( PlatformCap, sizeof(PlatformCap), Platform == PlatformHaiku ? "BEOS" : PlatformName); strupr(PlatformCap); ExtraLinkFlags = ""; ExeFlags = ""; m.Print("BuildDir = $(Build)\n" "\n" "Flags = -fPIC -w -fno-inline -fpermissive\n" // -fexceptions ); sDefines[0].Printf("-D%s -D_REENTRANT", PlatformCap); #ifdef LINUX sDefines[0] += " -D_FILE_OFFSET_BITS=64"; // >:-( sDefines[0] += " -DPOSIX"; #endif sDefines[1] = sDefines[0]; } List Deps; Proj->GetChildProjects(Deps); const char *sConfig[] = {"Debug", "Release"}; for (int Cfg = 0; Cfg < CountOf(sConfig); Cfg++) { // Set the config d->Settings.SetCurrentConfig(sConfig[Cfg]); // Get the defines setup const char *PDefs = d->Settings.GetStr(ProjDefines, NULL, Platform); if (ValidStr(PDefs)) { GToken Defs(PDefs, " ;,\r\n"); for (int i=0; iSettings.GetStr(ProjLibraryPaths, NULL, Platform); if (ValidStr(PLibPaths)) { GString::Array LibPaths = PLibPaths.Split("\n"); for (unsigned i=0; iSettings.GetStr(ProjLibraries, NULL, Platform); if (ValidStr(PLibs)) { GToken Libs(PLibs, "\r\n"); for (int i=0; iGetTargetName(Platform); if (Target) { char t[MAX_PATH]; strcpy_s(t, sizeof(t), Target); if (!strnicmp(t, "lib", 3)) memmove(t, t + 3, strlen(t + 3) + 1); char *dot = strrchr(t, '.'); if (dot) *dot = 0; GString s; s.Printf(" \\\n\t\t-l%s$(Tag)", ToUnixPath(t)); sLibs[Cfg] += s; GAutoString Base = dep->GetBasePath(); if (Base) { s.Printf(" \\\n\t\t-L%s/$(BuildDir)", ToUnixPath(Base)); sLibs[Cfg] += s; } } } // Includes // Do include paths LHashTbl,bool> Inc; const char *ProjIncludes = d->Settings.GetStr(ProjIncludePaths, NULL, Platform); if (ValidStr(ProjIncludes)) { // Add settings include paths. GToken Paths(ProjIncludes, "\r\n"); for (int i=0; iSettings.GetStr(ProjSystemIncludes, NULL, Platform); if (ValidStr(SysIncludes)) { // Add settings include paths. GToken Paths(SysIncludes, "\r\n"); for (int i=0; iGetFileName()) { char *e = LgiGetExtension(n->GetFileName()); if (e && stricmp(e, "h") == 0) { for (char *Dir=n->GetFileName(); *Dir; Dir++) { if (*Dir == '/' || *Dir == '\\') { *Dir = DIR_CHAR; } } char Path[256]; strcpy_s(Path, sizeof(Path), n->GetFileName()); LgiTrimDir(Path); char Rel[256]; if (!Proj->RelativePath(Rel, Path)) { strcpy(Rel, Path); } if (stricmp(Rel, ".") != 0) { GAutoString RelN = ToNativePath(Rel); if (!Inc.Find(RelN)) { Inc.Add(RelN, true); } } } } } List Incs; // char *i; // for (bool b=Inc.First(&i); b; b=Inc.Next(&i)) for (auto i : Inc) { Incs.Insert(NewStr(i.key)); } Incs.Sort(StrCmp); for (auto i = Incs.First(); i; i = Incs.Next()) { GString s; if (*i == '`') s.Printf(" \\\n\t\t%s", i); else s.Printf(" \\\n\t\t-I%s", ToUnixPath(i)); sIncludes[Cfg] += s; } } // Output the defs section for Debug and Release // Debug specific m.Print("\n" "ifeq ($(Build),Debug)\n" " Flags += -g -std=c++11\n" " Tag = d\n" " Defs = -D_DEBUG %s\n" " Libs = %s\n" " Inc = %s\n", CastEmpty(sDefines[0].Get()), CastEmpty(sLibs[0].Get()), CastEmpty(sIncludes[0].Get())); // Release specific m.Print("else\n" " Flags += -s -Os -std=c++11\n" " Defs = %s\n" " Libs = %s\n" " Inc = %s\n" "endif\n" "\n", CastEmpty(sDefines[1].Get()), CastEmpty(sLibs[1].Get()), CastEmpty(sIncludes[1].Get())); if (Files.First()) { GArray IncPaths; if (Proj->BuildIncludePaths(IncPaths, false, false, Platform)) { // Do dependencies m.Print("# Dependencies\n" "Depends =\t"); for (int c = 0; c < Files.Length() && !IsCancelled(); c++) { ProjectNode *n = Files[c]; if (n->GetType() == NodeSrc) { GAutoString f = ToNativePath(n->GetFileName()); char *d = f ? strrchr(f, DIR_CHAR) : 0; char *file = d ? d + 1 : f; d = file ? strrchr(file, '.') : 0; if (d) { if (c) m.Print(" \\\n\t\t\t"); m.Print("%.*s.o", d - file, file); } } } m.Print("\n\n"); // Write out the target stuff m.Print("# Target\n"); LHashTbl,bool> DepFiles; if (TargetType) { if (IsExecutableTarget) { m.Print("# Executable target\n" "$(Target) :"); GStringPipe Rules; IdeProject *Dep; uint64 Last = LgiCurrentTime(); int Count = 0; for (Dep=Deps.First(); Dep && !IsCancelled(); Dep=Deps.Next(), Count++) { // Get dependency to create it's own makefile... Dep->CreateMakefile(Platform, false); // Build a rule to make the dependency if any of the source changes... char t[MAX_PATH] = ""; GAutoString DepBase = Dep->GetBasePath(); GAutoString Base = Proj->GetBasePath(); if (DepBase && Base && Dep->GetTargetFile(t, sizeof(t))) { char Rel[MAX_PATH] = ""; if (!Proj->RelativePath(Rel, DepBase)) { strcpy_s(Rel, sizeof(Rel), DepBase); } ToUnixPath(Rel); // Add tag to target name GToken Parts(t, "."); if (Parts.Length() == 2) sprintf_s(t, sizeof(t), "lib%s$(Tag).%s", Parts[0], Parts[1]); else sprintf_s(t, sizeof(t), "%s", Parts[0]); sprintf(Buf, "%s/$(BuildDir)/%s", Rel, t); m.Print(" %s", Buf); GArray AllDeps; Dep->GetAllDependencies(AllDeps, Platform); LgiAssert(AllDeps.Length() > 0); AllDeps.Sort(StrSort); Rules.Print("%s : ", Buf); for (int i=0; iRelativePath(Rel, AllDeps[i]) ? Rel : AllDeps[i]; ToUnixPath(f); Rules.Print("%s", f); // Add these dependencies to this makefiles dep list if (!DepFiles.Find(f)) DepFiles.Add(f, true); } AllDeps.DeleteArrays(); Rules.Print("\n\texport Build=$(Build); \\\n" "\t$(MAKE) -C %s", Rel); GAutoString Mk = Dep->GetMakefile(); // RenameMakefileForPlatform(Mk, Platform); char *DepMakefile = strrchr(Mk, DIR_CHAR); if (DepMakefile) { Rules.Print(" -f %s", DepMakefile + 1); } Rules.Print("\n\n"); } uint64 Now = LgiCurrentTime(); if (Now - Last > 1000) { Last = Now; Log->Print("Building deps %i%%...\n", (int) (((int64)Count+1)*100/Deps.Length())); } } m.Print(" outputfolder $(Depends)\n" " @echo Linking $(Target) [$(Build)]...\n" " $(CPP)%s%s %s%s -o \\\n" " $(Target) $(addprefix $(BuildDir)/,$(Depends)) $(Libs)\n", ExtraLinkFlags, ExeFlags, ValidStr(LinkerFlags) ? "-Wl" : "", LinkerFlags.Get()); if (Platform == PlatformHaiku) { // Is there an application icon configured? const char *AppIcon = d->Settings.GetStr(ProjApplicationIcon, NULL, Platform); if (AppIcon) { m.Print(" addattr -f %s -t \"'VICN'\" \"BEOS:ICON\" $(Target)\n", AppIcon); } } GString PostBuildCmds = d->Settings.GetStr(ProjPostBuildCommands, NULL, Platform); if (ValidStr(PostBuildCmds)) { GString::Array a = PostBuildCmds.Split("\n"); for (unsigned i=0; i /dev/null\n" "\n"); m.Print("# Clean just this target\n" "clean :\n" " rm -f $(BuildDir)/*.o $(Target)%s\n" " @echo Cleaned $(BuildDir)\n" "\n", LGI_EXECUTABLE_EXT); m.Print("# Clean all targets\n" "cleanall :\n" " rm -f $(BuildDir)/*.o $(Target)%s\n" " @echo Cleaned $(BuildDir)\n", LGI_EXECUTABLE_EXT); for (IdeProject *d=Deps.First(); d; d=Deps.Next()) { GAutoString mk = d->GetMakefile(); if (mk) { GAutoString my_base = Proj->GetBasePath(); GAutoString dep_base = d->GetBasePath(); GAutoString rel_dir = LgiMakeRelativePath(my_base, dep_base); char *mk_leaf = strrchr(mk, DIR_CHAR); m.Print(" +make -C \"%s\" -f \"%s\" clean\n", ToUnixPath(rel_dir ? rel_dir.Get() : dep_base.Get()), ToUnixPath(mk_leaf ? mk_leaf + 1 : mk.Get())); } } m.Print("\n"); } // Shared library else if (!stricmp(TargetType, "DynamicLibrary")) { m.Print("TargetFile = lib$(Target)$(Tag).%s\n" "$(TargetFile) : outputfolder $(Depends)\n" " @echo Linking $(TargetFile) [$(Build)]...\n" " $(CPP)$s -shared \\\n" " %s%s \\\n" " -o $(BuildDir)/$(TargetFile) \\\n" " $(addprefix $(BuildDir)/,$(Depends)) \\\n" " $(Libs)\n", PlatformLibraryExt, ValidStr(ExtraLinkFlags) ? "-Wl" : "", ExtraLinkFlags, LinkerFlags.Get()); GString PostBuildCmds = d->Settings.GetStr(ProjPostBuildCommands, NULL, Platform); if (ValidStr(PostBuildCmds)) { GString::Array a = PostBuildCmds.Split("\n"); for (unsigned i=0; i /dev/null\n" "\n" "# Clean out targets\n" "clean :\n" " rm -f $(BuildDir)/*.o $(BuildDir)/$(TargetFile)\n" " @echo Cleaned $(BuildDir)\n" "\n", PlatformLibraryExt); } // Static library else if (!stricmp(TargetType, "StaticLibrary")) { m.Print("TargetFile = lib$(Target)$(Tag).%s\n" "$(TargetFile) : outputfolder $(Depends)\n" " @echo Linking $(TargetFile) [$(Build)]...\n" " ar rcs $(BuildDir)/$(TargetFile) $(addprefix $(BuildDir)/,$(Depends))\n", PlatformStaticLibExt); GString PostBuildCmds = d->Settings.GetStr(ProjPostBuildCommands, NULL, Platform); if (ValidStr(PostBuildCmds)) { GString::Array a = PostBuildCmds.Split("\n"); for (unsigned i=0; i /dev/null\n" "\n" "# Clean out targets\n" "clean :\n" " rm -f $(BuildDir)/*.o $(BuildDir)/$(TargetFile)\n" " @echo Cleaned $(BuildDir)\n" "\n", PlatformStaticLibExt); } } // Create dependency tree, starting with all the source files. for (int idx=0; idxGetType() == NodeSrc) { GString Src = n->GetFullPath(); if (Src) { char Part[256]; char *d = strrchr(Src, DIR_CHAR); d = d ? d + 1 : Src.Get(); strcpy(Part, d); char *Dot = strrchr(Part, '.'); if (Dot) *Dot = 0; char Rel[MAX_PATH]; if (Platform != PlatformHaiku) { if (!Proj->RelativePath(Rel, Src)) strcpy_s(Rel, sizeof(Rel), Src); } else { // Use full path for Haiku because the Debugger needs it to // find the source correctly. As there are duplicate filenames // for different platforms it's better to rely on full paths // rather than filename index to find the right file. strcpy_s(Rel, sizeof(Rel), Src); } m.Print("%s.o : %s ", Part, ToUnixPath(Rel)); GArray SrcDeps; if (Proj->GetDependencies(Src, IncPaths, SrcDeps, Platform)) { for (int i=0; i,bool> Processed; GAutoString Base = Proj->GetBasePath(); while (!Done) { Done = true; // char *Src; // for (bool b=DepFiles.First(&Src); b; b=DepFiles.Next(&Src)) for (auto it : DepFiles) { if (IsCancelled()) break; if (Processed.Find(it.key)) continue; Done = false; Processed.Add(it.key, true); char Full[MAX_PATH], Rel[MAX_PATH]; if (LgiIsRelativePath(it.key)) { LgiMakePath(Full, sizeof(Full), Base, it.key); strcpy_s(Rel, sizeof(Rel), it.key); } else { strcpy_s(Full, sizeof(Full), it.key); GAutoString a = LgiMakeRelativePath(Base, it.key); if (a) { strcpy_s(Rel, sizeof(Rel), a); } else { strcpy_s(Rel, sizeof(Rel), a); LgiTrace("%s:%i - Failed to make relative path '%s' '%s'\n", _FL, Base.Get(), it.key); } } char *c8 = ReadTextFile(Full); if (c8) { GArray Headers; if (BuildHeaderList(c8, Headers, IncPaths, false)) { m.Print("%s : ", Rel); for (int n=0; nRelativePath(Rel, i)) { strcpy(Rel, i); } if (stricmp(i, Full) != 0) m.Print("%s", ToUnixPath(Rel)); if (!DepFiles.Find(i)) { DepFiles.Add(i, true); } } Headers.DeleteArrays(); m.Print("\n\n"); } else LgiTrace("%s:%i - Error: BuildHeaderList failed for '%s'\n", _FL, Full); DeleteArray(c8); } else LgiTrace("%s:%i - Error: Failed to read '%s'\n", _FL, Full); break; } } // Output VPATH m.Print("VPATH=%%.cpp \\\n"); for (int i=0; iSettings.GetStr(ProjMakefileRules, NULL, Platform); if (ValidStr(OtherMakefileRules)) { m.Print("\n%s\n", OtherMakefileRules); } } } else { m.Print("# No files require building.\n"); } Log->Print("...Done: '%s'\n", MakeFile.Get()); if (BuildAfterwards) { if (!Proj->GetApp()->PostEvent(M_START_BUILD)) printf("%s:%i - PostEvent(M_START_BUILD) failed.\n", _FL); } return true; } }; ///////////////////////////////////////////////////////////////////////////////////// NodeSource::~NodeSource() { if (nView) { nView->nSrc = 0; } } NodeView::~NodeView() { if (nSrc) { nSrc->nView = 0; } } ////////////////////////////////////////////////////////////////////////////////// int NodeSort(GTreeItem *a, GTreeItem *b, NativeInt d) { ProjectNode *A = dynamic_cast(a); ProjectNode *B = dynamic_cast(b); if (A && B) { if ( (A->GetType() == NodeDir) ^ (B->GetType() == NodeDir) ) { return A->GetType() == NodeDir ? -1 : 1; } else { char *Sa = a->GetText(0); char *Sb = b->GetText(0); if (Sa && Sb) { return stricmp(Sa, Sb); } } } return 0; } int XmlSort(GXmlTag *a, GXmlTag *b, NativeInt d) { GTreeItem *A = dynamic_cast(a); GTreeItem *B = dynamic_cast(b); return NodeSort(A, B, d); } //////////////////////////////////////////////////////////////////////////////////////////////////// bool ReadVsProjFile(GString File, GString &Ver, GString::Array &Configs) { const char *Ext = LgiGetExtension(File); if (!Ext || _stricmp(Ext, "vcxproj")) return false; GFile f; if (!f.Open(File, O_READ)) return false; GXmlTree Io; GXmlTag r; if (Io.Read(&r, &f) && r.IsTag("Project")) { Ver = r.GetAttr("ToolsVersion"); GXmlTag *ItemGroup = r.GetChildTag("ItemGroup"); if (ItemGroup) for (GXmlTag *c = ItemGroup->Children.First(); c; c = ItemGroup->Children.Next()) { if (c->IsTag("ProjectConfiguration")) { char *s = c->GetAttr("Include"); if (s) Configs.New() = s; } } } else return false; return true; } //////////////////////////////////////////////////////////////////////////////////////////////////// BuildThread::BuildThread(IdeProject *proj, char *makefile, bool clean, bool release, bool all, int wordsize) : LThread("BuildThread") { Proj = proj; Makefile = makefile; Clean = clean; Release = release; All = all; WordSize = wordsize; Arch = DefaultArch; Compiler = DefaultCompiler; GString Cmds = proj->d->Settings.GetStr(ProjPostBuildCommands, NULL); if (ValidStr(Cmds)) PostBuild = Cmds.SplitDelimit("\r\n"); char *Ext = LgiGetExtension(Makefile); if (Ext && !_stricmp(Ext, "py")) { Compiler = PythonScript; } else if (Ext && !_stricmp(Ext, "sln")) { Compiler = VisualStudio; } else { GAutoString Comp(NewStr(Proj->GetSettings()->GetStr(ProjCompiler))); if (Comp) { // Use the specified compiler... if (!stricmp(Comp, "VisualStudio")) Compiler = VisualStudio; else if (!stricmp(Comp, "MingW")) Compiler = MingW; else if (!stricmp(Comp, "gcc")) Compiler = Gcc; else if (!stricmp(Comp, "cross")) Compiler = CrossCompiler; else if (!stricmp(Comp, "IAR")) Compiler = IAR; else LgiAssert(!"Unknown compiler."); } } if (Compiler == DefaultCompiler) { // Use default compiler for platform... #ifdef WIN32 Compiler = VisualStudio; #else Compiler = Gcc; #endif } Run(); } BuildThread::~BuildThread() { if (SubProc) { bool b = SubProc->Interrupt(); LgiTrace("%s:%i - Sub process interrupt = %i.\n", _FL, b); } else LgiTrace("%s:%i - No sub process to interrupt.\n", _FL); uint64 Start = LgiCurrentTime(); bool Killed = false; while (!IsExited()) { LgiSleep(10); if (LgiCurrentTime() - Start > STOP_BUILD_TIMEOUT && SubProc) { if (Killed) { // Thread is stuck as well... ok kill that too!!! Argh - kill all the things!!!! Terminate(); LgiTrace("%s:%i - Thread killed.\n", _FL); Proj->GetApp()->PostEvent(M_BUILD_DONE); break; } else { // Kill the sub-process... bool b = SubProc->Kill(); Killed = true; LgiTrace("%s:%i - Sub process killed.\n", _FL, b); Start = LgiCurrentTime(); } } } } ssize_t BuildThread::Write(const void *Buffer, ssize_t Size, int Flags) { if (Proj->GetApp()) { Proj->GetApp()->PostEvent(M_APPEND_TEXT, (GMessage::Param)NewStr((char*)Buffer, Size), 0); } return Size; } #pragma comment(lib, "version.lib") struct ProjInfo { GString Guid, Name, File; LHashTbl,int> Configs; }; GString BuildThread::FindExe() { GToken p(getenv("PATH"), LGI_PATH_SEPARATOR); if (Compiler == PythonScript) { #if defined(WINDOWS) - uint32 BestVer = 0; + uint32_t BestVer = 0; #endif GString Best; for (int i=0; idwProductVersionMS > BestVer) { BestVer = v->dwProductVersionMS; Best = Path; } } } else if (!Best) { Best = Path; } free(Buf); #else Best = Path; break; #endif } } return Best; } else if (Compiler == VisualStudio) { // Find the version we need: double fVer = 0.0; ProjInfo *First = NULL; LHashTbl, ProjInfo*> Projects; const char *Ext = LgiGetExtension(Makefile); if (Ext && !_stricmp(Ext, "sln")) { GFile f; if (f.Open(Makefile, O_READ)) { GString VerKey = "Format Version "; GString ProjKey = "Project("; GString StartSection = "GlobalSection("; GString EndSection = "EndGlobalSection"; GString Section; ssize_t Pos; GString::Array Ln = f.Read().SplitDelimit("\r\n"); for (size_t i = 0; i < Ln.Length(); i++) { GString s = Ln[i]; if ((Pos = s.Find(VerKey)) > 0) { GString sVer = s(Pos + VerKey.Length(), -1); fVer = sVer.Float(); } else if ((Pos = s.Find(ProjKey)) >= 0) { GString::Array p = s.SplitDelimit("(),="); if (p.Length() > 5) { ProjInfo *i = new ProjInfo; i->Name = p[3].Strip(" \t\""); i->File = p[4].Strip(" \t\'\""); i->Guid = p[5].Strip(" \t\""); if (LgiIsRelativePath(i->File)) { char f[MAX_PATH]; LgiMakePath(f, sizeof(f), Makefile, ".."); LgiMakePath(f, sizeof(f), f, i->File); if (FileExists(f)) i->File = f; else LgiAssert(0); } if (!First) First = i; Projects.Add(i->Guid, i); } } else if (s.Find(StartSection) >= 0) { auto p = s.SplitDelimit("() \t"); Section = p[1]; } else if (s.Find(EndSection) >= 0) { Section.Empty(); } else if (Section == "ProjectConfigurationPlatforms") { auto p = s.SplitDelimit(". \t"); auto i = Projects.Find(p[0]); if (i) { if (!i->Configs.Find(p[1])) { int Idx = i->Configs.Length() + 1; i->Configs.Add(p[1], Idx); } } } } } } else if (Ext && !_stricmp(Ext, "vcxproj")) { // ProjFile = Makefile; } else { if (Arch == DefaultArch) { if (sizeof(size_t) == 4) Arch = ArchX32; else Arch = ArchX64; } #ifdef _MSC_VER // Nmake file.. GString NmakePath; switch (_MSC_VER) { case _MSC_VER_VS2013: { if (Arch == ArchX32) NmakePath = "c:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\VC\\bin\\nmake.exe"; else NmakePath = "c:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\VC\\bin\\amd64\\nmake.exe"; break; } case _MSC_VER_VS2015: { if (Arch == ArchX32) NmakePath = "c:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\bin\\nmake.exe"; else NmakePath = "c:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\bin\\amd64\\nmake.exe"; break; } default: { LgiAssert(!"Impl me."); break; } } if (FileExists(NmakePath)) { Compiler = Nmake; return NmakePath; } #endif } /* if (ProjFile && FileExists(ProjFile)) { GString sVer; if (ReadVsProjFile(ProjFile, sVer, BuildConfigs)) { fVer = sVer.Float(); } } */ if (First) { for (auto i: First->Configs) { BuildConfigs[i.value - 1] = i.key; } } if (fVer > 0.0) { GString p; p.Printf("C:\\Program Files (x86)\\Microsoft Visual Studio %.1f\\Common7\\IDE\\devenv.com", fVer); if (FileExists(p)) { return p; } } } else if (Compiler == IAR) { // const char *Def = "c:\\Program Files (x86)\\IAR Systems\\Embedded Workbench 7.0\\common\\bin\\IarBuild.exe"; GString ProgFiles = LGetSystemPath(LSP_USER_APPS, 32); char p[MAX_PATH]; if (!LgiMakePath(p, sizeof(p), ProgFiles, "IAR Systems")) return GString(); GDirectory d; double LatestVer = 0.0; GString Latest; for (int b = d.First(p); b; b = d.Next()) { if (d.IsDir()) { GString n(d.GetName()); GString::Array p = n.Split(" "); if (p.Length() == 3 && p[2].Float() > LatestVer) { LatestVer = p[2].Float(); Latest = n; } } } if (Latest && LgiMakePath(p, sizeof(p), p, Latest) && LgiMakePath(p, sizeof(p), p, "common\\bin\\IarBuild.exe")) { if (FileExists(p)) return p; } } else { if (Compiler == MingW) { // Have a look in the default spot first... const char *Def = "C:\\MinGW\\msys\\1.0\\bin\\make.exe"; if (FileExists(Def)) { return Def; } } for (int i=0; iGetApp()->GetOptions()->GetValue(OPT_Jobs, Jobs) || Jobs.CastInt32() < 1) Jobs = 2; auto Pos = InitDir.RFind(DIR_STR); if (Pos) InitDir.Length(Pos); GString TmpArgs, Include, Lib, LibPath, Path; if (Compiler == VisualStudio) { // TmpArgs.Printf("\"%s\" /make \"All - Win32 Debug\"", Makefile.Get()); GString BuildConf = "All - Win32 Debug"; if (BuildConfigs.Length()) { const char *Key = Release ? "Release" : "Debug"; for (size_t i=0; i= 0) { BuildConf = c; break; } } } TmpArgs.Printf("\"%s\" %s \"%s\"", Makefile.Get(), Clean ? "/Clean" : "/Build", BuildConf.Get()); } else if (Compiler == Nmake) { const char *DefInc[] = { "C:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\VC\\INCLUDE", "C:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\VC\\ATLMFC\\INCLUDE", "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\shared", "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\um", "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\winrt" }; GString f; #define ADD_PATHS(out, in) \ for (unsigned i=0; iGetChildTag("name") : NULL; if (c) Conf = c->GetContent(); } } TmpArgs.Printf("\"%s\" %s %s -log warnings", Makefile.Get(), Clean ? "-clean" : "-make", Conf.Get()); } else { if (Compiler == MingW) { GString a; char *Dir = strrchr(MakePath, DIR_CHAR); #if 1 TmpArgs.Printf("/C \"%s\"", Exe.Get()); /* As of MSYS v1.0.18 the support for multiple jobs causes make to hang: http://sourceforge.net/p/mingw/bugs/1950/ http://mingw-users.1079350.n2.nabble.com/MSYS-make-freezes-td7579038.html Apparently it'll be "fixed" in v1.0.19. We'll see. >:-( if (Jobs.CastInt32() > 1) a.Print(" -j %i", Jobs.CastInt32()); */ a.Printf(" -f \"%s\"", Dir ? Dir + 1 : MakePath.Get()); TmpArgs += a; #else TmpArgs.Printf("/C set"); #endif Exe = "C:\\Windows\\System32\\cmd.exe"; } else { if (Jobs.CastInt32()) TmpArgs.Printf("-j %i -f \"%s\"", Jobs.CastInt32(), MakePath.Get()); else TmpArgs.Printf("-f \"%s\"", MakePath.Get()); } if (Clean) { if (All) { TmpArgs += " cleanall"; } else { TmpArgs += " clean"; } } if (Release) TmpArgs += " Build=Release"; } GString Msg; Msg.Printf("Making: %s\n", MakePath.Get()); Proj->GetApp()->PostEvent(M_APPEND_TEXT, (GMessage::Param)NewStr(Msg), 0); LgiTrace("%s %s\n", Exe.Get(), TmpArgs.Get()); if (SubProc.Reset(new GSubProcess(Exe, TmpArgs))) { SubProc->SetNewGroup(false); SubProc->SetInitFolder(InitDir); if (Include) SubProc->SetEnvironment("INCLUDE", Include); if (Lib) SubProc->SetEnvironment("LIB", Lib); if (LibPath) SubProc->SetEnvironment("LIBPATHS", LibPath); if (Path) { GString Cur = getenv("PATH"); GString New = Cur + LGI_PATH_SEPARATOR + Path; SubProc->SetEnvironment("PATH", New); } // SubProc->SetEnvironment("DLL", "1"); if (Compiler == MingW) SubProc->SetEnvironment("PATH", "c:\\MingW\\bin;C:\\MinGW\\msys\\1.0\\bin;%PATH%"); if ((Status = SubProc->Start(true, false))) { // Read all the output char Buf[256]; ssize_t rd; while ( (rd = SubProc->Read(Buf, sizeof(Buf))) > 0 ) { Write(Buf, rd); } - uint32 ex = SubProc->Wait(); + uint32_t ex = SubProc->Wait(); Print("Make exited with %i (0x%x)\n", ex, ex); if (Compiler == IAR && ex == 0 && PostBuild.Length()) { for (auto Cmd : PostBuild) { auto p = Cmd.Split(" ", 1); if (p[0].Equals("cd")) { if (p.Length() > 1) FileDev->SetCurrentFolder(p[1]); else LgiAssert(!"No folder for cd?"); } else { GSubProcess PostCmd(p[0], p.Length() > 1 ? p[1] : NULL); if (PostCmd.Start(true, false)) { char Buf[256]; ssize_t rd; while ( (rd = PostCmd.Read(Buf, sizeof(Buf))) > 0 ) { Write(Buf, rd); } } } } } } else { // Create a nice error message. GAutoString ErrStr = LgiErrorCodeToString(SubProc->GetErrorCode()); if (ErrStr) { char *e = ErrStr + strlen(ErrStr); while (e > ErrStr && strchr(" \t\r\n.", e[-1])) *(--e) = 0; } sprintf_s(ErrBuf, sizeof(ErrBuf), "Running make failed with %i (%s)\n", SubProc->GetErrorCode(), ErrStr.Get()); Err = ErrBuf; } } } else { Err = "Couldn't find program to build makefile."; LgiTrace("%s,%i - %s.\n", _FL, Err); } AppWnd *w = Proj->GetApp(); if (w) { w->PostEvent(M_BUILD_DONE); if (Err) Proj->GetApp()->PostEvent(M_BUILD_ERR, 0, (GMessage::Param)NewStr(Err)); } else LgiAssert(0); return 0; } ////////////////////////////////////////////////////////////////////////////////// IdeProject::IdeProject(AppWnd *App) : IdeCommon(NULL) { Project = this; d = new IdeProjectPrivate(App, this); Tag = NewStr("Project"); } IdeProject::~IdeProject() { d->App->OnProjectDestroy(this); GXmlTag::Empty(true); DeleteObj(d); } bool IdeProject::OnNode(const char *Path, ProjectNode *Node, bool Add) { if (!Path || !Node) { LgiAssert(0); return false; } char Full[MAX_PATH]; if (LgiIsRelativePath(Path)) { GAutoString Base = GetBasePath(); if (LgiMakePath(Full, sizeof(Full), Base, Path)) { Path = Full; } } bool Status = false; if (Add) Status = d->Nodes.Add(Path, Node); else Status = d->Nodes.Delete(Path); if (Status) d->App->OnNode(Path, Node, Add ? FindSymbolSystem::FileAdd : FindSymbolSystem::FileRemove); return Status; } void IdeProject::ShowFileProperties(const char *File) { ProjectNode *Node = NULL; // char *fp = FindFullPath(File, &Node); if (Node) { Node->OnProperties(); } } const char *IdeProject::GetFileComment() { return d->Settings.GetStr(ProjCommentFile); } const char *IdeProject::GetFunctionComment() { return d->Settings.GetStr(ProjCommentFunction); } IdeProject *IdeProject::GetParentProject() { return d->ParentProject; } void IdeProject::SetParentProject(IdeProject *p) { d->ParentProject = p; } bool IdeProject::GetChildProjects(List &c) { CollectAllSubProjects(c); return c.First() != 0; } bool IdeProject::RelativePath(char *Out, const char *In, bool Debug) { if (Out && In) { GAutoString Base = GetBasePath(); if (Base) { if (Debug) LgiTrace("XmlBase='%s'\n In='%s'\n", Base.Get(), In); GToken b(Base, DIR_STR); GToken i(In, DIR_STR); if (Debug) LgiTrace("Len %i-%i\n", b.Length(), i.Length()); auto ILen = i.Length() + (DirExists(In) ? 0 : 1); auto Max = MIN(b.Length(), ILen); int Common = 0; for (; Common < Max; Common++) { #ifdef WIN32 #define StrCompare stricmp #else #define StrCompare strcmp #endif if (Debug) LgiTrace("Cmd '%s'-'%s'\n", b[Common], i[Common]); if (StrCompare(b[Common], i[Common]) != 0) { break; } } if (Debug) LgiTrace("Common=%i\n", Common); if (Common > 0) { if (Common < b.Length()) { Out[0] = 0; auto Back = b.Length() - Common; if (Debug) LgiTrace("Back=%i\n", (int)Back); for (int n=0; nSettings.GetStr(ProjExe); if (PExe) { if (LgiIsRelativePath(PExe)) { GAutoString Base = GetBasePath(); if (Base) { LgiMakePath(Path, Len, Base, PExe); } else return false; } else { strcpy_s(Path, Len, PExe); } return true; } else return false; } GAutoString IdeProject::GetMakefile() { GAutoString Path; const char *PMakefile = d->Settings.GetStr(ProjMakefile); if (PMakefile) { if (LgiIsRelativePath(PMakefile)) { GAutoString Base = GetBasePath(); if (Base) { char p[MAX_PATH]; LgiMakePath(p, sizeof(p), Base, PMakefile); Path.Reset(NewStr(p)); } } else { Path.Reset(NewStr(PMakefile)); } } return Path; } void IdeProject::Clean(bool All, bool Release) { if (!d->Thread && d->Settings.GetStr(ProjMakefile)) { GAutoString m = GetMakefile(); if (m) d->Thread.Reset(new BuildThread(this, m, true, Release, All, sizeof(ssize_t)*8)); } } char *QuoteStr(char *s) { GStringPipe p(256); while (s && *s) { if (*s == ' ') { p.Push("\\ ", 2); } else p.Push(s, 1); s++; } return p.NewStr(); } class ExecuteThread : public LThread, public GStream { IdeProject *Proj; char *Exe, *Args, *Path; int Len; ExeAction Act; public: ExecuteThread(IdeProject *proj, const char *exe, const char *args, char *path, ExeAction act) : LThread("ExecuteThread") { Len = 32 << 10; Proj = proj; Act = act; Exe = NewStr(exe); Args = NewStr(args); Path = NewStr(path); DeleteOnExit = true; Run(); } ~ExecuteThread() { DeleteArray(Exe); DeleteArray(Args); DeleteArray(Path); } int Main() override { if (Proj->GetApp()) { Proj->GetApp()->PostEvent(M_APPEND_TEXT, 0, 1); } if (Exe) { GProcess p; if (Act == ExeDebug) { char *a = QuoteStr(Exe); char *b = QuoteStr(Path); p.Run("kdbg", a, b, true, 0, this); DeleteArray(a); DeleteArray(b); } else if (Act == ExeValgrind) { #ifdef LINUX GString ExePath = Proj->GetExecutable(GetCurrentPlatform()); if (ExePath) { char Path[MAX_PATH]; char *ExeLeaf = LgiGetLeaf(Exe); strcpy_s(Path, sizeof(Path), ExeLeaf ? ExeLeaf : Exe); LgiTrimDir(Path); char *Term = 0; char *WorkDir = 0; char *Execute = 0; switch (LgiGetWindowManager()) { case WM_Kde: Term = "konsole"; WorkDir = "--workdir "; Execute = "-e"; break; case WM_Gnome: Term = "gnome-terminal"; WorkDir = "--working-directory="; Execute = "-x"; break; } if (Term && WorkDir && Execute) { char *e = QuoteStr(ExePath); char *p = QuoteStr(Path); char *a = Proj->GetExeArgs() ? Proj->GetExeArgs() : (char*)""; char Args[512]; sprintf(Args, "%s%s " "--noclose " "%s valgrind --tool=memcheck --num-callers=20 %s %s", WorkDir, p, Execute, e, a); LgiExecute(Term, Args); } } #endif } else { p.Run(Exe, Args, Path, true, 0, this); } } return 0; } ssize_t Write(const void *Buffer, ssize_t Size, int Flags = 0) override { if (Len > 0) { if (Proj->GetApp()) { Size = MIN(Size, Len); Proj->GetApp()->PostEvent(M_APPEND_TEXT, (GMessage::Param)NewStr((char*)Buffer, Size), 1); Len -= Size; } return Size; } return 0; } }; GDebugContext *IdeProject::Execute(ExeAction Act) { GAutoString Base = GetBasePath(); if (d->Settings.GetStr(ProjExe) && Base) { char e[MAX_PATH]; if (GetExePath(e, sizeof(e))) { if (FileExists(e)) { const char *Args = d->Settings.GetStr(ProjArgs); int RunAsAdmin = d->Settings.GetInt(ProjDebugAdmin); if (Act == ExeDebug) { return new GDebugContext(d->App, this, e, Args, RunAsAdmin != 0); } else { new ExecuteThread(this, e, Args, Base, Act); } } else { LgiMsg(Tree, "Executable '%s' doesn't exist.\n", AppName, MB_OK, e); } } } return NULL; } bool IdeProject::IsMakefileUpToDate() { List Proj; if (GetChildProjects(Proj)) { Proj.Insert(this); for (IdeProject *p = Proj.First(); p; p = Proj.Next()) { // Is the project file modified after the makefile? GAutoString Proj = p->GetFullPath(); uint64 ProjModTime = 0, MakeModTime = 0; GDirectory dir; if (dir.First(Proj)) { ProjModTime = dir.GetLastWriteTime(); dir.Close(); } GAutoString m = p->GetMakefile(); if (!m) { d->App->GetBuildLog()->Print("Error: no makefile? (%s:%i)\n", _FL); break; } if (dir.First(m)) { MakeModTime = dir.GetLastWriteTime(); dir.Close(); } // printf("Proj=%s - Timestamps " LGI_PrintfInt64 " - " LGI_PrintfInt64 "\n", Proj.Get(), ProjModTime, MakeModTime); if (ProjModTime != 0 && MakeModTime != 0 && ProjModTime > MakeModTime) { // Need to rebuild the makefile... return false; } } } return true; } bool IdeProject::FindDuplicateSymbols() { GStream *Log = d->App->GetBuildLog(); Log->Print("FindDuplicateSymbols starting...\n"); List Proj; CollectAllSubProjects(Proj); Proj.Insert(this); int Lines = 0; LHashTbl,int64> Map(200000); int Found = 0; for (IdeProject *p = Proj.First(); p; p = Proj.Next()) { GString s = p->GetExecutable(GetCurrentPlatform()); if (s) { GString Args; Args.Printf("--print-size --defined-only -C %s", s.Get()); GSubProcess Nm("nm", Args); if (Nm.Start(true, false)) { char Buf[256]; GStringPipe q; for (ssize_t Rd = 0; (Rd = Nm.Read(Buf, sizeof(Buf))); ) q.Write(Buf, Rd); GString::Array a = q.NewGStr().SplitDelimit("\r\n"); LHashTbl,bool> Local(200000); for (GString *Ln = NULL; a.Iterate(Ln); Lines++) { GString::Array p = Ln->SplitDelimit(" \t", 3); if (!Local.Find(p.Last())) { Local.Add(p.Last(), true); // const char *Sz = p[1]; int64 Ours = p[1].Int(16); int64 Theirs = Map.Find(p.Last()); if (Theirs >= 0) { if (Ours != Theirs) { if (Found++ < 100) Log->Print(" %s (" LPrintfInt64 " -> " LPrintfInt64 ")\n", p.Last().Get(), Ours, Theirs); } } else if (Ours >= 0) { Map.Add(p.Last(), Ours); } else { printf("Bad line: %s\n", Ln->Get()); } } } } } else printf("%s:%i - GetExecutable failed.\n", _FL); } /* char *Sym; for (int Count = Map.First(&Sym); Count; Count = Map.Next(&Sym)) { if (Count > 1) Log->Print(" %i: %s\n", Count, Sym); } */ Log->Print("FindDuplicateSymbols finished (%i lines)\n", Lines); return false; } bool IdeProject::FixMissingFiles() { FixMissingFilesDlg(this); return true; } void IdeProject::Build(bool All, bool Release) { if (d->Thread) { d->App->GetBuildLog()->Print("Error: Already building (%s:%i)\n", _FL); return; } GAutoString m = GetMakefile(); if (!m) { d->App->GetBuildLog()->Print("Error: no makefile? (%s:%i)\n", _FL); return; } if (GetApp()) GetApp()->PostEvent(M_APPEND_TEXT, 0, 0); SetClean(); if (!IsMakefileUpToDate()) CreateMakefile(GetCurrentPlatform(), true); else // Start the build thread... d->Thread.Reset ( new BuildThread ( this, m, false, Release, All, sizeof(size_t)*8 ) ); } void IdeProject::StopBuild() { d->Thread.Reset(); } bool IdeProject::Serialize(bool Write) { return true; } AppWnd *IdeProject::GetApp() { return d->App; } const char *IdeProject::GetIncludePaths() { return d->Settings.GetStr(ProjIncludePaths); } const char *IdeProject::GetPreDefinedValues() { return d->Settings.GetStr(ProjDefines); } const char *IdeProject::GetExeArgs() { return d->Settings.GetStr(ProjArgs); } GString IdeProject::GetExecutable(IdePlatform Platform) { GString Bin = d->Settings.GetStr(ProjExe, NULL, Platform); GAutoString Base = GetBasePath(); if (Bin) { if (LgiIsRelativePath(Bin) && Base) { char p[MAX_PATH]; if (LgiMakePath(p, sizeof(p), Base, Bin)) Bin = p; } return Bin; } // Create binary name from target: GString Target = GetTargetName(Platform); if (Target) { int BuildMode = d->App->GetBuildMode(); const char *Name = BuildMode ? "Release" : "Debug"; const char *Postfix = BuildMode ? "" : "d"; switch (Platform) { case PlatformWin32: { Bin.Printf("%s%s.dll", Target.Get(), Postfix); break; } case PlatformMac: { Bin.Printf("lib%s%s.dylib", Target.Get(), Postfix); break; } case PlatformLinux: case PlatformHaiku: { Bin.Printf("lib%s%s.so", Target.Get(), Postfix); break; } default: { LgiAssert(0); printf("%s:%i - Unknown platform.\n", _FL); return GString(); } } // Find the actual file... if (!Base) { printf("%s:%i - GetBasePath failed.\n", _FL); return GString(); } char Path[MAX_PATH]; LgiMakePath(Path, sizeof(Path), Base, Name); LgiMakePath(Path, sizeof(Path), Path, Bin); if (FileExists(Path)) Bin = Path; else printf("%s:%i - '%s' doesn't exist.\n", _FL, Path); return Bin; } return GString(); } char *IdeProject::GetFileName() { return d->FileName; } int IdeProject::GetPlatforms() { return PLATFORM_ALL; } GAutoString IdeProject::GetFullPath() { GAutoString Status; if (!d->FileName) { // LgiAssert(!"No path."); return Status; } GArray sections; IdeProject *proj = this; while ( proj && proj->GetFileName() && LgiIsRelativePath(proj->GetFileName())) { sections.AddAt(0, proj->GetFileName()); proj = proj->GetParentProject(); } if (!proj) { // LgiAssert(!"All projects have a relative path?"); return Status; // No absolute path in the parent projects? } char p[MAX_PATH]; strcpy_s(p, sizeof(p), proj->GetFileName()); // Copy the base path if (sections.Length() > 0) LgiTrimDir(p); // Trim off the filename for (int i=0; iFileName.Empty(); d->UserFile.Empty(); d->App->GetTree()->Insert(this); ProjectNode *f = new ProjectNode(this); if (f) { f->SetName("Source"); f->SetType(NodeDir); InsertTag(f); } f = new ProjectNode(this); if (f) { f->SetName("Headers"); f->SetType(NodeDir); InsertTag(f); } d->Settings.Set(ProjEditorTabSize, 4); d->Settings.Set(ProjEditorIndentSize, 4); d->Settings.Set(ProjEditorUseHardTabs, true); d->Dirty = true; Expanded(true); } ProjectStatus IdeProject::OpenFile(char *FileName) { GProfile Prof("IdeProject::OpenFile"); Prof.HideResultsIfBelow(1000); Empty(); Prof.Add("Init"); d->UserNodeFlags.Empty(); if (LgiIsRelativePath(FileName)) { char p[MAX_PATH]; getcwd(p, sizeof(p)); LgiMakePath(p, sizeof(p), p, FileName); d->FileName = p; } else { d->FileName = FileName; } d->UserFile = d->FileName + "." + LCurrentUserName(); if (!d->FileName) { LgiTrace("%s:%i - No filename.\n", _FL); return OpenError; } Prof.Add("FileOpen"); GFile f; if (!f.Open(d->FileName, O_READWRITE)) { LgiTrace("%s:%i - Error: Can't open '%s'.\n", _FL, d->FileName.Get()); return OpenError; } Prof.Add("Xml"); GXmlTree x; GXmlTag r; if (!x.Read(&r, &f)) { LgiTrace("%s:%i - Error: Can't read XML: %s\n", _FL, x.GetErrorMsg()); LgiMsg(Tree, x.GetErrorMsg(), AppName); return OpenError; } Prof.Add("Progress Setup"); #if DEBUG_OPEN_PROGRESS int64 Nodes = r.CountTags(); GProgressDlg Prog(d->App, 1000); Prog.SetDescription("Loading project..."); Prog.SetLimits(0, Nodes); Prog.SetYieldTime(1000); Prog.SetAlwaysOnTop(true); #endif Prof.Add("UserFile"); if (FileExists(d->UserFile)) { GFile Uf; if (Uf.Open(d->UserFile, O_READ)) { GString::Array Ln = Uf.Read().SplitDelimit("\n"); for (unsigned i=0; iUserNodeFlags.Add((int)p[0].Int(), (int)p[1].Int(16)); } } } if (!r.IsTag("Project")) return OpenError; Prof.Add("OnOpen"); bool Ok = OnOpen( #if DEBUG_OPEN_PROGRESS &Prog, #else NULL, #endif &r); #if DEBUG_OPEN_PROGRESS if (Prog.IsCancelled()) return OpenCancel; else #endif if (!Ok) return OpenError; Prof.Add("Insert"); d->App->GetTree()->Insert(this); Expanded(true); Prof.Add("Serialize"); d->Settings.Serialize(&r, false /* read */); return OpenOk; } bool IdeProject::SaveFile() { GAutoString Full = GetFullPath(); printf("IdeProject::SaveFile %s %i\n", Full.Get(), d->Dirty); if (ValidStr(Full) && d->Dirty) { GFile f; if (f.Open(Full, O_WRITE)) { f.SetSize(0); GXmlTree x; GProgressDlg Prog(d->App, 1000); Prog.SetAlwaysOnTop(true); Prog.SetDescription("Serializing project XML..."); Prog.SetYieldTime(200); Prog.SetCanCancel(false); d->Settings.Serialize(this, true /* write */); GStringPipe Buf(4096); if (x.Write(this, &Buf, &Prog)) { GCopyStreamer Cp; Prog.SetDescription("Writing XML..."); LgiYield(); if (Cp.Copy(&Buf, &f)) d->Dirty = false; } else LgiTrace("%s:%i - Failed to write XML.\n", _FL); } else LgiTrace("%s:%i - Couldn't open '%s' for writing.\n", _FL, Full.Get()); } if (d->UserFileDirty) { GFile f; LgiAssert(d->UserFile.Get()); if (f.Open(d->UserFile, O_WRITE)) { f.SetSize(0); // Save user file details.. // int Id; // for (int Flags = d->UserNodeFlags.First(&Id); Flags >= 0; Flags = d->UserNodeFlags.Next(&Id)) for (auto i : d->UserNodeFlags) { f.Print("%i,%x\n", i.key, i.value); } d->UserFileDirty = false; } } printf("\tIdeProject::SaveFile %i %i\n", d->Dirty, d->UserFileDirty); return !d->Dirty && !d->UserFileDirty; } void IdeProject::SetDirty() { d->Dirty = true; d->App->OnProjectChange(); } void IdeProject::SetUserFileDirty() { d->UserFileDirty = true; } bool IdeProject::GetExpanded(int Id) { int f = d->UserNodeFlags.Find(Id); return f >= 0 ? f : false; } void IdeProject::SetExpanded(int Id, bool Exp) { if (d->UserNodeFlags.Find(Id) != (int)Exp) { d->UserNodeFlags.Add(Id, Exp); SetUserFileDirty(); } } int IdeProject::AllocateId() { return d->NextNodeId++; } bool IdeProject::SetClean() { // printf("IdeProject::SetClean %i %i\n", d->Dirty, d->UserFileDirty); if (d->Dirty || d->UserFileDirty) { if (!ValidStr(d->FileName)) { GFileSelect s; s.Parent(Tree); s.Name("Project.xml"); if (s.Save()) { d->FileName = s.Name(); d->UserFile = d->FileName + "." + LCurrentUserName(); d->App->OnFile(d->FileName, true); Update(); } else return false; } SaveFile(); } ForAllProjectNodes(p) { p->SetClean(); } return true; } char *IdeProject::GetText(int Col) { if (d->FileName) { char *s = strrchr(d->FileName, DIR_CHAR); return s ? s + 1 : d->FileName.Get(); } return (char*)Untitled; } int IdeProject::GetImage(int Flags) { return 0; } void IdeProject::Empty() { GXmlTag *t; while ((t = Children.First())) { ProjectNode *n = dynamic_cast(t); if (n) { n->Remove(); } DeleteObj(t); } } GXmlTag *IdeProject::Create(char *Tag) { if (!stricmp(Tag, TagSettings)) return NULL; return new ProjectNode(this); } void IdeProject::OnMouseClick(GMouse &m) { if (m.IsContextMenu()) { GSubMenu Sub; Sub.AppendItem("New Folder", IDM_NEW_FOLDER); Sub.AppendItem("New Web Folder", IDM_WEB_FOLDER); Sub.AppendSeparator(); Sub.AppendItem("Build", IDM_BUILD); Sub.AppendItem("Clean Project", IDM_CLEAN_PROJECT); Sub.AppendItem("Clean All", IDM_CLEAN_ALL); Sub.AppendItem("Rebuild Project", IDM_REBUILD_PROJECT); Sub.AppendItem("Rebuild All", IDM_REBUILD_ALL); Sub.AppendSeparator(); Sub.AppendItem("Sort Children", IDM_SORT_CHILDREN); Sub.AppendSeparator(); Sub.AppendItem("Settings", IDM_SETTINGS, true); Sub.AppendItem("Insert Dependency", IDM_INSERT_DEP); m.ToScreen(); GdcPt2 c = _ScrollPos(); m.x -= c.x; m.y -= c.y; switch (Sub.Float(Tree, m.x, m.y)) { case IDM_NEW_FOLDER: { GInput Name(Tree, "", "Name:", AppName); if (Name.DoModal()) { GetSubFolder(this, Name.GetStr(), true); } break; } case IDM_WEB_FOLDER: { WebFldDlg Dlg(Tree, 0, 0, 0); if (Dlg.DoModal()) { if (Dlg.Ftp && Dlg.Www) { IdeCommon *f = GetSubFolder(this, Dlg.Name, true); if (f) { f->SetAttr(OPT_Ftp, Dlg.Ftp); f->SetAttr(OPT_Www, Dlg.Www); } } } break; } case IDM_BUILD: { StopBuild(); Build(true, d->App->IsReleaseMode()); break; } case IDM_CLEAN_PROJECT: { Clean(false, d->App->IsReleaseMode()); break; } case IDM_CLEAN_ALL: { Clean(true, d->App->IsReleaseMode()); break; } case IDM_REBUILD_PROJECT: { StopBuild(); Clean(false, d->App->IsReleaseMode()); Build(false, d->App->IsReleaseMode()); break; } case IDM_REBUILD_ALL: { StopBuild(); Clean(true, d->App->IsReleaseMode()); Build(true, d->App->IsReleaseMode()); break; } case IDM_SORT_CHILDREN: { SortChildren(); Project->SetDirty(); break; } case IDM_SETTINGS: { if (d->Settings.Edit(Tree)) { SetDirty(); } break; } case IDM_INSERT_DEP: { GFileSelect s; s.Parent(Tree); s.Type("Project", "*.xml"); if (s.Open()) { ProjectNode *New = new ProjectNode(this); if (New) { New->SetFileName(s.Name()); New->SetType(NodeDependancy); InsertTag(New); SetDirty(); } } break; } } } } char *IdeProject::FindFullPath(const char *File, ProjectNode **Node) { char *Full = 0; ForAllProjectNodes(c) { ProjectNode *n = c->FindFile(File, &Full); if (n) { if (Node) *Node = n; break; } } return Full; } bool IdeProject::HasNode(ProjectNode *Node) { ForAllProjectNodes(c) { if (c->HasNode(Node)) return true; } return false; } bool IdeProject::GetAllNodes(GArray &Nodes) { ForAllProjectNodes(c) { c->AddNodes(Nodes); } return true; } bool IdeProject::InProject(bool FuzzyMatch, const char *Path, bool Open, IdeDoc **Doc) { if (!Path) return false; // Search complete path first... ProjectNode *n = d->Nodes.Find(Path); if (!n && FuzzyMatch) { // No match, do partial matching. const char *Leaf = LgiGetLeaf(Path); auto PathLen = strlen(Path); auto LeafLen = strlen(Leaf); - uint32 MatchingScore = 0; + uint32_t MatchingScore = 0; // Traverse all nodes and try and find the best fit. // const char *p; // for (ProjectNode *Cur = d->Nodes.First(&p); Cur; Cur = d->Nodes.Next(&p)) for (auto Cur : d->Nodes) { int CurPlatform = Cur.value->GetPlatforms(); - uint32 Score = 0; + uint32_t Score = 0; if (stristr(Cur.key, Path)) { Score += PathLen; } else if (stristr(Cur.key, Leaf)) { Score += LeafLen; } const char *pLeaf = LgiGetLeaf(Cur.key); if (pLeaf && !stricmp(pLeaf, Leaf)) { Score |= 0x80000000; } if (Score && (CurPlatform & PLATFORM_CURRENT) != 0) { Score |= 0x40000000; } if (Score > MatchingScore) { MatchingScore = Score; n = Cur.value; } } } if (n && Doc) { *Doc = n->Open(); } return n != 0; } char *GetQuotedStr(char *Str) { char *s = strchr(Str, '\"'); if (s) { s++; char *e = strchr(s, '\"'); if (e) { return NewStr(s, e - s); } } return 0; } void IdeProject::ImportDsp(char *File) { if (File && FileExists(File)) { char Base[256]; strcpy(Base, File); LgiTrimDir(Base); char *Dsp = ReadTextFile(File); if (Dsp) { GToken Lines(Dsp, "\r\n"); IdeCommon *Current = this; bool IsSource = false; for (int i=0; iGetSubFolder(this, Folder, true); if (Sub) { Current = Sub; } DeleteArray(Folder); } } else if (strnicmp(L, "# End Group", 11) == 0) { IdeCommon *Parent = dynamic_cast(Current->GetParent()); if (Parent) { Current = Parent; } } else if (strnicmp(L, "# Begin Source", 14) == 0) { IsSource = true; } else if (strnicmp(L, "# End Source", 12) == 0) { IsSource = false; } else if (Current && IsSource && strncmp(L, "SOURCE=", 7) == 0) { ProjectNode *New = new ProjectNode(this); if (New) { char *Src = 0; if (strchr(L, '\"')) { Src = GetQuotedStr(L); } else { Src = NewStr(L + 7); } if (Src) { // Make absolute path char Abs[256]; LgiMakePath(Abs, sizeof(Abs), Base, ToUnixPath(Src)); // Make relitive path New->SetFileName(Src); DeleteArray(Src); } Current->InsertTag(New); SetDirty(); } } } DeleteArray(Dsp); } } } IdeProjectSettings *IdeProject::GetSettings() { return &d->Settings; } bool IdeProject::BuildIncludePaths(GArray &Paths, bool Recurse, bool IncludeSystem, IdePlatform Platform) { List Projects; if (Recurse) { GetChildProjects(Projects); } Projects.Insert(this, 0); LHashTbl, bool> Map; for (IdeProject *p=Projects.First(); p; p=Projects.Next()) { GString ProjInclude = d->Settings.GetStr(ProjIncludePaths, NULL, Platform); GAutoString Base = p->GetBasePath(); const char *Delim = ",;\r\n"; GString::Array In, Out; GString::Array a = ProjInclude.SplitDelimit(Delim); In = a; if (IncludeSystem) { GString SysInclude = d->Settings.GetStr(ProjSystemIncludes, NULL, Platform); a = SysInclude.SplitDelimit(Delim); In.SetFixedLength(false); In.Add(a); } for (unsigned i=0; i 1 ? a[1].Get() : NULL, NULL, true, NULL, &Buf)) { GString result = Buf.NewGStr(); a = result.Split(" \t\r\n"); for (int i=0; i Nodes; if (p->GetAllNodes(Nodes)) { GAutoString Base = p->GetFullPath(); if (Base) { LgiTrimDir(Base); for (unsigned i=0; iGetType() == NodeHeader && // Only look at headers. (n->GetPlatforms() & (1 << Platform)) != 0) // Exclude files not on this platform. { char *f = n->GetFileName(); char p[MAX_PATH]; if (f && LgiMakePath(p, sizeof(p), Base, f)) { char *l = strrchr(p, DIR_CHAR); if (l) *l = 0; if (!Map.Find(p)) { Map.Add(p, true); } } } } } } } // char *p; // for (bool b = Map.First(&p); b; b = Map.Next(&p)) for (auto p : Map) Paths.Add(p.key); return true; } void IdeProjectPrivate::CollectAllFiles(GTreeNode *Base, GArray &Files, bool SubProjects, int Platform) { for (GTreeItem *i = Base->GetChild(); i; i = i->GetNext()) { IdeProject *Proj = dynamic_cast(i); if (Proj) { if (Proj->GetParentProject() && !SubProjects) { continue; } } else { ProjectNode *p = dynamic_cast(i); if (p) { if (p->GetType() == NodeSrc || p->GetType() == NodeHeader) { if (p->GetPlatforms() & Platform) { Files.Add(p); } } } } CollectAllFiles(i, Files, SubProjects, Platform); } } GString IdeProject::GetTargetName(IdePlatform Platform) { GString Status; const char *t = d->Settings.GetStr(ProjTargetName, NULL, Platform); if (ValidStr(t)) { // Take target name from the settings Status = t; } else { char *s = strrchr(d->FileName, DIR_CHAR); if (s) { // Generate the target executable name char Target[MAX_PATH]; strcpy_s(Target, sizeof(Target), s + 1); s = strrchr(Target, '.'); if (s) *s = 0; strlwr(Target); s = Target; for (char *i = Target; *i; i++) { if (*i != '.' && *i != ' ') { *s++ = *i; } } *s = 0; Status = Target; } } return Status; } bool IdeProject::GetTargetFile(char *Buf, int BufSize) { bool Status = false; GString Target = GetTargetName(PlatformCurrent); if (Target) { const char *TargetType = d->Settings.GetStr(ProjTargetType); if (TargetType) { if (!stricmp(TargetType, "Executable")) { strcpy_s(Buf, BufSize, Target); Status = true; } else if (!stricmp(TargetType, "DynamicLibrary")) { char t[MAX_PATH]; strcpy_s(t, sizeof(t), Target); char *ext = LgiGetExtension(t); if (!ext) sprintf(t + strlen(t), ".%s", LGI_LIBRARY_EXT); else if (stricmp(ext, LGI_LIBRARY_EXT)) strcpy(ext, LGI_LIBRARY_EXT); strcpy_s(Buf, BufSize, t); Status = true; } else if (!stricmp(TargetType, "StaticLibrary")) { snprintf(Buf, BufSize, "lib%s.%s", Target.Get(), LGI_STATIC_LIBRARY_EXT); Status = true; } } } return Status; } struct Dependency { bool Scanned; GAutoString File; Dependency(const char *f) { Scanned = false; File.Reset(NewStr(f)); } }; bool IdeProject::GetAllDependencies(GArray &Files, IdePlatform Platform) { if (!GetTree()->Lock(_FL)) return false; LHashTbl, Dependency*> Deps; GAutoString Base = GetBasePath(); // Build list of all the source files... GArray Src; CollectAllSource(Src, Platform); // Get all include paths GArray IncPaths; BuildIncludePaths(IncPaths, false, false, Platform); // Add all source to dependencies for (int i=0; i Unscanned; do { // Find all the unscanned dependencies Unscanned.Length(0); // for (Dependency *d = Deps.First(); d; d = Deps.Next()) for (auto d : Deps) { if (!d.value->Scanned) Unscanned.Add(d.value); } for (int i=0; iScanned = true; char *Src = d->File; char Full[MAX_PATH]; if (LgiIsRelativePath(d->File)) { LgiMakePath(Full, sizeof(Full), Base, d->File); Src = Full; } GArray SrcDeps; if (GetDependencies(Src, IncPaths, SrcDeps, Platform)) { for (int n=0; n 0); // for (Dependency *d = Deps.First(); d; d = Deps.Next()) for (auto d : Deps) { Files.Add(d.value->File.Release()); } Deps.DeleteObjects(); GetTree()->Unlock(); return true; } bool IdeProject::GetDependencies(const char *SourceFile, GArray &IncPaths, GArray &Files, IdePlatform Platform) { if (!FileExists(SourceFile)) { LgiTrace("%s:%i - can't read '%s'\n", _FL, SourceFile); return false; } GAutoString c8(ReadTextFile(SourceFile)); if (!c8) return false; GArray Headers; if (!BuildHeaderList(c8, Headers, IncPaths, false)) return false; for (int n=0; nCreateMakefile) { if (d->CreateMakefile->IsExited()) d->CreateMakefile.Reset(); else { d->App->GetBuildLog()->Print("%s:%i - Makefile thread still running.\n", _FL); return false; } } return d->CreateMakefile.Reset(new MakefileThread(d, Platform, BuildAfterwards)); } //////////////////////////////////////////////////////////////////////////////////////////// IdeTree::IdeTree() : GTree(IDC_PROJECT_TREE, 0, 0, 100, 100) { Hit = 0; MultiSelect(true); } void IdeTree::OnCreate() { SetWindow(this); } void IdeTree::OnDragExit() { SelectDropTarget(0); } int IdeTree::WillAccept(List &Formats, GdcPt2 p, int KeyState) { static bool First = true; for (char *f=Formats.First(); f; ) { /* if (First) LgiTrace(" WillAccept='%s'\n", f); */ if (stricmp(f, NODE_DROP_FORMAT) == 0 || stricmp(f, LGI_FileDropFormat) == 0) { f = Formats.Next(); } else { Formats.Delete(f); DeleteArray(f); f = Formats.Current(); } } First = false; if (Formats.Length() > 0) { Hit = ItemAtPoint(p.x, p.y); if (Hit) { if (!stricmp(Formats.First(), LGI_FileDropFormat)) { SelectDropTarget(Hit); return DROPEFFECT_LINK; } else { IdeCommon *Src = dynamic_cast(Selection()); IdeCommon *Dst = dynamic_cast(Hit); if (Src && Dst) { // Check this folder is not a child of the src for (IdeCommon *n=Dst; n; n=dynamic_cast(n->GetParent())) { if (n == Src) { return DROPEFFECT_NONE; } } } // Valid target SelectDropTarget(Hit); return DROPEFFECT_MOVE; } } } else LgiTrace("%s:%i - No valid drop formats.\n", _FL); return DROPEFFECT_NONE; } int IdeTree::OnDrop(GArray &Data, GdcPt2 p, int KeyState) { SelectDropTarget(0); if (!Hit) Hit = ItemAtPoint(p.x, p.y); if (!Hit) return DROPEFFECT_NONE; for (unsigned n=0; nType == GV_BINARY && Data->Value.Binary.Length == sizeof(ProjectNode*)) { ProjectNode *Src = ((ProjectNode**)Data->Value.Binary.Data)[0]; if (Src) { ProjectNode *Folder = dynamic_cast(Hit); while (Folder && Folder->GetType() > NodeDir) { Folder = dynamic_cast(Folder->GetParent()); } IdeCommon *Dst = dynamic_cast(Folder?Folder:Hit); if (Dst) { // Check this folder is not a child of the src for (IdeCommon *n=Dst; n; n=dynamic_cast(n->GetParent())) { if (n == Src) { return DROPEFFECT_NONE; } } // Detach GTreeItem *i = dynamic_cast(Src); i->Detach(); if (Src->GXmlTag::Parent) { LgiAssert(Src->GXmlTag::Parent->Children.HasItem(Src)); Src->GXmlTag::Parent->Children.Delete(Src); } // Attach Src->GXmlTag::Parent = Dst; Dst->Children.Insert(Src); Dst->Insert(Src); // Dirty Src->GetProject()->SetDirty(); } return DROPEFFECT_MOVE; } } } else if (dd.IsFileDrop()) { ProjectNode *Folder = dynamic_cast(Hit); while (Folder && Folder->GetType() > NodeDir) { Folder = dynamic_cast(Folder->GetParent()); } IdeCommon *Dst = dynamic_cast(Folder?Folder:Hit); if (Dst) { AddFilesProgress Prog(this); GDropFiles Df(dd); for (int i=0; iAddFiles(&Prog, Df[i]); } } } else { LgiTrace("%s:%i - Unknown drop format: %s.\n", _FL, dd.Format.Get()); } } return DROPEFFECT_NONE; } ///////////////////////////////////////////////////////////////////////////////////////////////// AddFilesProgress::AddFilesProgress(GViewI *par) { v = 0; Cancel = false; Msg = NULL; SetParent(par); Ts = LgiCurrentTime(); GRect r(0, 0, 140, 100); SetPos(r); MoveSameScreen(par); Name("Importing files..."); GString::Array a = GString(DefaultExt).SplitDelimit(","); for (unsigned i=0; iGetCell(0, 0); c->Add(new GTextLabel(-1, 0, 0, -1, -1, "Loaded:")); c = t->GetCell(1, 0); c->Add(Msg = new GTextLabel(-1, 0, 0, -1, -1, "...")); c = t->GetCell(0, 1, true, 2); c->TextAlign(GCss::Len(GCss::AlignRight)); c->Add(new GButton(IDCANCEL, 0, 0, -1, -1, "Cancel")); } int64 AddFilesProgress::Value() { return v; } void AddFilesProgress::Value(int64 val) { v = val; if (Visible() && Msg) { Msg->Value(v); Msg->SendNotify(GNotifyTableLayout_Refresh); } uint64 Now = LgiCurrentTime(); if (Visible()) { if (Now - Ts > 150) LgiYield(); } else if (Now - Ts > 1000) { DoModeless(); } } int AddFilesProgress::OnNotify(GViewI *c, int f) { if (c->GetId() == IDCANCEL) Cancel = true; return 0; } diff --git a/Ide/Code/IdeProjectSettings.cpp b/Ide/Code/IdeProjectSettings.cpp --- a/Ide/Code/IdeProjectSettings.cpp +++ b/Ide/Code/IdeProjectSettings.cpp @@ -1,992 +1,992 @@ #include "Lgi.h" #include "LgiIde.h" #include "IdeProject.h" #include "GTableLayout.h" #include "resdefs.h" #include "GTextLabel.h" #include "GEdit.h" #include "GCheckBox.h" #include "GCombo.h" #include "GButton.h" const char TagSettings[] = "Settings"; // const char TagConfigs[] = "Configurations"; const char sGeneral[] = "General"; const char sBuild[] = "Build"; const char sEditor[] = "Editor"; const char sAdvanced[] = "Advanced"; const char sDebug[] = "Debug"; const char sRelease[] = "Release"; const char sAllPlatforms[] = "All"; const char sCurrentPlatform[] = #if defined WIN32 "Windows" #elif defined LINUX "Linux" #elif defined MAC "Mac" #elif defined BEOS "Haiku" #else #error "Not impl" #endif ; const char *sCompilers[] = { #if defined WIN32 "VisualStudio", "Cygwin", "MingW", "IAR", #elif defined MAC "XCode", #else "gcc", "cross", #endif NULL }; const char *sBuildTypes[] = { "Executable", "StaticLibrary", "DynamicLibrary", NULL }; static const char **GetEnumValues(ProjSetting s) { switch (s) { case ProjCompiler: return sCompilers; case ProjTargetType: return sBuildTypes; default: LgiAssert(!"Unknown enum type."); break; } return NULL; } #define SF_MULTILINE 0x01 // String setting is multiple lines, otherwise it's single line #define SF_CROSSPLATFORM 0x02 // Just has a "all" platforms setting (no platform specific) #define SF_PLATFORM_SPECIFC 0x04 // Just has a platform specific setting (no all) #define SF_CONFIG_SPECIFIC 0x08 // Can have a different setting in different configs #define SF_ENUM 0x10 // Integer is actually an enum index, not a straight number #define SF_FILE_SELECT 0x20 // UI needs a file selection button #define IDC_TEXT_BASE 100 #define IDC_EDIT_BASE 200 #define IDC_CHECKBOX_BASE 300 #define IDC_COMBO_BASE 400 #define IDC_BROWSE_FILE 500 struct SettingInfo { struct BitFlags { - uint32 MultiLine : 1; - uint32 CrossPlatform : 1; - uint32 PlatformSpecific : 1; - uint32 ConfigSpecific : 1; - uint32 Enum : 1; - uint32 FileSelect : 1; + uint32_t MultiLine : 1; + uint32_t CrossPlatform : 1; + uint32_t PlatformSpecific : 1; + uint32_t ConfigSpecific : 1; + uint32_t Enum : 1; + uint32_t FileSelect : 1; }; ProjSetting Setting; int Type; const char *Name; const char *Category; union { - uint32 Flags; + uint32_t Flags; BitFlags Flag; }; }; SettingInfo AllSettings[] = { {ProjMakefile, GV_STRING, "Makefile", sGeneral, SF_PLATFORM_SPECIFC|SF_FILE_SELECT}, {ProjExe, GV_STRING, "Executable", sGeneral, SF_PLATFORM_SPECIFC|SF_CONFIG_SPECIFIC}, {ProjArgs, GV_STRING, "Arguments", sGeneral, SF_CROSSPLATFORM|SF_CONFIG_SPECIFIC}, {ProjDebugAdmin, GV_BOOL, "DebugAdmin", sGeneral, SF_CROSSPLATFORM}, {ProjDefines, GV_STRING, "Defines", sGeneral, SF_MULTILINE|SF_CONFIG_SPECIFIC}, {ProjCompiler, GV_INT32, "Compiler", sGeneral, SF_PLATFORM_SPECIFC|SF_ENUM}, {ProjCCrossCompiler, GV_STRING, "CCrossCompiler", sGeneral, SF_PLATFORM_SPECIFC|SF_FILE_SELECT}, {ProjCppCrossCompiler, GV_STRING, "CppCrossCompiler", sGeneral, SF_PLATFORM_SPECIFC|SF_FILE_SELECT}, {ProjIncludePaths, GV_STRING, "IncludePaths", sBuild, SF_MULTILINE|SF_CONFIG_SPECIFIC}, {ProjSystemIncludes, GV_STRING, "SystemIncludes", sBuild, SF_MULTILINE|SF_CONFIG_SPECIFIC|SF_PLATFORM_SPECIFC}, {ProjLibraries, GV_STRING, "Libraries", sBuild, SF_MULTILINE|SF_CONFIG_SPECIFIC}, {ProjLibraryPaths, GV_STRING, "LibraryPaths", sBuild, SF_MULTILINE|SF_CONFIG_SPECIFIC}, {ProjTargetType, GV_INT32, "TargetType", sBuild, SF_CROSSPLATFORM|SF_ENUM}, {ProjTargetName, GV_STRING, "TargetName", sBuild, SF_PLATFORM_SPECIFC|SF_CONFIG_SPECIFIC}, {ProjApplicationIcon, GV_STRING, "ApplicationIcon", sBuild, SF_PLATFORM_SPECIFC|SF_FILE_SELECT}, {ProjEditorTabSize, GV_INT32, "TabSize", sEditor, SF_CROSSPLATFORM}, {ProjEditorIndentSize, GV_INT32, "IndentSize", sEditor, SF_CROSSPLATFORM}, {ProjEditorShowWhiteSpace, GV_BOOL, "ShowWhiteSpace", sEditor, SF_CROSSPLATFORM}, {ProjEditorUseHardTabs, GV_BOOL, "UseHardTabs", sEditor, SF_CROSSPLATFORM}, {ProjCommentFile, GV_STRING, "CommentFile", sEditor, SF_MULTILINE|SF_CROSSPLATFORM}, {ProjCommentFunction, GV_STRING, "CommentFunction", sEditor, SF_MULTILINE|SF_CROSSPLATFORM}, {ProjMakefileRules, GV_STRING, "OtherMakefileRules", sAdvanced, SF_MULTILINE}, {ProjPostBuildCommands, GV_STRING, "PostBuildCommands", sAdvanced, SF_PLATFORM_SPECIFC|SF_CONFIG_SPECIFIC|SF_MULTILINE}, {ProjNone, GV_NULL, NULL, NULL, 0}, }; static void ClearEmptyTags(GXmlTag *t) { for (int i=0; iChildren.Length(); i++) { GXmlTag *c = t->Children[i]; if (!c->GetContent() && !c->Children.Length()) { c->RemoveTag(); i--; DeleteObj(c); } else { ClearEmptyTags(c); } } } struct IdeProjectSettingsPriv { char PathBuf[256]; public: IdeProject *Project; LHashTbl, SettingInfo*> Map; IdeProjectSettings *Parent; GXmlTag Active; GXmlTag Editing; GAutoString CurConfig; GArray Configs; GAutoString StrBuf; // Temporary storage for generated settings IdeProjectSettingsPriv(IdeProjectSettings *parent) : Active(TagSettings) { Parent = parent; Project = NULL; for (SettingInfo *i = AllSettings; i->Setting; i++) { Map.Add(i->Setting, i); } Configs.Add(NewStr(sDebug)); Configs.Add(NewStr(sRelease)); CurConfig.Reset(NewStr(sDebug)); } ~IdeProjectSettingsPriv() { Configs.DeleteArrays(); } int FindConfig(const char *Cfg) { for (int i=0; i= 0 && Platform < PlatformMax) { return PlatformNames[Platform]; } return sCurrentPlatform; } return sAllPlatforms; } char *BuildPath(ProjSetting s, int Flags, IdePlatform Platform, int Config = -1) { SettingInfo *i = Map.Find(s); LgiAssert(i); const char *PlatformStr = PlatformToString(Flags, Platform); int Ch = sprintf_s( PathBuf, sizeof(PathBuf), "%s.%s.%s", i->Category, i->Name, PlatformStr); if (i->Flag.ConfigSpecific) { sprintf_s( PathBuf+Ch, sizeof(PathBuf)-Ch, ".%s", Config >= 0 ? Configs[Config] : CurConfig); } return PathBuf; } }; class GSettingDetail : public GLayout, public ResObject { GTableLayout *Tbl; IdeProjectSettingsPriv *d; SettingInfo *Setting; int Flags; struct CtrlInfo { GTextLabel *Text; GEdit *Edit; GCheckBox *Chk; GCombo *Cbo; CtrlInfo() { Text = NULL; Edit = NULL; Chk = NULL; Cbo = NULL; } }; GArray Ctrls; public: GSettingDetail() : ResObject(Res_Custom) { Flags = 0; d = NULL; Setting = NULL; AddView(Tbl = new GTableLayout); } void OnCreate() { AttachChildren(); } void OnPaint(GSurface *pDC) { pDC->Colour(LC_MED, 24); pDC->Rectangle(); } bool OnLayout(GViewLayoutInfo &Inf) { Inf.Width.Min = -1; Inf.Width.Max = -1; Inf.Height.Min = -1; Inf.Height.Max = -1; return true; } void SetPriv(IdeProjectSettingsPriv *priv) { d = priv; } void AddLine(int i, int Config) { char *Path; int CellY = i * 2; // Do label cell GLayoutCell *c = Tbl->GetCell(0, CellY); c->Add(Ctrls[i].Text = new GTextLabel(IDC_TEXT_BASE + i, 0, 0, -1, -1, Path = d->BuildPath(Setting->Setting, Flags, PlatformCurrent, Config))); // Do value cell c = Tbl->GetCell(0, CellY + 1); GXmlTag *t = d->Editing.GetChildTag(Path); if (Setting->Type == GV_STRING) { c->Add(Ctrls[i].Edit = new GEdit(IDC_EDIT_BASE + i, 0, 0, 60, 20)); Ctrls[i].Edit->MultiLine(Setting->Flag.MultiLine); if (t && t->GetContent()) Ctrls[i].Edit->Name(t->GetContent()); if (Setting->Flag.FileSelect) { c = Tbl->GetCell(1, CellY + 1); if (c) { c->Add(new GButton(IDC_BROWSE_FILE + i, 0, 0, -1, -1, "...")); } else LgiTrace("%s:%i - No cell.\n", _FL); } } else if (Setting->Type == GV_INT32) { if (Setting->Flag.Enum) { // Enum setting c->Add(Ctrls[i].Cbo = new GCombo(IDC_COMBO_BASE + i, 0, 0, 60, 20)); const char **Init = GetEnumValues(Setting->Setting); if (Init) { for (int n=0; Init[n]; n++) { Ctrls[i].Cbo->Insert(Init[n]); if (t && t->GetContent() && !stricmp(t->GetContent(), Init[n])) Ctrls[i].Cbo->Value(n); } } } else { // Straight integer c->Add(Ctrls[i].Edit = new GEdit(IDC_EDIT_BASE + i, 0, 0, 60, 20)); if (t) Ctrls[i].Edit->Value(t->GetContent() ? atoi(t->GetContent()) : 0); } } else if (Setting->Type == GV_BOOL) { c->Add(Ctrls[i].Chk = new GCheckBox(IDC_CHECKBOX_BASE + i, 0, 0, -1, -1, NULL)); if (t && t->GetContent()) Ctrls[i].Chk->Value(atoi(t->GetContent())); } else LgiAssert(!"Unknown type?"); } void SetSetting(SettingInfo *setting, int flags) { if (Setting) { // Save previous values for (int i=0; iName(); if (!Path) { LgiAssert(0); continue; } GXmlTag *t = d->Editing.GetChildTag(Path, true); if (!t) { LgiAssert(0); continue; } if (Ctrls[i].Edit) { char *Val = Ctrls[i].Edit->Name(); // LgiTrace("Saving edit setting '%s': '%s'\n", Path, Val); t->SetContent(Val); } else if (Ctrls[i].Chk) { int64 Val = Ctrls[i].Chk->Value(); // LgiTrace("Saving bool setting '%s': "LGI_PrintfInt64"\n", Path, Val); t->SetContent((int)Val); } else if (Ctrls[i].Cbo) { char *Val = Ctrls[i].Cbo->Name(); // LgiTrace("Saving enum setting '%s': '%s'\n", Path, Val); t->SetContent(Val); } else LgiAssert(0); } } Tbl->Empty(); Ctrls.Length(0); Setting = setting; Flags = flags; if (Setting) { // GLayoutCell *c; if (Setting->Flag.ConfigSpecific) { for (int i=0; iConfigs.Length(); i++) { AddLine(i, i); } } else { AddLine(0, -1); } Tbl->InvalidateLayout(); Tbl->AttachChildren(); Tbl->SetPos(GetClient()); } } void OnPosChange() { Tbl->SetPos(GetClient()); } }; class GSettingDetailFactory : public GViewFactory { GView *NewView ( const char *Class, GRect *Pos, const char *Text ) { if (!stricmp(Class, "GSettingDetail")) return new GSettingDetail; return NULL; } } SettingDetailFactory; class ProjectSettingsDlg; class SettingItem : public GTreeItem { ProjectSettingsDlg *Dlg; public: SettingInfo *Setting; int Flags; SettingItem(SettingInfo *setting, int flags, ProjectSettingsDlg *dlg) { Setting = setting; Dlg = dlg; Flags = flags; } void Select(bool b); }; class ProjectSettingsDlg : public GDialog { IdeProjectSettingsPriv *d; GTree *Tree; GSettingDetail *Detail; uint64 DefLockOut; public: ProjectSettingsDlg(GViewI *parent, IdeProjectSettingsPriv *priv) { Tree = NULL; Detail = NULL; DefLockOut = 0; d = priv; SetParent(parent); if (LoadFromResource(IDD_PROJECT_SETTINGS)) { MoveToCenter(); GAutoString FullPath = d->Project->GetFullPath(); if (FullPath) { SetCtrlName(IDC_PATH, FullPath); } if (GetViewById(IDC_SETTINGS, Tree)) { const char *Section = NULL; GTreeItem *SectionItem = NULL; for (SettingInfo *i = AllSettings; i->Setting; i++) { if (!SectionItem || (Section && stricmp(i->Category, Section))) { Section = i->Category; SectionItem = new GTreeItem(); SectionItem->SetText(i->Category); Tree->Insert(SectionItem); } GTreeItem *Item = new GTreeItem(); Item->SetText(i->Name); SectionItem->Insert(Item); SectionItem->Expanded(true); if (!i->Flag.PlatformSpecific) { SettingItem *All = new SettingItem(i, 0, this); All->SetText(sAllPlatforms); Item->Insert(All); } if (!i->Flag.CrossPlatform) { SettingItem *Platform = new SettingItem(i, SF_PLATFORM_SPECIFC, this); Platform->SetText(sCurrentPlatform); Item->Insert(Platform); } } } if (GetViewById(IDC_DETAIL, Detail)) { Detail->SetPriv(d); } GView *Search; if (GetViewById(IDC_SEARCH, Search)) Search->Focus(true); } } GTreeItem *FindItem(GTreeItem *i, const char *search) { char *s = i->GetText(0); if (s && search && stristr(s, search)) return i; for (GTreeItem *c = i->GetChild(); c; c = c->GetNext()) { GTreeItem *f = FindItem(c, search); if (f) return f; } return NULL; } void OnSearch(const char *s) { if (!Tree) return; GTreeItem *f = NULL; for (GTreeItem *i = Tree->GetChild(); i && !f; i = i->GetNext()) { f = FindItem(i, s); } if (f) { f->Select(true); for (GTreeItem *i = f; i; i = i->GetParent()) i->Expanded(true); Tree->Focus(true); } } int OnNotify(GViewI *Ctrl, int Flags) { switch (Ctrl->GetId()) { case IDC_BROWSE: { const char *s = GetCtrlName(IDC_PATH); if (s) LgiBrowseToFile(s); break; } case IDC_SEARCH: { if (Flags == VK_RETURN) { DefLockOut = LgiCurrentTime(); OnSearch(GetCtrlName(IDC_SEARCH)); } else { SetCtrlEnabled(IDC_CLEAR_SEARCH, ValidStr(Ctrl->Name())); } break; } case IDC_CLEAR_SEARCH: { SetCtrlName(IDC_SEARCH, NULL); OnSearch(NULL); break; } case IDOK: { if (LgiCurrentTime() - DefLockOut < 500) break; Detail->SetSetting(NULL, 0); EndModal(1); break; } case IDCANCEL: { if (LgiCurrentTime() - DefLockOut < 500) break; EndModal(0); break; } default: { int Id = Ctrl->GetId(); if (Id >= IDC_BROWSE_FILE && Id < IDC_BROWSE_FILE+100) { int i = Id - IDC_BROWSE_FILE; GEdit *e; if (GetViewById(IDC_EDIT_BASE + i, e)) { GFileSelect s; s.Parent(this); GFile::Path Path(d->Project->GetBasePath()); GFile::Path Cur(e->Name()); Path.Join(Cur.GetFull()); Path--; if (Path.Exists()) s.InitialDir(Path); if (s.Open()) { const char *Base = GetCtrlName(IDC_PATH); GAutoString Rel; if (Base) { GFile::Path p = Base; Rel = LgiMakeRelativePath(--p, s.Name()); } e->Name(Rel ? Rel : s.Name()); } } // else LgiTrace("%s:%i - Failed to find editbox: %i\n", _FL, i); } break; } } return GDialog::OnNotify(Ctrl, Flags); } void OnSelect(SettingItem *si) { Detail->SetSetting(si->Setting, si->Flags); } }; void SettingItem::Select(bool b) { GTreeItem::Select(b); if (b) Dlg->OnSelect(this); } IdeProjectSettings::IdeProjectSettings(IdeProject *Proj) { d = new IdeProjectSettingsPriv(this); d->Project = Proj; InitAllSettings(); } IdeProjectSettings::~IdeProjectSettings() { DeleteObj(d); } const char *IdeProjectSettings::GetCurrentConfig() { return d->CurConfig; } bool IdeProjectSettings::SetCurrentConfig(const char *Config) { int i = d->FindConfig(Config); if (i < 0) return false; d->CurConfig.Reset(NewStr(d->Configs[i])); return true; } bool IdeProjectSettings::AddConfig(const char *Config) { int i = d->FindConfig(Config); if (i >= 0) return true; d->Configs.Add(NewStr(Config)); return true; } bool IdeProjectSettings::DeleteConfig(const char *Config) { int i = d->FindConfig(Config); if (i < 0) return false; delete [] d->Configs[i]; d->Configs.DeleteAt(i); return true; } void IdeProjectSettings::InitAllSettings(bool ClearCurrent) { char *p; for (SettingInfo *i = AllSettings; i->Setting; i++) { GVariant Default; GXmlTag *t = NULL; switch (i->Setting) { default: break; case ProjMakefile: { char s[64]; sprintf_s(s, sizeof(s), "Makefile.%s", LgiGetOsName()); strlwr(s + 1); Default = s; break; } case ProjTargetType: { Default = "Executable"; break; } /* This won't work in the constructor because the IdeProject isn't initialized yet. case ProjTargetName: { if (d->Project) { char s[256]; char *Fn = d->Project->GetFileName(); char *Dir = Fn ? strrchr(Fn, DIR_CHAR) : NULL; if (Dir) { strsafecpy(s, ++Dir, sizeof(s)); char *Dot = strrchr(s, '.'); #ifdef WIN32 strcpy(Dot, ".exe"); #else if (Dot) *Dot = 0; #endif Default = s; } } break; } */ case ProjEditorTabSize: { Default = 4; break; } case ProjEditorIndentSize: { Default = 4; break; } case ProjEditorShowWhiteSpace: { Default = false; break; } case ProjEditorUseHardTabs: { Default = true; break; } } if (i->Flag.ConfigSpecific) { for (int Cfg = 0; Cfg < d->Configs.Length(); Cfg++) { if (!i->Flag.PlatformSpecific) { p = d->BuildPath(i->Setting, 0, PlatformCurrent, Cfg); d->Active.GetChildTag(p, true); if (t && !t->GetContent() && Default.Type != GV_NULL) t->SetContent(Default.Str()); } if (!i->Flag.CrossPlatform) { p = d->BuildPath(i->Setting, SF_PLATFORM_SPECIFC, PlatformCurrent, Cfg); d->Active.GetChildTag(p, true); if (t && !t->GetContent() && Default.Type != GV_NULL) t->SetContent(Default.Str()); } } } else { if (!i->Flag.PlatformSpecific) { p = d->BuildPath(i->Setting, 0, PlatformCurrent, -1); t = d->Active.GetChildTag(p, true); if (t && !t->GetContent() && Default.Type != GV_NULL) t->SetContent(Default.Str()); } if (!i->Flag.CrossPlatform) { p = d->BuildPath(i->Setting, SF_PLATFORM_SPECIFC, PlatformCurrent, -1); t = d->Active.GetChildTag(p, true); if (t && !t->GetContent() && Default.Type != GV_NULL) t->SetContent(Default.Str()); } } } } bool IdeProjectSettings::Edit(GViewI *parent) { // Copy all the settings to the edit tag... d->Editing.Copy(d->Active, true); // Show the dialog... ProjectSettingsDlg Dlg(parent, d); bool Changed = Dlg.DoModal(); if (Changed) { // User elected to save settings... so copy all the values // back over to the active settings... d->Active.Copy(d->Editing, true); d->Editing.Empty(true); } return Changed; } bool IdeProjectSettings::Serialize(GXmlTag *Parent, bool Write) { if (!Parent) { LgiAssert(!"No parent tag?"); return false; } GXmlTag *t = Parent->GetChildTag(TagSettings, true); if (!t) { LgiAssert(!"Can't find settings tags?"); return false; } if (Write) { // d->Active -> Parent ClearEmptyTags(&d->Active); return t->Copy(d->Active, true); } else { // Parent -> d->Active bool Status = d->Active.Copy(*t, true); InitAllSettings(); return Status; } return false; } const char *IdeProjectSettings::GetStr(ProjSetting Setting, const char *Default, IdePlatform Platform) { SettingInfo *s = d->Map.Find(Setting); LgiAssert(s); GArray Strs; int Bytes = 0; if (!s->Flag.PlatformSpecific) { GXmlTag *t = d->Active.GetChildTag(d->BuildPath(Setting, 0, Platform)); if (t && t->GetContent()) { Strs.Add(t->GetContent()); Bytes += strlen(t->GetContent()) + 1; } } if (!s->Flag.CrossPlatform) { GXmlTag *t = d->Active.GetChildTag(d->BuildPath(Setting, SF_PLATFORM_SPECIFC, Platform)); if (t && t->GetContent()) { Strs.Add(t->GetContent()); Bytes += strlen(t->GetContent()) + 1; } } if (Strs.Length() == 0) return Default; if (Strs.Length() == 1) return Strs[0]; if (!d->StrBuf.Reset(new char[Bytes])) return Default; // char *c = d->StrBuf; int ch = 0; for (int i=0; iStrBuf + ch, Bytes - ch, "%s%s", Strs[i], iStrBuf; } int IdeProjectSettings::GetInt(ProjSetting Setting, int Default, IdePlatform Platform) { int Status = Default; SettingInfo *s = d->Map.Find(Setting); LgiAssert(s); if (!s->Flag.PlatformSpecific) { GXmlTag *t = d->Active.GetChildTag(d->BuildPath(Setting, 0, Platform)); if (t) { Status = t->GetContent() ? atoi(t->GetContent()) : 0; } } else if (!s->Flag.CrossPlatform) { GXmlTag *t = d->Active.GetChildTag(d->BuildPath(Setting, SF_PLATFORM_SPECIFC, Platform)); if (t) { Status = t->GetContent() ? atoi(t->GetContent()) : 0; } } else LgiAssert(0); return Status; } bool IdeProjectSettings::Set(ProjSetting Setting, const char *Value, IdePlatform Platform) { bool Status = false; char *path = d->BuildPath(Setting, true, Platform); GXmlTag *t = d->Active.GetChildTag(path, true); if (t) { t->SetContent(Value); } else LgiTrace("%s:%i - Warning: missing setting tag '%s'\n", _FL, path); return Status; } bool IdeProjectSettings::Set(ProjSetting Setting, int Value, IdePlatform Platform) { bool Status = false; char *path = d->BuildPath(Setting, true, Platform); GXmlTag *t = d->Active.GetChildTag(path, true); if (t) { t->SetContent(Value); } else LgiTrace("%s:%i - Warning: missing setting tag '%s'\n", _FL, path); return Status; } diff --git a/Ide/Code/SysCharSupport.cpp b/Ide/Code/SysCharSupport.cpp --- a/Ide/Code/SysCharSupport.cpp +++ b/Ide/Code/SysCharSupport.cpp @@ -1,162 +1,162 @@ #include #include "Lgi.h" #include "LgiIde.h" #include "LList.h" #include "GDisplayString.h" class CharItem : public LListItem { GSurface *pDC; public: CharItem(GSurface *pdc) { pDC = pdc; } ~CharItem() { DeleteObj(pDC); } void OnPaintColumn(GItem::ItemPaintCtx &Ctx, int i, GItemColumn *c) { LListItem::OnPaintColumn(Ctx, i, c); if (i == 1) { Ctx.pDC->Blt(Ctx.x1, Ctx.y1, pDC); } } void OnMeasure(GdcPt2 *Info) { LListItem::OnMeasure(Info); if (pDC) Info->y = MAX(Info->y, pDC->Y()); } }; class SysCharSupportPriv : public GLgiRes { public: LList *Match, *NonMatch; SysCharSupportPriv() { Match = NonMatch = 0; } void Search(char *ch) { if (ch && Match && NonMatch) { Match->Empty(); NonMatch->Empty(); GUtf8Str Utf(ch); - uint32 c = Utf; + uint32_t c = Utf; char msg[256]; sprintf(msg, "%i 0x%x", c, c); Match->GetWindow()->SetCtrlName(IDC_VALUE, msg); GFontSystem *s = GFontSystem::Inst(); if (s) { GString::Array Fonts; if (s->EnumerateFonts(Fonts)) { for (auto &f : Fonts) { GFont *Fnt = new GFont; if (Fnt) { Fnt->SubGlyphs(false); if (Fnt->Create(f, "16pt")) { LList *m = _HasUnicodeGlyph(Fnt->GetGlyphMap(), c) ? Match : NonMatch; GMemDC *pDC = new GMemDC; if (pDC) { char16 Str[] = { (char16)c, 0 }; GDisplayString d(Fnt, Str); if (pDC->Create(d.X(), d.Y(), System32BitColourSpace)) { pDC->Colour(LC_WORKSPACE, 24); pDC->Colour(LC_TEXT, 24); d.Draw(pDC, 0, 0); } } LListItem *i; m->Insert(i = new CharItem(pDC)); if (i) { i->SetText(f); } } DeleteObj(Fnt); } } } } } } }; SysCharSupport::SysCharSupport(AppWnd *app) { d = new SysCharSupportPriv; Name("System Character Support"); if (Attach(0)) { d->LoadFromResource(IDD_CHAR, this, &GetPos()); MoveToCenter(); AttachChildren(); GetViewById(IDC_MATCH, d->Match); GetViewById(IDC_NOT_MATCH, d->NonMatch); OnPosChange(); Visible(true); } } SysCharSupport::~SysCharSupport() { DeleteObj(d); } void SysCharSupport::OnPosChange() { /* if (d->Match) { GRect c = GetClient(); GRect r = d->Lst->GetPos(); r.x2 = c.x2 - r.x1; r.y2 = c.y2 - r.x1; d->Lst->SetPos(r); } */ } int SysCharSupport::OnNotify(GViewI *v, int f) { switch (v->GetId()) { case IDOK: { d->Search(GetCtrlName(IDC_CHAR)); break; } } return 0; } diff --git a/Lgi.xml b/Lgi.xml --- a/Lgi.xml +++ b/Lgi.xml @@ -1,580 +1,582 @@ - - - - - - - - - + + + + + + + + + + .\Makefile.windows ./Makefile.linux Makefile.haiku LGI_LIBRARY HAS_FILE_CMD=1 LGI_LIBRARY HAS_FILE_CMD=1 WINNATIVE WINDOWS WINNATIVE WINDOWS POSIX POSIX MingW include/common include/common include/win32 /local/include include/win32 /local/include include/linux include/linux/Gtk include/linux include/linux/Gtk include/beos /boot/develop/headers/os/app include/beos /boot/develop/headers/os/app ws2_32 ComCtl32 uxtheme gdi32 comdlg32 ole32 uuid ws2_32 ComCtl32 uxtheme gdi32 comdlg32 ole32 uuid magic -static-libgcc `pkg-config --libs gtk+-2.0` magic -static-libgcc `pkg-config --libs gtk+-2.0` network be game textencoding network be game textencoding DynamicLibrary lgi lgi lgi lgi lgi lgi /usr/include/gstreamer-1.0 /usr/lib/x86_64-linux-gnu/gstreamer-1.0/include `pkg-config --cflags gtk+-2.0` /usr/include/gstreamer-1.0 /usr/lib/x86_64-linux-gnu/gstreamer-1.0/include `pkg-config --cflags gtk+-2.0` 4 4 0 1 + diff --git a/include/common/GArray.h b/include/common/GArray.h --- a/include/common/GArray.h +++ b/include/common/GArray.h @@ -1,809 +1,809 @@ /// \file /// \author Matthew Allen /// \brief Growable, type-safe array. #ifndef _GARRAY_H_ #define _GARRAY_H_ #include #include #define GARRAY_MIN_SIZE 16 #if defined(LGI_CHECK_MALLOC) && !defined(LGI_MEM_DEBUG) #error "Include GMem.h first" #endif #if defined(PLATFORM_MINGW) #ifdef __cplusplus extern "C" { #endif #ifndef _QSORT_S_DEFINED #define _QSORT_S_DEFINED _CRTIMP void __cdecl qsort_s(void *_Base, size_t _NumOfElements, size_t _SizeOfElements, int (__cdecl *_PtFuncCompare)(void *,const void *,const void *), void *_Context); #endif #ifdef __cplusplus } #endif #endif #include "GRange.h" /// \brief Growable type-safe array. /// \ingroup Base /// /// You can store simple objects inline in this array, but all their contents are initialized /// to the octet 0x00. Which limits use to objects that don't have a virtual table and /// don't need construction (constructors are not called). /// /// The objects are copied around during use so you can't assume their pointer /// will remain the same over time either. However when objects are deleted from the /// array their destructors WILL be called. This allows you to have simple objects that /// have dynamically allocated pointers that are freed properly. A good example of this /// type of object is the GVariant or GAutoString class. /// /// If you want to store objects with a virtual table, or that need their constructor /// to be called then you should create the GArray with pointers to the objects instead /// of inline objects. And to clean up the memory you can call GArray::DeleteObjects or /// GArray::DeleteArrays. template class GArray { Type *p; size_t len; size_t alloc; #ifdef _DEBUG public: int GetAlloc() { return alloc; } #endif protected: bool fixed; public: typedef Type ItemType; /// Constructor GArray(size_t PreAlloc = 0) { p = 0; alloc = len = PreAlloc; fixed = false; if (alloc) { size_t Bytes = sizeof(Type) * alloc; p = (Type*) malloc(Bytes); if (p) { memset(p, 0, Bytes); } else { alloc = len = 0; } } } GArray(const GArray &c) { p = 0; alloc = len = 0; fixed = false; *this = c; } /// Destructor ~GArray() { Length(0); } /// Does a range check on a pointer... /// \returns true if the pointer is pointing to a valid object /// in this array. bool PtrCheck(void *Ptr) { return p != NULL && Ptr >= p && Ptr < &p[len]; } /// Does a range check on an index... bool IdxCheck(ssize_t i) { return i >= 0 && i < len; } /// Returns the number of used entries size_t Length() const { return len; } /// Makes the length fixed.. void SetFixedLength(bool fix = true) { fixed = fix; } /// Emtpies the array of all objects. bool Empty() { return Length(0); } /// Sets the length of available entries bool Length(size_t i) { if (i > 0) { if (i > len && fixed) { assert(!"Attempt to enlarged fixed array."); return false; } size_t nalloc = alloc; if (i < len) { // Shrinking } else { // Expanding nalloc = 0x10; while (nalloc < i) nalloc <<= 1; assert(nalloc >= i); } if (nalloc != alloc) { Type *np = (Type*)malloc(sizeof(Type) * nalloc); if (!np) { return false; } memset(np + len, 0, (nalloc - len) * sizeof(Type)); if (p) { // copy across common elements memcpy(np, p, MIN(len, i) * sizeof(Type)); free(p); } p = np; alloc = nalloc; } if (i > len) { // zero new elements memset(p + len, 0, sizeof(Type) * (nalloc - len)); } else if (i < len) { for (size_t n=i; n &operator =(const GArray &a) { Length(a.Length()); if (p && a.p) { - for (uint32 i=0; i= 0 && (uint32)i < len) + if (i >= 0 && (uint32_t)i < len) return p[i]; static Type t; return t; } // Returns the address of an item or NULL if index is out of range Type *AddressOf(size_t i = 0) { return i < len ? p + i : NULL; } /// \brief Returns a reference a given entry. /// /// If the entry is off the end of the array and "fixed" is false, /// it will grow to make it valid. Type &operator [](size_t i) { static Type t; if ( - (fixed && (uint32)i >= len) + (fixed && (uint32_t)i >= len) ) { memset(&t, 0, sizeof(t)); - if (fixed && (uint32)i >= len) + if (fixed && (uint32_t)i >= len) { assert(!"Attempt to enlarged fixed array."); } return t; } if (i >= (int)alloc) { // increase array length size_t nalloc = MAX(alloc, GARRAY_MIN_SIZE); - while (nalloc <= (uint32)i) + while (nalloc <= (uint32_t)i) { nalloc <<= 1; } #if 0 if (nalloc > 1<<30) { #if defined(_DEBUG) && defined(_MSC_VER) LgiAssert(0); #endif ZeroObj(t); return t; } #endif // alloc new array Type *np = (Type*) malloc(sizeof(Type) * nalloc); if (np) { // clear new cells memset(np + len, 0, (nalloc - len) * sizeof(Type)); if (p) { // copy across old cells memcpy(np, p, len * sizeof(Type)); // clear old array free(p); } // new values p = np; alloc = nalloc; } else { static Type *t = 0; return *t; } } // adjust length of the the array - if ((uint32)i + 1 > len) + if ((uint32_t)i + 1 > len) { len = i + 1; } return p[i]; } /// Delete all the entries as if they are pointers to objects void DeleteObjects() { if (len > 0) { size_t InitialLen = len; delete p[0]; if (InitialLen == len) { // Non self deleting for (uint i=1; i= 0; } /// Removes last element bool PopLast() { if (len <= 0) return false; return Length(len - 1); } /// Deletes an entry bool DeleteAt ( /// The index of the entry to delete size_t Index, /// true if the order of the array matters, otherwise false. bool Ordered = false ) { if (p && Index < len) { // Delete the object p[Index].~Type(); // Move the memory up if (Index < len - 1) { if (Ordered) { memmove(p + Index, p + Index + 1, (len - Index - 1) * sizeof(Type) ); } else { p[Index] = p[len-1]; } } // Adjust length len--; // Kill the element at the end... otherwise New() returns non-zero data. memset(p + len, 0, sizeof(Type)); return true; } return false; } /// Deletes the entry 'n' bool Delete ( /// The value of the entry to delete Type n, /// true if the order of the array matters, otherwise false. bool Ordered = false ) { ssize_t i = IndexOf(n); if (p && i >= 0) { return DeleteAt(i, Ordered); } return false; } /// Appends an element void Add ( /// Item to insert const Type &n ) { (*this)[len] = n; } /// Appends multiple elements bool Add ( /// Items to insert Type *s, /// Length of array ssize_t count ) { if (!s || count < 1) return false; ssize_t i = len; if (!Length(len + count)) return false; Type *d = p + i; while (count--) { *d++ = *s++; } return true; } /// Appends an array of elements bool Add ( /// Array to insert GArray &a ) { ssize_t old = len; if (Length(len + a.Length())) { for (unsigned i=0; i &operator +(GArray &a) { Add(a); return *this; } /// Inserts an element into the array bool AddAt ( /// Item to insert before size_t Index, /// Item to insert Type n ) { // Make room if (Length(len + 1)) { - if (Index >= 0 && (uint32)Index < len - 1) + if (Index >= 0 && (uint32_t)Index < len - 1) { // Shift elements after insert point up one memmove(p + Index + 1, p + Index, (len - Index - 1) * sizeof(Type) ); } else { // Add at the end, not after the end... Index = len - 1; } // Insert item memset(p + Index, 0, sizeof(*p)); p[Index] = n; return true; } return false; } /// Sorts the array void Sort(int (*Compare)(Type*, Type*)) { typedef int (*qsort_compare)(const void *, const void *); qsort(p, len, sizeof(Type), (qsort_compare)Compare); } // Sorts the array with a comparison function (can I get a standard here?) #if defined(_MSC_VER) || defined(PLATFORM_MINGW) #define DeclGArrayCompare(func_name, type, user_type) \ int func_name(user_type *param, type *a, type *b) template void Sort(int (*Compare)(U *user_param, Type*, Type*), U *user_param) { typedef int (*qsort_s_compare)(void *, const void *, const void *); qsort_s(p, len, sizeof(Type), (qsort_s_compare)Compare, user_param); } #elif defined(MAC) #define DeclGArrayCompare(func_name, type, user_type) \ int func_name(user_type *param, type *a, type *b) template void Sort(int (*Compare)(U *user_param, Type*, Type*), U *user_param) { typedef int (*qsort_r_compare)(void *, const void *, const void *); qsort_r(p, len, sizeof(Type), user_param, (qsort_r_compare)Compare); } #elif !defined(BEOS) // POSIX? #define DeclGArrayCompare(func_name, type, user_type) \ int func_name(type *a, type *b, user_type *param) template void Sort(int (*Compare)(Type*, Type*, T *user_param), T *user_param) { typedef int (*qsort_r_compare)(const void *, const void *, void *); qsort_r(p, len, sizeof(Type), (qsort_r_compare)Compare, user_param); } #endif /// \returns a reference to a new object on the end of the array /// /// Never assign this to an existing variable. e.g: /// GArray a; /// MyObject &o = a.New(); /// o.Type = something; /// o = a.New(); /// o.Type = something else; /// /// This causes the first object to be overwritten with a blank copy. Type &New() { return (*this)[len]; } /// Returns the memory held by the array and sets itself to empty Type *Release() { Type *Ptr = p; p = 0; len = alloc = 0; return Ptr; } void Swap(GArray &other) { LSwap(p, other.p); LSwap(len, other.len); LSwap(alloc, other.alloc); } /// Swaps a range of elements between this array and 'b' bool SwapRange ( // The range of 'this' to swap out GRange aRange, // The other array to swap with GArray &b, // The range of 'b' to swap with this array GRange bRange ) { GArray Tmp; // Store entries in this that will be swapped Tmp.Add(AddressOf(aRange.Start), aRange.Len); // Copy b's range into this ssize_t Common = MIN(bRange.Len, aRange.Len); ssize_t aIdx = aRange.Start; ssize_t bIdx = bRange.Start; for (int i=0; i bRange.Len) { // Shrink the range in this to fit 'b' ssize_t Del = aRange.Len - bRange.Len; for (ssize_t i=0; i bRange.Len) { // Grow range to fit this ssize_t Add = aRange.Len - bRange.Len; for (ssize_t i=0; i class Iter { ssize_t i; char each_dir; GArray *a; public: Iter(GArray *arr) // 'End' constructor { i = -1; a = arr; each_dir = 0; } Iter(GArray *arr, size_t pos) { i = pos; a = arr; each_dir = 0; } bool operator ==(const Iter &it) const { int x = (int)In() + (int)it.In(); if (x == 2) return (a == it.a) && (i == it.i); return x == 0; } bool operator !=(const Iter &it) const { return !(*this == it); } operator bool() const { return In(); } bool In() const { return i >= 0 && i < (ssize_t)a->Length(); } bool End() const { return i < 0 || i >= a->Length(); } T &operator *() { return (*a)[i]; } Iter &operator ++() { i++; return *this; } Iter &operator --() { i--; return *this; } Iter &operator ++(int) { i++; return *this; } Iter &operator --(int) { i--; return *this; } }; typedef Iter I; I begin() { return I(this, 0); } I rbegin() { return I(this, len-1); } I end() { return I(this); } /* To use this iteration method in a for loop: for (T *Ptr = NULL; Array.Iterate(Ptr); ) { // Use 'Ptr' here } */ bool Iterate(Type *&Ptr) { if (!len || !p) return false; return Ptr ? PtrCheck(++Ptr) : (Ptr = p) != NULL; } /* To use this iteration method in a for loop: for (T *Ptr = NULL, size_t Idx; Array.Iterate(Idx, Ptr); ) { // Use 'Ptr' here } */ template bool IteratePtr(size_t &Idx, T &Ptr) { if (!len || !p) return false; if (!Ptr) { Ptr = dynamic_cast(*p); Idx = 0; return true; } Ptr = p + (++Idx); return PtrCheck(Ptr); } }; #endif diff --git a/include/common/GBox.h b/include/common/GBox.h --- a/include/common/GBox.h +++ b/include/common/GBox.h @@ -1,51 +1,51 @@ #ifndef _GBOX_H_ #define _GBOX_H_ #include "GCss.h" /// This is a vertical or horizontal layout box, similar to the /// old GSplitter control except it can handle any number of children class LgiClass GBox : public GView { struct GBoxPriv *d; protected: public: struct Spacer { GRect Pos; // Position on screen in view coords GColour Colour; // Colour of the spacer - uint32 SizePx; // Size in pixels + uint32_t SizePx; // Size in pixels }; GBox(int Id = -1, bool Vertical = false, const char *name = NULL); ~GBox(); const char *GetClass() { return "GBox"; } bool IsVertical(); void SetVertical(bool v); Spacer *GetSpacer(int i); GViewI *GetViewAt(int i); - bool SetViewAt(uint32 i, GViewI *v); + bool SetViewAt(uint32_t i, GViewI *v); int64 Value(); void Value(int64 i); void OnCreate(); void OnPaint(GSurface *pDC); void OnPosChange(); void OnMouseClick(GMouse &m); bool OnViewMouse(GView *v, GMouse &m); void OnMouseMove(GMouse &m); void OnChildrenChanged(GViewI *Wnd, bool Attaching); GMessage::Result OnEvent(GMessage *Msg); bool Pour(GRegion &r); LgiCursor GetCursor(int x, int y); bool OnLayout(GViewLayoutInfo &Inf); bool Serialize(GDom *Dom, const char *OptName, bool Write); bool SetSize(int ViewIndex, GCss::Len Size); }; #endif \ No newline at end of file diff --git a/include/common/GClipBoard.h b/include/common/GClipBoard.h --- a/include/common/GClipBoard.h +++ b/include/common/GClipBoard.h @@ -1,60 +1,60 @@ /// \file #ifndef _GCLIPBOARD_H #define _GCLIPBOARD_H /// Clipboard API class LgiClass GClipBoard { class GClipBoardPriv *d; GView *Owner; bool Open; GSurface *pDC; GAutoString Txt; GAutoWString wTxt; public: /// On windows, this equates to a CF_TEXT, CF_BITMAP, CF_DIB type #define - typedef uint32 FormatType; + typedef uint32_t FormatType; static GString FmtToStr(FormatType Fmt); static FormatType StrToFmt(GString Fmt); /// Creates the clipboard access object. GClipBoard(GView *o); ~GClipBoard(); bool IsOpen() { return Open; } GClipBoard &operator =(GClipBoard &c); /// Empties the clipboard of it's current content. bool Empty(); bool EnumFormats(GArray &Formats); // Text bool Text(char *Str, bool AutoEmpty = true); char *Text(); // ptr returned is still owned by this object bool TextW(char16 *Str, bool AutoEmpty = true); char16 *TextW(); // ptr returned is still owned by this object // HTML bool Html(const char *doc, bool AutoEmpty = true); GString Html(); // Bitmap bool Bitmap(GSurface *pDC, bool AutoEmpty = true); GSurface *Bitmap(); #if WINNATIVE GSurface *ConvertFromPtr(void *Ptr); #endif // Files GString::Array Files(); bool Files(GString::Array &Paths, bool AutoEmpty = true); // Binary - bool Binary(FormatType Format, uint8 *Ptr, ssize_t Len, bool AutoEmpty); // Set - bool Binary(FormatType Format, GAutoPtr &Ptr, ssize_t *Len); // Get + bool Binary(FormatType Format, uint8_t *Ptr, ssize_t Len, bool AutoEmpty); // Set + bool Binary(FormatType Format, GAutoPtr &Ptr, ssize_t *Len); // Get }; #endif diff --git a/include/common/GColour.h b/include/common/GColour.h --- a/include/common/GColour.h +++ b/include/common/GColour.h @@ -1,124 +1,124 @@ /// \file /// \author Matthew Allen /// \created 3/2/2011 #ifndef _GCOLOUR_H_ #define _GCOLOUR_H_ #include "GColourSpace.h" /// A colour definition class LgiClass GColour { protected: class GPalette *pal; union { - uint32 flat; - uint8 index; + uint32_t flat; + uint8_t index; System32BitPixel rgb; GHls32 hls; }; GColourSpace space; int HlsValue(double fN1, double fN2, double fHue) const; public: static const GColour Black; static const GColour White; static const GColour Red; static const GColour Green; static const GColour Blue; /// Transparent GColour(); /// Indexed colour - GColour(uint8 idx8, GPalette *palette); + GColour(uint8_t idx8, GPalette *palette); /// True colour GColour(int r, int g, int b, int a = 255); /// Conversion from COLOUR - GColour(uint32 c, int bits, GPalette *palette = NULL); + GColour(uint32_t c, int bits, GPalette *palette = NULL); GColourSpace GetColourSpace(); bool operator ==(const GColour &c) { return c32() == c.c32(); } bool operator !=(const GColour &c) { return c32() != c.c32(); } bool IsValid(); void Empty(); bool IsTransparent(); /// Sets the colour to a rgb(a) value void Rgb(int r, int g, int b, int a = 255); /// Sets the colour - void Set(uint32 c, int bits, GPalette *palette = NULL); - uint32 Get(int bits); + void Set(uint32_t c, int bits, GPalette *palette = NULL); + uint32_t Get(int bits); /// Gets the red component (0-255) - uint8 r() const; + uint8_t r() const; /// Gets the green component (0-255) - uint8 g() const; + uint8_t g() const; /// Gets the blue component (0-255) - uint8 b() const; + uint8_t b() const; /// Gets the alpha component (0-255) - uint8 a() const; + uint8_t a() const; // Gets the indexed colour - uint8 c8() const; + uint8_t c8() const; // Sets indexed colour - void c8(uint8 c, GPalette *p); + void c8(uint8_t c, GPalette *p); // Get as 24 bit colour - uint32 c24() const; + uint32_t c24() const; /// Set 24 bit colour - void c24(uint32 c); + void c24(uint32_t c); /// Get 32 bit colour - uint32 c32() const; + uint32_t c32() const; /// Set 32 bit colour - void c32(uint32 c); + void c32(uint32_t c); /// Mixes 'Tint' with the current colour and returns it /// without modifying the object. GColour Mix(GColour Tint, float RatioOfTint = 0.5) const; // Hue Lum Sat methods /// Returns the hue value (0-359) - uint32 GetH(); + uint32_t GetH(); /// Returns whether the hue is value or not bool HueIsUndefined(); /// Returns the luminance - uint32 GetL(); + uint32_t GetL(); /// Returns the saturation - uint32 GetS(); + uint32_t GetS(); /// Converts the colour space to HLS bool ToHLS(); /// Sets the colour to a HLS value void SetHLS ( /// (0-359) - uint16 h, + uint16_t h, /// (0-255) - uint8 l, + uint8_t l, /// (0-255) - uint8 s + uint8_t s ); // Convert HLS to RGB void ToRGB(); int GetGray(int BitDepth = 8); - uint32 GetNative(); + uint32_t GetNative(); // String IO char *GetStr(); bool SetStr(const char *Str); #ifdef BEOS operator rgb_color() { rgb_color c; c.red = r(); c.green = g(); c.blue = b(); return c; } #endif }; #endif diff --git a/include/common/GColourSpace.h b/include/common/GColourSpace.h --- a/include/common/GColourSpace.h +++ b/include/common/GColourSpace.h @@ -1,586 +1,586 @@ #ifndef _G_COLOUR_SPACE_H_ #define _G_COLOUR_SPACE_H_ /// Colour component type enum GComponentType { CtNone, // 0 CtIndex, // 1 CtRed, // 2 CtGreen, // 3 CtBlue, // 4 CtAlpha, // 5 CtPad, // 6 CtHue, // 7 CtSaturation, // 8 CtLuminance, // 9 CtCyan, // 10 CtMagenta, // 11 CtYellow, // 12 CtBlack // 13 }; // Component construction: 4bits type, 4bits size. 8 bits per component. #define GDC_COLOUR_SPACE_1(type, size) \ ((type << 4) | (size)) #define GDC_COLOUR_SPACE_3(t1, s1, t2, s2, t3, s3) \ ( \ ((t1 << 20) | (s1 << 16)) | \ ((t2 << 12) | (s2 << 8)) | \ ((t3 << 4) | (s3 << 0)) \ ) #define GDC_COLOUR_SPACE_4(t1, s1, t2, s2, t3, s3, t4, s4) \ ( \ ((t1 << 28) | (s1 << 24)) | \ ((t2 << 20) | (s2 << 16)) | \ ((t3 << 12) | (s3 << 8)) | \ ((t4 << 4) | (s4 << 0)) \ ) /// Defines a specific colour space enum GColourSpace { CsNone = 0, - // uint8 types + // uint8_t types CsIndex1 = GDC_COLOUR_SPACE_1(CtIndex, 1), // Monochrome CsIndex4 = GDC_COLOUR_SPACE_1(CtIndex, 4), // 16 colour CsIndex8 = GDC_COLOUR_SPACE_1(CtIndex, 8), // 256 colour CsAlpha8 = GDC_COLOUR_SPACE_1(CtAlpha, 8), // Alpha channel only // uint16 types CsArgb15 = GDC_COLOUR_SPACE_4(CtAlpha, 1, CtRed, 5, CtGreen, 5, CtBlue, 5), CsRgb15 = GDC_COLOUR_SPACE_3(CtRed, 5, CtGreen, 5, CtBlue, 5), CsAbgr15 = GDC_COLOUR_SPACE_4(CtAlpha, 1, CtBlue, 5, CtGreen, 5, CtRed, 5), CsBgr15 = GDC_COLOUR_SPACE_3(CtBlue, 5, CtGreen, 5, CtRed, 5), CsRgb16 = GDC_COLOUR_SPACE_3(CtRed, 5, CtGreen, 6, CtBlue, 5), CsBgr16 = GDC_COLOUR_SPACE_3(CtBlue, 5, CtGreen, 6, CtRed, 5), // 24 bit types CsRgb24 = GDC_COLOUR_SPACE_3(CtRed, 8, CtGreen, 8, CtBlue, 8), CsBgr24 = GDC_COLOUR_SPACE_3(CtBlue, 8, CtGreen, 8, CtRed, 8), // 24 bits with padding CsRgbx32 = GDC_COLOUR_SPACE_4(CtRed, 8, CtGreen, 8, CtBlue, 8, CtPad, 8), CsBgrx32 = GDC_COLOUR_SPACE_4(CtBlue, 8, CtGreen, 8, CtRed, 8, CtPad, 8), CsXrgb32 = GDC_COLOUR_SPACE_4(CtPad, 8, CtRed, 8, CtGreen, 8, CtBlue, 8), CsXbgr32 = GDC_COLOUR_SPACE_4(CtPad, 8, CtBlue, 8, CtGreen, 8, CtRed, 8), // 32 bit types CsRgba32 = GDC_COLOUR_SPACE_4(CtRed, 8, CtGreen, 8, CtBlue, 8, CtAlpha, 8), CsBgra32 = GDC_COLOUR_SPACE_4(CtBlue, 8, CtGreen, 8, CtRed, 8, CtAlpha, 8), CsArgb32 = GDC_COLOUR_SPACE_4(CtAlpha, 8, CtRed, 8, CtGreen, 8, CtBlue, 8), CsAbgr32 = GDC_COLOUR_SPACE_4(CtAlpha, 8, CtBlue, 8, CtGreen, 8, CtRed, 8), // 16 bit component depth (size==0 means 16 bit) CsBgr48 = GDC_COLOUR_SPACE_3(CtBlue, 0, CtGreen, 0, CtRed, 0), CsRgb48 = GDC_COLOUR_SPACE_3(CtRed, 0, CtGreen, 0, CtBlue, 0), CsBgra64 = GDC_COLOUR_SPACE_4(CtBlue, 0, CtGreen, 0, CtRed, 0, CtAlpha, 0), CsRgba64 = GDC_COLOUR_SPACE_4(CtRed, 0, CtGreen, 0, CtBlue, 0, CtAlpha, 0), CsAbgr64 = GDC_COLOUR_SPACE_4(CtAlpha, 0, CtBlue, 0, CtGreen, 0, CtRed, 0), CsArgb64 = GDC_COLOUR_SPACE_4(CtAlpha, 0, CtRed, 0, CtGreen, 0, CtBlue, 0), // other colour spaces CsHls32 = GDC_COLOUR_SPACE_4(CtHue, 0 /*16*/, CtRed, 8, CtGreen, 8, CtBlue, 8), CsCmyk32 = GDC_COLOUR_SPACE_4(CtCyan, 8, CtMagenta, 8, CtYellow, 8, CtBlack, 8), }; #ifdef WIN32 #pragma pack(push, before_pack) #pragma pack(1) #endif #define LEAST_SIG_BIT_FIRST 1 #define LEAST_SIG_BYTE_FIRST 1 struct GRgb15 { #if LEAST_SIG_BIT_FIRST - uint16 b : 5; - uint16 g : 5; - uint16 r : 5; - uint16 pad : 1; + uint16_t b : 5; + uint16_t g : 5; + uint16_t r : 5; + uint16_t pad : 1; #else uint16 pad : 1; uint16 r : 5; uint16 g : 5; uint16 b : 5; #endif }; struct GArgb15 { #if LEAST_SIG_BIT_FIRST - uint16 b : 5; - uint16 g : 5; - uint16 r : 5; - uint16 a : 1; + uint16_t b : 5; + uint16_t g : 5; + uint16_t r : 5; + uint16_t a : 1; #else uint16 a : 1; uint16 r : 5; uint16 g : 5; uint16 b : 5; #endif }; struct GBgr15 { #if LEAST_SIG_BIT_FIRST - uint16 r : 5; - uint16 g : 5; - uint16 b : 5; - uint16 pad : 1; + uint16_t r : 5; + uint16_t g : 5; + uint16_t b : 5; + uint16_t pad : 1; #else uint16 pad : 1; uint16 b : 5; uint16 g : 5; uint16 r : 5; #endif }; struct GAbgr15 { #if LEAST_SIG_BIT_FIRST - uint16 r : 5; - uint16 g : 5; - uint16 b : 5; - uint16 a : 1; + uint16_t r : 5; + uint16_t g : 5; + uint16_t b : 5; + uint16_t a : 1; #else uint16 a : 1; uint16 b : 5; uint16 g : 5; uint16 r : 5; #endif }; struct GRgb16 { #if LEAST_SIG_BIT_FIRST - uint16 b : 5; - uint16 g : 6; - uint16 r : 5; + uint16_t b : 5; + uint16_t g : 6; + uint16_t r : 5; #else uint16 r : 5; uint16 g : 6; uint16 b : 5; #endif }; struct GBgr16 { #if LEAST_SIG_BIT_FIRST - uint16 r : 5; - uint16 g : 6; - uint16 b : 5; + uint16_t r : 5; + uint16_t g : 6; + uint16_t b : 5; #else uint16 b : 5; uint16 g : 6; uint16 r : 5; #endif }; struct GRgb24 { #if LEAST_SIG_BYTE_FIRST - uint8 r, g, b; + uint8_t r, g, b; #else - uint8 b, g, r; + uint8_t b, g, r; #endif }; struct GBgr24 { #if LEAST_SIG_BYTE_FIRST - uint8 b, g, r; + uint8_t b, g, r; #else - uint8 r, g, b; + uint8_t r, g, b; #endif }; struct GRgba32 { #if LEAST_SIG_BYTE_FIRST - uint8 r, g, b, a; + uint8_t r, g, b, a; #else - uint8 a, b, g, r; + uint8_t a, b, g, r; #endif }; struct GBgra32 { #if LEAST_SIG_BYTE_FIRST - uint8 b, g, r, a; + uint8_t b, g, r, a; #else - uint8 a, r, g, b; + uint8_t a, r, g, b; #endif }; struct GArgb32 { #if LEAST_SIG_BYTE_FIRST - uint8 a, r, g, b; + uint8_t a, r, g, b; #else - uint8 b, g, r, a; + uint8_t b, g, r, a; #endif }; struct GAbgr32 { #if LEAST_SIG_BYTE_FIRST - uint8 a, b, g, r; + uint8_t a, b, g, r; #else - uint8 r, g, b, a; + uint8_t r, g, b, a; #endif }; struct GXrgb32 { #if LEAST_SIG_BYTE_FIRST - uint8 pad, r, g, b; + uint8_t pad, r, g, b; #else - uint8 b, g, r, pad; + uint8_t b, g, r, pad; #endif }; struct GRgbx32 { #if LEAST_SIG_BYTE_FIRST - uint8 r, g, b, pad; + uint8_t r, g, b, pad; #else - uint8 pad, b, g, r; + uint8_t pad, b, g, r; #endif }; struct GXbgr32 { #if LEAST_SIG_BYTE_FIRST - uint8 pad, b, g, r; + uint8_t pad, b, g, r; #else - uint8 r, g, b, pad; + uint8_t r, g, b, pad; #endif }; struct GBgrx32 { #if LEAST_SIG_BYTE_FIRST - uint8 b, g, r, pad; + uint8_t b, g, r, pad; #else - uint8 pad, r, g, b; + uint8_t pad, r, g, b; #endif }; #ifdef WIN32 #pragma pack(push, 2) #endif struct GRgb48 { #if LEAST_SIG_BYTE_FIRST - uint16 r, g, b; + uint16_t r, g, b; #else uint16 b, g, r; #endif }; struct GBgr48 { #if LEAST_SIG_BYTE_FIRST - uint16 b, g, r; + uint16_t b, g, r; #else uint16 r, g, b; #endif }; struct GRgba64 { #if LEAST_SIG_BYTE_FIRST - uint16 r, g, b, a; + uint16_t r, g, b, a; #else uint16 a, b, g, r; #endif }; struct GBgra64 { #if LEAST_SIG_BYTE_FIRST - uint16 b, g, r, a; + uint16_t b, g, r, a; #else uint16 a, r, g, b; #endif }; struct GArgb64 { #if LEAST_SIG_BYTE_FIRST - uint16 a, r, g, b; + uint16_t a, r, g, b; #else uint16 b, g, r, a; #endif }; struct GAbgr64 { #if LEAST_SIG_BYTE_FIRST - uint16 a, b, g, r; + uint16_t a, b, g, r; #else uint16 r, g, b, a; #endif }; #ifdef WIN32 #pragma pack(pop) #endif struct GHls32 { - uint16 h; - uint8 l, s; + uint16_t h; + uint8_t l, s; }; struct GCmyk32 { #if LEAST_SIG_BYTE_FIRST - uint8 c, m, y, k; + uint8_t c, m, y, k; #else - uint8 k, y, m, c; + uint8_t k, y, m, c; #endif }; struct GColourComponent { // ((type << 4) | (size)) - uint8 Bits; + uint8_t Bits; GComponentType Type() { return (GComponentType) ((Bits >> 4) & 0xf); } - void Type(GComponentType t) { Bits = (Bits & 0xf) | ((uint8)t << 4); } - uint8 Size() { return Bits & 0xf; } - void Size(uint8 t) { Bits = (Bits & 0xf0) | (t & 0xf); } + void Type(GComponentType t) { Bits = (Bits & 0xf) | ((uint8_t)t << 4); } + uint8_t Size() { return Bits & 0xf; } + void Size(uint8_t t) { Bits = (Bits & 0xf0) | (t & 0xf); } }; union GColourSpaceBits { - uint32 All; + uint32_t All; GColourComponent Bits[4]; GColourComponent &operator[](int i) { static int Reverse = -1; if (Reverse < 0) { GColourSpaceBits t; t.All = 0; t.Bits[0].Type(CtIndex); if (t.All == 0x10) Reverse = 0; else if (t.All == (0x10<<24)) Reverse = 1; else LgiAssert(0); } if (Reverse == 1) return Bits[3-i]; return Bits[i]; } }; #ifdef WIN32 #pragma pack(pop, before_pack) #endif /// Converts a colour space into a string for debugging/reporting. LgiFunc const char *GColourSpaceToString(GColourSpace cs); /// Works out how many bits required for a pixel in a particular colour space. LgiFunc int GColourSpaceToBits(GColourSpace ColourSpace); /// /returns the number of channels in the colour space LgiFunc int GColourSpaceChannels(GColourSpace Cs); /// /returns true if the colour space has an alpha channel LgiFunc bool GColourSpaceHasAlpha(GColourSpace Cs); /// Converts a bit-depth into the default colour space for that depth. Used mostly /// in interfacing old bit-depth based code to newer colour space code. LgiFunc GColourSpace GBitsToColourSpace(int Bits); /// Converts a string representation into a colour space. LgiFunc GColourSpace GStringToColourSpace(const char *c); /// Tests that the bit and byte ordering of the pixel structures are compiled correctly. /// \return true if #LEAST_SIG_BIT_FIRST and #LEAST_SIG_BYTE_FIRST are correct for this platform. LgiFunc bool GColourSpaceTest(); #ifdef __GTK_H__ /// Converts a GTK visual to a Lgi colour space. LgiFunc GColourSpace GdkVisualToColourSpace(Gtk::GdkVisual *v, int output_bits); #endif // These definitions are provide as a convenience when converting old code to the // GColourSpace system. However you should not use them for new code, as some systems // can have different colour spaces depending on OS version or hardware configuration. #if defined(WIN32) || defined(LINUX) || defined(BEOS) #define System15BitColourSpace CsBgr15 typedef GBgr15 System15BitPixel; #define System16BitColourSpace CsBgr16 typedef GBgr16 System16BitPixel; #define System24BitColourSpace CsBgr24 typedef GBgr24 System24BitPixel; #define System32BitColourSpace CsBgra32 typedef GBgra32 System32BitPixel; #else #define System15BitColourSpace CsRgb15 typedef GRgb15 System15BitPixel; #define System16BitColourSpace CsRgb16 typedef GRgb16 System16BitPixel; #define System24BitColourSpace CsRgb24 typedef GRgb24 System24BitPixel; #define System32BitColourSpace CsRgba32 typedef GRgba32 System32BitPixel; #endif typedef union { - uint8 *u8; - uint16 *u16; - uint32 *u32; + uint8_t *u8; + uint16_t *u16; + uint32_t *u32; int i; GRgb15 *rgb15; GBgr15 *bgr15; GRgb16 *rgb16; GBgr16 *bgr16; GRgb24 *rgb24; GBgr24 *bgr24; GRgba32 *rgba32; GArgb32 *argb32; GHls32 *hls32; GCmyk32 *cmyk32; System15BitPixel *s15; System16BitPixel *s16; System24BitPixel *s24; System32BitPixel *s32; } GPixelPtr; /** \brief 32bit colour of varing bit depth. If no associated depth is given, 32 bits is assumed. \code COLOUR typedef description 31 24 23 16 15 8 7 0 |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| Standard: 8bit mode: |------------------------ unused ---------------------| |---- index ----| 15bit mode: |----------------- unused ------------|T|-- red --|-- green ---|-- blue -| 16bit mode: |------------- unused -------------| |-- red --|--- green ----|-- blue -| 24bit mode: |---- unused ---| |----- blue ----| |---- green ----| |----- red -----| 32bit mode: |---- alpha ----| |----- blue ----| |---- green ----| |----- red -----| \endcode Note: In 15bit mode the T bit can toggle between 8-bit indexing and RGB, or it can also just be a 1 bit alpha channel. */ -typedef uint32 COLOUR; +typedef uint32_t COLOUR; /// Alpha value, 0 - transparent, 255 - solid -typedef uint8 ALPHA; +typedef uint8_t ALPHA; // colour conversion defines // RGB colour space #define BitWidth(bits, cropbits) ( (((bits)+(cropbits)-1)/(cropbits)) * 4 ) #define C24R 2 #define C24G 1 #define C24B 0 #define C32A 3 #define C32R 2 #define C32G 1 #define C32B 0 /// Create a 15bit COLOUR from components #define Rgb15(r, g, b) ( ((r&0xF8)<<7) | ((g&0xF8)<<2) | ((b&0xF8)>>3)) #define Rgb32To15(c32) ( ((c32&0xF8)>>3) | ((c32&0xF800)>>6) | ((c32&0xF80000)>>9) ) #define Rgb24To15(c24) ( (B24(c24)>>3) | ((G24(c24)<<2)&0x3E0) | ((R24(c24)<<7)&0x7C00) ) #define Rgb16To15(c16) ( ((c16&0xFFC0)>>1) | (c16&0x1F) ) /// Get the red component of a 15bit COLOUR #define R15(c15) ( (uchar) ((c15>>10)&0x1F) ) /// Get the green component of a 15bit COLOUR #define G15(c15) ( (uchar) ((c15>>5)&0x1F) ) /// Get the blue component of a 15bit COLOUR #define B15(c15) ( (uchar) ((c15)&0x1F) ) #define Rc15(c) ( (((c) & 0x7C00) >> 7) | (((c) & 0x7C00) >> 12) ) #define Gc15(c) ( (((c) & 0x03E0) >> 2) | (((c) & 0x03E0) >> 7) ) #define Bc15(c) ( (((c) & 0x001F) << 3) | (((c) & 0x001F) >> 2) ) /// Create a 16bit COLOUR from components #define Rgb16(r, g, b) ( ((r&0xF8)<<8) | ((g&0xFC)<<3) | ((b&0xF8)>>3)) #define Rgb32To16(c32) ( ((c32&0xF8)>>3) | ((c32&0xFC00)>>5) | ((c32&0xF80000)>>8) ) #define Rgb24To16(c24) ( (B24(c24)>>3) | ((G24(c24)<<3)&0x7E0) | ((R24(c24)<<8)&0xF800) ) #define Rgb15To16(c15) ( ((c15&0x7FE0)<<1) | (c15&0x1F) ) /// Get the red component of a 16bit COLOUR #define R16(c16) ( (uchar) ((c16>>11)&0x1F) ) /// Get the green component of a 16bit COLOUR #define G16(c16) ( (uchar) ((c16>>5)&0x3F) ) /// Get the blue component of a 16bit COLOUR #define B16(c16) ( (uchar) ((c16)&0x1F) ) #define Rc16(c) ( (((c) & 0xF800) >> 8) | (((c) & 0xF800) >> 13) ) #define Gc16(c) ( (((c) & 0x07E0) >> 3) | (((c) & 0x07E0) >> 9) ) #define Bc16(c) ( (((c) & 0x001F) << 3) | (((c) & 0x001F) >> 2) ) -#define G5bitTo8bit(c) (uint8) ( ((c) << 3) | ((c) >> 2) ) -#define G6bitTo8bit(c) (uint8) ( ((c) << 2) | ((c) >> 4) ) +#define G5bitTo8bit(c) (uint8_t) ( ((c) << 3) | ((c) >> 2) ) +#define G6bitTo8bit(c) (uint8_t) ( ((c) << 2) | ((c) >> 4) ) #define G8bitTo5bit(c) ( (c) >> 3 ) #define G8bitTo6bit(c) ( (c) >> 2 ) #define G8bitTo16bit(c) ( ((uint16)(c) << 8) | ((c) & 0xff) ) /// Get the red component of a 24bit COLOUR #define R24(c24) ( ((c24)>>(C24R*8)) & 0xff ) /// Get the green component of a 24bit COLOUR #define G24(c24) ( ((c24)>>(C24G*8)) & 0xff ) /// Get the blue component of a 24bit COLOUR #define B24(c24) ( ((c24)>>(C24B*8)) & 0xff ) /// Create a 24bit COLOUR from components #define Rgb24(r, g, b) ( (((r)&0xFF) << (C24R*8)) | (((g)&0xFF) << (C24G*8)) | (((b)&0xFF) << (C24B*8)) ) #define Rgb32To24(c32) Rgb24(R32(c32), G32(c32), B32(c32)) #define Rgb15To24(c15) ( ((c15&0x7C00)>>7) | ((c15&0x3E0)<<6) | ((c15&0x1F)<<19) ) #define Rgb16To24(c16) ( ((c16&0xF800)>>8) | ((c16&0x7E0)<<5) | ((c16&0x1F)<<19) ) // 32 // 31 24 23 16 15 8 7 0 // |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| // Windows |---- alpha ----| |----- red -----| |---- green ----| |----- blue ----| // Linux |----- blue ----| |---- green ----| |----- red -----| |---- alpha ----| /// Get the red component of a 32bit COLOUR #define R32(c32) ( (uchar) (c32 >> (C32R << 3)) ) /// Get the green component of a 32bit COLOUR #define G32(c32) ( (uchar) (c32 >> (C32G << 3)) ) /// Get the blue component of a 32bit COLOUR #define B32(c32) ( (uchar) (c32 >> (C32B << 3)) ) /// Get the opacity/alpha component of a 32bit COLOUR #define A32(c32) ( (uchar) (c32 >> (C32A << 3)) ) #define RgbPreMul(c, a) ( Div255Lut[(c)*a] ) -#define Rgb32(r, g, b) ( ((((uint32)r)&0xFF)<<(C32R<<3)) | (((g)&0xFF)<<(C32G<<3)) | (((b)&0xFF)<<(C32B<<3)) | (0xFF<<(C32A<<3)) ) +#define Rgb32(r, g, b) ( ((((uint32_t)r)&0xFF)<<(C32R<<3)) | (((g)&0xFF)<<(C32G<<3)) | (((b)&0xFF)<<(C32B<<3)) | (0xFF<<(C32A<<3)) ) #define Rgba32(r, g, b, a) ( ((((unsigned)r)&0xFF)<<(C32R<<3)) | (((g)&0xFF)<<(C32G<<3)) | (((b)&0xFF)<<(C32B<<3)) | (((a)&0xFF)<<(C32A<<3)) ) #define Rgbpa32(r, g, b, a) ( (RgbPreMul(r, a)<<(C32R<<3)) | (RgbPreMul(g, a)<<(C32G<<3)) | (RgbPreMul(b, a)<<(C32B<<3)) | ((a&0xFF)<<(C32A<<3)) ) #define Rgb24To32(c24) Rgba32( R24(c24), G24(c24), B24(c24), 255 ) #define Rgb15To32(c15) ( ((c15&0x7C00)<<9) | ((c15&0x3E0)<<6) | ((c15&0x1F)<<3) | (0xFF<<(C32A*8)) ) #define Rgb16To32(c16) ( ((c16&0xF800)<<8) | ((c16&0x7E0)<<5) | ((c16&0x1F)<<3) | (0xFF<<(C32A*8)) ) // HLS colour space tools /// Builds a complete 32bit HLS colour from it's components /// /// The hue (H) component varies from 0 to 359 /// The lightness (L) component varies from 0 (black), thru 128 (colour) to 255 (white) /// The saturation (S) component varies from 0 (grey) thru to 255 (full colour) /// /// \sa HlsToRgb(), RgbToHls() #define Hls32(h, l, s) ( ((h)<<16) | ((l)<<8) | (s) ) /// Extracts the hue component from the 32-bit HLS colour /// \sa #Hls32 #define H32(c) ( ((c)>>16)&0xFFFF ) /// Extracts the lightness component from the 32-bit HLS colour /// \sa #Hls32 #define L32(c) ( ((c)>>8)&0xFF ) /// Extracts the saturation component from the 32-bit HLS colour /// \sa #Hls32 #define S32(c) ( (c)&0xFF ) /// The value of an undefined hue /// \sa #Hls32 #define HUE_UNDEFINED 1024 #endif diff --git a/include/common/GCom.h b/include/common/GCom.h --- a/include/common/GCom.h +++ b/include/common/GCom.h @@ -1,385 +1,385 @@ #if !defined(_GCOM_H_) && defined(WIN32) #define _GCOM_H_ #ifdef __GNUC__ #include #define COM_NO_WINDOWS_H #endif #include #include #include #include "GContainers.h" #include "GVariant.h" template class GUnknownImpl : public T { int Count; class Interface { public: REFIID iid; void *pvObject; Interface(REFIID i, void *p) : iid(i) { pvObject = p; } }; List Interfaces; protected: bool TraceRefs; void AddInterface(REFIID iid, void *pvObject) { Interface *i = new Interface(iid, pvObject); if (i) { Interfaces.Insert(i); } } public: GUnknownImpl() { Count = 0; TraceRefs = false; } virtual ~GUnknownImpl() { Interfaces.DeleteObjects(); } // IUnknown HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject) { if (memcmp(&IID_IUnknown, &iid, sizeof(IID)) == 0) { *ppvObject = this; AddRef(); if (TraceRefs) LgiTrace("%s:%i - QueryInterface got IID_IUnknown\n", _FL); return S_OK; } for (Interface *i = Interfaces.First(); i; i=Interfaces.Next()) { if (memcmp(&i->iid, &iid, sizeof(IID)) == 0) { *ppvObject = i->pvObject; AddRef(); if (TraceRefs) LgiTrace("%s:%i - QueryInterface got custom IID\n", _FL); return S_OK; } } return E_NOINTERFACE; } ULONG STDMETHODCALLTYPE AddRef() { if (TraceRefs) LgiTrace("%s:%i - %p::AddRef %i\n", _FL, this, Count+1); return ++Count; } ULONG STDMETHODCALLTYPE Release() { int i = --Count; LgiAssert(i >= 0); if (TraceRefs) LgiTrace("%s:%i - %p::Release %i\n", _FL, this, Count); if (i <= 0) { delete this; } return i; } // Helpers BSTR VariantToBstr(VARIANT *v) { if (v) { if (v->vt == (VT_VARIANT | VT_BYREF)) v = v->pvarVal; if (v->vt == VT_BSTR) return v->bstrVal; if (v->vt == (VT_BSTR | VT_BYREF)) return *v->pbstrVal; } return 0; } }; template class GDispatchImpl : public T { public: HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT __RPC_FAR *pctinfo) { return S_OK; } HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo) { return S_OK; } HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid, LPOLESTR __RPC_FAR *rgszNames, UINT cNames, LCID lcid, DISPID __RPC_FAR *rgDispId) { return S_OK; } HRESULT STDMETHODCALLTYPE Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS __RPC_FAR *pDispParams, VARIANT __RPC_FAR *pVarResult, EXCEPINFO __RPC_FAR *pExcepInfo, UINT __RPC_FAR *puArgErr) { return S_OK; } }; /// This class wraps a IStream in an GStream interface... class IStreamWrap : public GStream { IStream *s; public: GAutoPtr Desc; IStreamWrap(IStream *str) { s = str; } ~IStreamWrap() { Close(); } int Open(const char *Str = 0, int Int = 0) { return IsOpen(); } bool IsOpen() { return s != NULL; } bool GetVariant(const char *Name, GVariant &Value, char *Array = NULL) override { if (!Desc) return false; GDomProperty p = LgiStringToDomProp(Name); switch (p) { case ObjName: Value.OwnStr(WideToUtf8(Desc->cFileName)); return true; case ObjLength: Value = ((int64) Desc->nFileSizeHigh << 32) | Desc->nFileSizeLow; return true; default: LgiAssert(!"Unknown field."); break; } return false; } bool SetVariant(const char *Name, GVariant &Value, char *Array = NULL) override { LgiAssert(!"Impl me."); return false; } int Close() override { if (s) { s->Release(); s = NULL; } return 0; } int64 GetSize() override { STATSTG sz; HRESULT res = s->Stat(&sz, STATFLAG_DEFAULT); if (SUCCEEDED(res)) return sz.cbSize.QuadPart; return -1; } int64 SetSize(int64 Size) override { ULARGE_INTEGER sz; sz.QuadPart = Size; HRESULT res = s->SetSize(sz); return SUCCEEDED(res) ? Size : -1; } int64 GetPos() override { LARGE_INTEGER i; ULARGE_INTEGER o; i.QuadPart = 0; HRESULT res = s->Seek(i, STREAM_SEEK_CUR, &o); if (SUCCEEDED(res)) return o.QuadPart; return -1; } int64 SetPos(int64 Pos) override { LARGE_INTEGER i; ULARGE_INTEGER o; i.QuadPart = Pos; HRESULT res = s->Seek(i, STREAM_SEEK_SET, &o); if (SUCCEEDED(res)) return o.QuadPart; return -1; } ssize_t Read(void *Ptr, ssize_t Size, int Flags = 0) override { ULONG Rd = 0; HRESULT res = s->Read(Ptr, (ULONG)Size, &Rd); return SUCCEEDED(res) ? Rd : 0; } ssize_t Write(const void *Ptr, ssize_t Size, int Flags = 0) override { ULONG Wr = 0; HRESULT res = s->Write(Ptr, (ULONG)Size, &Wr); return SUCCEEDED(res) ? Wr : 0; } GStreamI *Clone() override { return new IStreamWrap(s); } }; /// This class wraps a GStream in an IStream interface... class GStreamWrap : public GUnknownImpl { bool Own; GStream *s; public: GStreamWrap(GStream *src, bool own = true) { s = src; Own = own; TraceRefs = true; AddInterface(IID_IStream, (IStream*)this); } ~GStreamWrap() { if (Own) delete s; } HRESULT STDMETHODCALLTYPE Read(void *pv, ULONG cb, ULONG *pcbRead) { if (!s || !pv) return E_INVALIDARG; ULONG TotalRd = 0; - uint8 *Ptr = (uint8*)pv; + uint8_t *Ptr = (uint8_t*)pv; size_t i = 0; while (i < cb) { size_t Remaining = cb - i; auto Rd = s->Read(Ptr + i, Remaining); // LgiTrace("Read(%i)=%i\n", cb, Rd); if (Rd > 0) i += Rd; else break; } if (pcbRead) *pcbRead = (ULONG)i; return S_OK; } HRESULT STDMETHODCALLTYPE Write(const void *pv, ULONG cb, ULONG *pcbWritten) { if (!s || !pv) return E_INVALIDARG; auto Wr = s->Write(pv, cb); if (pcbWritten) *pcbWritten = (ULONG)Wr; return S_OK; } HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) { if (!s) return E_INVALIDARG; int64 NewPos = -1; switch (dwOrigin) { case STREAM_SEEK_SET: NewPos = s->SetPos(dlibMove.QuadPart); break; case STREAM_SEEK_CUR: NewPos = s->SetPos(s->GetPos() + dlibMove.QuadPart); break; case STREAM_SEEK_END: NewPos = s->SetPos(s->GetSize() - dlibMove.QuadPart); break; // default: return E_INVALIDARG; } if (NewPos < 0) return E_NOTIMPL; if (plibNewPosition) plibNewPosition->QuadPart = NewPos; return S_OK; } HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER libNewSize) { if (!s) return E_INVALIDARG; if (s->SetSize(libNewSize.QuadPart) != libNewSize.QuadPart) return E_FAIL; return S_OK; } HRESULT STDMETHODCALLTYPE Stat(__RPC__out STATSTG *pstatstg, DWORD grfStatFlag) { if (!pstatstg) return E_INVALIDARG; GVariant Name; if (pstatstg->pwcsName && s->GetValue("FileName", Name)) { GAutoWString w(Utf8ToWide(Name.Str())); Strcpy(pstatstg->pwcsName, 256, w.Get()); } pstatstg->type = STGTY_STREAM; pstatstg->cbSize.QuadPart = s->GetSize(); return S_OK; } #define NotImplemented { LgiAssert(!"Function not implemented."); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE CopyTo(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten) NotImplemented HRESULT STDMETHODCALLTYPE Commit(DWORD grfCommitFlags) NotImplemented HRESULT STDMETHODCALLTYPE Revert() NotImplemented HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) NotImplemented HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) NotImplemented HRESULT STDMETHODCALLTYPE Clone(__RPC__deref_out_opt IStream **ppstm) NotImplemented }; #endif diff --git a/include/common/GContainers.h b/include/common/GContainers.h --- a/include/common/GContainers.h +++ b/include/common/GContainers.h @@ -1,1160 +1,1160 @@ /** \file \author Matthew Allen \date 27/11/1996 \brief Container class header.\n Copyright (C) 1996-2003, Matthew Allen */ #ifndef _CONTAIN_H_ #define _CONTAIN_H_ #include "LgiInc.h" #include "LgiOsDefs.h" #include "GStream.h" /// Template for using DLinkList with a type safe API. #define ITEM_PTRS 64 LgiFunc bool UnitTest_ListClass(); #ifdef _DEBUG #define VALIDATE() Validate() #else #define VALIDATE() #endif template class List { struct LstBlk { LstBlk *Next, *Prev; - uint8 Count; + uint8_t Count; T *Ptr[ITEM_PTRS]; LstBlk() { Next = Prev = NULL; Count = 0; ZeroObj(Ptr); } bool Full() { return Count >= ITEM_PTRS; } int Remaining() { return ITEM_PTRS - Count; } }; public: class Iter { public: List *Lst; LstBlk *i; int Cur; Iter(List *lst) { Lst = lst; i = 0; Cur = 0; } Iter(List *lst, LstBlk *item, int c) { Lst = lst; i = item; Cur = c; } bool operator ==(const Iter &it) const { int x = (int)In() + (int)it.In(); if (x == 2) return (i == it.i) && (Cur == it.Cur); return x == 0; } bool operator !=(const Iter &it) const { return !(*this == it); } bool In() const { return i && Cur >= 0 && Cur < i->Count; } operator T*() const { return In() ? i->Ptr[Cur] : NULL; } T *operator *() const { return In() ? i->Ptr[Cur] : NULL; } Iter &operator =(LstBlk *item) { i = item; if (!i) Cur = 0; return *this; } Iter &operator =(int c) { Cur = c; return *this; } Iter &operator =(Iter *iter) { Lst = iter->Lst; i = iter->i; Cur = iter->Cur; return *this; } int GetIndex(int Base) { if (i) return Base + Cur; return -1; } bool Next() { if (i) { Cur++; if (Cur >= i->Count) { i = i->Next; if (i) { Cur = 0; return i->Count > 0; } } else return true; } return false; } bool Prev() { if (i) { Cur--; if (Cur < 0) { i = i->Prev; if (i && i->Count > 0) { Cur = i->Count - 1; return true; } } else return true; } return false; } bool Delete() { if (i) { LgiAssert(Lst); i->Delete(Cur, i); return true; } return false; } Iter &operator ++() { Next(); return *this; } Iter &operator --() { Prev(); return *this; } Iter &operator ++(int) { Next(); return *this; } Iter &operator --(int) { Prev(); return *this; } }; typedef Iter I; // typedef int (*CompareFn)(T *a, T *b, NativeInt data); protected: size_t Items; LstBlk *FirstObj, *LastObj; Iter Local; LstBlk *NewBlock(LstBlk *Where) { LstBlk *i = new LstBlk; LgiAssert(i != NULL); if (!i) return NULL; if (Where) { i->Prev = Where; if (i->Prev->Next) { // Insert i->Next = Where->Next; i->Prev->Next = i->Next->Prev = i; } else { // Append i->Prev->Next = i; LgiAssert(LastObj == Where); LastObj = i; } } else { // First object LgiAssert(FirstObj == 0); LgiAssert(LastObj == 0); FirstObj = LastObj = i; } return i; } bool DeleteBlock(LstBlk *i) { if (!i) { LgiAssert(!"No ptr."); return false; } if (i->Prev != 0 && i->Next != 0) { LgiAssert(FirstObj != i); LgiAssert(LastObj != i); } if (i->Prev) { i->Prev->Next = i->Next; } else { LgiAssert(FirstObj == i); FirstObj = i->Next; } if (i->Next) { i->Next->Prev = i->Prev; } else { LgiAssert(LastObj == i); LastObj = i->Prev; } delete i; return true; } bool Insert(LstBlk *i, T *p, ssize_t Index = -1) { if (!i) return false; if (i->Full()) { if (!i->Next) { // Append a new LstBlk if (!NewBlock(i)) return false; } if (Index < 0) return Insert(i->Next, p, Index); // Push last pointer into Next if (i->Next->Full()) NewBlock(i); // Create an empty Next if (!Insert(i->Next, i->Ptr[ITEM_PTRS-1], 0)) return false; i->Count--; Items--; // We moved the item... not inserted it. // Fall through to the local "non-full" insert... } LgiAssert(!i->Full()); if (Index < 0) Index = i->Count; else if (Index < i->Count) memmove(i->Ptr+Index+1, i->Ptr+Index, (i->Count-Index) * sizeof(p)); i->Ptr[Index] = p; i->Count++; Items++; LgiAssert(i->Count <= ITEM_PTRS); return true; } bool Delete(Iter &Pos) { if (!Pos.In()) return false; int &Index = Pos.Cur; LstBlk *&i = Pos.i; if (Index < i->Count-1) memmove(i->Ptr+Index, i->Ptr+Index+1, (i->Count-Index-1) * sizeof(T*)); Items--; if (--i->Count == 0) { // This Item is now empty, remove and reset current // into the next Item bool ClearLocal = i == Local.i; LstBlk *n = i->Next; bool Status = DeleteBlock(i); Pos.Cur = 0; Pos.i = n; if (ClearLocal) Local.i = NULL; return Status; } else if (Index >= i->Count) { // Carry current item over to next Item Pos.i = Pos.i->Next; Pos.Cur = 0; } return true; } Iter GetIndex(size_t Index, size_t *Base = NULL) { size_t n = 0; for (LstBlk *i = FirstObj; i; i = i->Next) { if (Index >= n && Index < n + i->Count) { if (Base) *Base = n; return Iter(this, i, (int) (Index - n)); } n += i->Count; } if (Base) *Base = 0; return Iter(this); } Iter GetPtr(T *Ptr, size_t *Base = NULL) { size_t n = 0; for (LstBlk *i = FirstObj; i; i = i->Next) { for (int k=0; kCount; k++) { if (i->Ptr[k] == Ptr) { if (Base) *Base = n; return Iter(this, i, k); } } n += i->Count; } if (Base) *Base = 0; return Iter(this); } class BTreeNode { public: T *Node; BTreeNode *Left; BTreeNode *Right; T ***Index(T ***Items) { if (Left) { Items = Left->Index(Items); } **Items = Node; Items++; if (Right) { Items = Right->Index(Items); } return Items; } }; class BTree { size_t Items; size_t Used; BTreeNode *Node; BTreeNode *Min; BTreeNode *Max; public: BTree(size_t i) { Used = 0; Min = Max = 0; Node = new BTreeNode[Items = i]; if (Node) { memset(Node, 0, Items * sizeof(BTreeNode)); } } ~BTree() { DeleteArray(Node); } int GetItems() { return Used; } template bool Add(T *Obj, int (*Cmp)(T *a, T *b, User data), User Data) { if (Used) { BTreeNode *Cur = Node; if (Used < Items) { if (Cmp(Obj, Max->Node, Data) >= 0) { Max->Right = Node + Used++; Max = Max->Right; Max->Node = Obj; return true; } if (Cmp(Obj, Min->Node, Data) < 0) { Min->Left = Node + Used++; Min = Min->Left; Min->Node = Obj; return true; } while (true) { int c = Cmp(Obj, Cur->Node, Data); BTreeNode **Ptr = (c < 0) ? &Cur->Left : &Cur->Right; if (*Ptr) { Cur = *Ptr; } else if (Used < Items) { *Ptr = &Node[Used++]; (*Ptr)->Node = Obj; return true; } else return false; } } else { LgiAssert(0); } return false; } else { Min = Max = Node; Node[Used++].Node = Obj; return true; } } void Index(T ***Items) { if (Node) { Node->Index(Items); } } }; public: List() : Local(this, NULL, 0) { FirstObj = LastObj = NULL; Items = 0; } ~List() { VALIDATE(); Empty(); } size_t Length() const { return Items; } bool Length(size_t Len) { if (Len == 0) return Empty(); else if (Len == Items) return true; VALIDATE(); bool Status = false; if (Len < Items) { // Decrease list size... size_t Base = 0; Iter i = GetIndex(Len, &Base); if (i.i) { size_t Offset = Len - Base; if (!(Offset <= i.i->Count)) { LgiAssert(!"Offset error"); } - i.i->Count = (uint8) (Len - Base); + i.i->Count = (uint8_t) (Len - Base); LgiAssert(i.i->Count >= 0 && i.i->Count < ITEM_PTRS); while (i.i->Next) { DeleteBlock(i.i->Next); } Items = Len; } else LgiAssert(!"Iterator invalid."); } else { // Increase list size... LgiAssert(!"Impl me."); } VALIDATE(); return Status; } bool Empty() { VALIDATE(); Local.i = NULL; LstBlk *n; for (LstBlk *i = FirstObj; i; i = n) { n = i->Next; delete i; } FirstObj = LastObj = NULL; Items = 0; VALIDATE(); return true; } bool Delete() { VALIDATE(); bool Status = Delete(Local); VALIDATE(); return Status; } bool DeleteAt(size_t i) { VALIDATE(); Iter p = GetIndex(i); if (!p.In()) return false; bool Status = Delete(p); VALIDATE(); return Status; } bool Delete(T *Ptr) { VALIDATE(); Local = GetPtr(Ptr); if (!Local.In()) return false; bool Status = Delete(Local); VALIDATE(); return Status; } bool Insert(T *p, ssize_t Index = -1) { VALIDATE(); if (!LastObj) { LstBlk *b = NewBlock(NULL); if (!b) return false; b->Ptr[b->Count++] = p; Items++; VALIDATE(); return true; } bool Status; size_t Base; Iter Pos(this); if (Index < 0) Status = Insert(LastObj, p, Index); else { Pos = GetIndex(Index, &Base); if (Pos.i) Status = Insert(Pos.i, p, (int) (Index - Base)); else Status = Insert(LastObj, p, -1); } VALIDATE(); LgiAssert(Status); return Status; } bool Add(T *p) { return Insert(p); } T *First() { VALIDATE(); Local = FirstObj; Local.Cur = 0; VALIDATE(); return Local; } T *Last() { VALIDATE(); Local = LastObj; if (Local.i) Local.Cur = Local.i->Count - 1; VALIDATE(); return Local; } T *Next() { return ++Local; } T *Prev() { return --Local; } T *Current() const { VALIDATE(); return Local; } T *operator [](size_t Index) { VALIDATE(); Local = GetIndex(Index); VALIDATE(); return Local; } ssize_t IndexOf(T *p) { VALIDATE(); size_t Base = -1; Local = GetPtr(p, &Base); LgiAssert(Base != -1); ssize_t Idx = Local.In() ? Base + Local.Cur : -1; VALIDATE(); return Idx; } bool HasItem(T *p) { VALIDATE(); Iter Pos = GetPtr(p); bool Status = Pos.In(); VALIDATE(); return Status; } T *ItemAt(ssize_t i) { VALIDATE(); Local = GetIndex(i); VALIDATE(); return Local; } /// Sorts the list template void Sort ( /// The callback function used to compare 2 pointers int (*Compare)(T *a, T *b, User data), /// User data that is passed into the callback User Data = 0 ) { if (Items < 1) return; VALIDATE(); BTree Tree(Items); T ***iLst = new T**[Items]; if (iLst) { int n=0; LstBlk *i = FirstObj; while (i) { for (int k=0; kCount; k++) { iLst[n++] = i->Ptr + k; Tree.Add(i->Ptr[k], Compare, Data); } i = i->Next; } Tree.Index(iLst); delete [] iLst; } VALIDATE(); } /// Delete all pointers in the list as dynamically allocated objects void DeleteObjects() { VALIDATE(); LstBlk *n; for (LstBlk *i = FirstObj; i; i = n) { n = i->Next; for (int n=0; nCount; n++) { if (i->Ptr[n]) { #ifdef _DEBUG size_t Objs = Items; #endif delete i->Ptr[n]; #ifdef _DEBUG if (Objs != Items) LgiAssert(!"Do you have self deleting objects?"); #endif i->Ptr[n] = NULL; } } delete i; } FirstObj = LastObj = NULL; Items = 0; Local.i = NULL; VALIDATE(); } /// Delete all pointers in the list as dynamically allocated arrays void DeleteArrays() { VALIDATE(); LstBlk *n; for (LstBlk *i = FirstObj; i; i = n) { n = i->Next; for (char n=0; nCount; n++) { delete [] i->Ptr[n]; i->Ptr[n] = NULL; } delete i; } FirstObj = LastObj = NULL; Items = 0; Local.i = NULL; VALIDATE(); } void Swap(List &other) { LSwap(FirstObj, other.FirstObj); LSwap(LastObj, other.LastObj); LSwap(Items, other.Items); LSwap(Local.Lst, other.Local.Lst); LSwap(Local.i, other.Local.i); LSwap(Local.Cur, other.Local.Cur); } /// Assign the contents of another list to this one #if 0 List &operator=(const List &lst) { Empty(); for (auto i : lst) Add(i); return *this; } #else List &operator =(const List &lst) { VALIDATE(); // Make sure we have enough blocks allocated size_t i = 0; // Set the existing blocks to empty... for (LstBlk *out = FirstObj; out; out = out->Next) { out->Count = 0; i += ITEM_PTRS; } // If we don't have enough, add more... while (i < lst.Length()) { LstBlk *out = NewBlock(LastObj); if (out) i += ITEM_PTRS; else { LgiAssert(!"Can't allocate enough blocks?"); return *this; } } // If we have too many, free some... while (LastObj && i > lst.Length() + ITEM_PTRS) { DeleteBlock(LastObj); i -= ITEM_PTRS; } // Now copy over the block's contents. LstBlk *out = FirstObj; Items = 0; for (LstBlk *in = lst.FirstObj; in; in = in->Next) { for (int pos = 0; pos < in->Count; ) { if (!out->Remaining()) { out = out->Next; if (!out) { LgiAssert(!"We should have pre-allocated everything..."); return *this; } } int Cp = MIN(out->Remaining(), in->Count - pos); LgiAssert(Cp > 0); memcpy(out->Ptr + out->Count, in->Ptr + pos, Cp * sizeof(T*)); out->Count += Cp; pos += Cp; Items += Cp; } } VALIDATE(); return *this; } #endif Iter begin(ssize_t At = 0) { return GetIndex(At); } Iter rbegin(ssize_t At = 0) { return GetIndex(Length()-1); } Iter end() { return Iter(this, NULL, -1); } bool Validate() const { if (FirstObj == NULL && LastObj == NULL && Items == 0) return true; size_t n = 0; LstBlk *Prev = NULL; bool SeenLocalBlk = false; for (LstBlk *i = FirstObj; i; i = i->Next) { if (Local.i == i) SeenLocalBlk = true; for (int k=0; kCount; k++) { if (!i->Ptr[k]) { LgiAssert(!"NULL pointer in LstBlk."); return false; } else { n++; } } if (i == FirstObj) { if (i->Prev) { LgiAssert(!"First object's 'Prev' should be NULL."); return false; } } else if (i == LastObj) { if (i->Next) { LgiAssert(!"Last object's 'Next' should be NULL."); return false; } } else { if (i->Prev != Prev) { LgiAssert(!"Middle LstBlk 'Prev' incorrect."); return false; } } Prev = i; } if (Local.i != NULL) { if (!SeenLocalBlk && Local.i != NULL) { LgiAssert(!"The local iterator is not present in the list."); return false; } } if (Items != n) { LgiAssert(!"Item count cache incorrect."); return false; } return true; } }; /// \brief Data storage class. /// /// Allows data to be buffered in separate memory /// blocks and then written to a continuous block for processing. Works /// as a first in first out que. You can (and should) provide a suitable /// PreAlloc size to the constructor. This can reduce the number of blocks /// of memory being used (and their associated alloc/free time and /// tracking overhead) in high volume situations. class LgiClass GMemQueue : public GStream { protected: /// Data block. These can contain a mix of 3 types of data: /// 1) Bytes that have been read (always at the start of the block) /// 2) Bytes that have been written (always in the middle) /// 3) Unwritten buffer space (always at the end) /// /// Initially a block starts out as completely type 3 bytes, just garbage /// data waiting to be written to. Then something writes into the pipe and bytes /// are stored into this free space, and the 'Used' variable is set to show /// how much of the available buffer is used. At this point we have type 2 and /// type 3 bytes in the block. The buffer can fill up completely in which /// case Used == Size and there are no type 3 bytes left. Also at some point /// something can start reading bytes out of the block which causes the 'Next' /// value to be increased, at which point the block starts with 'Next' bytes /// of type 1. Crystal? struct Block { /// Number of bytes in this block that have been read /// Type 1 or 'read' bytes are in [0,Next-1]. int Next; /// Number of bytes that are used in this block include read bytes. /// Type 2 or 'used' bytes are in [Next,Used-1]. int Used; /// Total size of the memory block /// Type 3 or 'unused' bytes are in [Used,Size-1]. int Size; - uint8 *Ptr() { return (uint8*) (this + 1); } + uint8_t *Ptr() { return (uint8_t*) (this + 1); } Block() { Next = 0; Size = 0; Used = 0; } }; int PreAlloc; List Mem; public: /// Constructor GMemQueue ( /// Sets the block size, which means allocating ahead and then joining /// together smaller inserts into 1 continuous block. int PreAlloc = 0 ); /// Destructor virtual ~GMemQueue(); GMemQueue &operator =(GMemQueue &p); /// Empties the container freeing any memory used. virtual void Empty(); /// Returns a dynamically allocated block that contains all the data in the container /// in a continuous block. virtual void *New ( /// If this is > 0 then the function add's the specified number of bytes containing /// the value 0 on the end of the block. This is useful for NULL terminating strings /// or adding buffer space on the end of the block returned. int AddBytes = 0 ); /// Reads data from the start of the container without removing it from /// the que. Returns the bytes copied. virtual int64 Peek ( /// Buffer for output uchar *Ptr, /// Bytes to look at int Size ); /// Gets the total bytes in the container int64 GetSize() override; /// Reads bytes off the start of the container ssize_t Read(void *Buffer, ssize_t Size, int Flags = 0) override; /// Writes bytes to the end of the container ssize_t Write(const void *Buffer, ssize_t Size, int Flags = 0) override; bool Write(const GString &s) { return Write(s.Get(), s.Length()) == s.Length(); } }; /// A version of GBytePipe for strings. Adds some special handling for strings. class LgiClass GStringPipe : public GMemQueue { ssize_t LineChars(); ssize_t SaveToBuffer(char *Str, ssize_t BufSize, ssize_t Chars); public: /// Constructs the object GStringPipe ( /// Number of bytes to allocate per block. int PreAlloc = -1 ) : GMemQueue(PreAlloc) {} ~GStringPipe() {} virtual ssize_t Pop(char *Str, ssize_t Chars); virtual ssize_t Pop(GArray &Buf); virtual GString Pop(); virtual ssize_t Push(const char *Str, ssize_t Chars = -1); virtual ssize_t Push(const char16 *Str, ssize_t Chars = -1); char *NewStr() { return (char*)New(sizeof(char)); } GString NewGStr(); char16 *NewStrW() { return (char16*)New(sizeof(char16)); } GStringPipe &operator +=(const GString &s) { Write(s.Get(), s.Length()); return *this; } #ifdef _DEBUG static bool UnitTest(); #endif }; #define GMEMFILE_BLOCKS 8 class LgiClass GMemFile : public GStream { struct Block { size_t Offset; size_t Used; - uint8 Data[1]; + uint8_t Data[1]; }; // The current file pointer uint64 CurPos; /// Number of blocks in use int Blocks; /// Data payload of blocks int BlockSize; /// Some blocks are built into the struct, no memory alloc needed. Block *Local[GMEMFILE_BLOCKS]; // Buf if they run out we can alloc some more. GArray Extra; Block *Get(int Index); bool FreeBlock(Block *b); Block *GetLast() { return Get(Blocks-1); } Block *Create(); int CurBlock() { return (int) (CurPos / BlockSize); } public: GMemFile(int BlkSize = 256); ~GMemFile(); void Empty(); int64 GetSize() override; int64 SetSize(int64 Size) override; int64 GetPos() override; int64 SetPos(int64 Pos) override; ssize_t Read(void *Ptr, ssize_t Size, int Flags = 0) override; ssize_t Write(const void *Ptr, ssize_t Size, int Flags = 0) override; }; #endif diff --git a/include/common/GCss.h b/include/common/GCss.h --- a/include/common/GCss.h +++ b/include/common/GCss.h @@ -1,1275 +1,1275 @@ /// \file /// \author Matthew Allen #ifndef _G_CSS_H_ #define _G_CSS_H_ /// I've using the american spelling for 'color' as opposed to the english 'colour' /// because the CSS spec is written with 'color' as the spelling. #include "LgiInc.h" #include "LgiOsDefs.h" #include "GMem.h" #include "Gdc2.h" #include "GAutoPtr.h" #include "GString.h" #include "LHashTable.h" #ifndef LINUX #pragma pack(push, 1) #endif /// Css property container class LgiClass GCss { public: enum ParsingStyle { ParseStrict, ParseRelaxed, }; enum PropTypes { TypeEnum = 1, TypeLen, TypeGRect, TypeColor, TypeBackground, TypeImage, TypeBorder, TypeStrings, }; /// These are all the types of CSS properties catagorised by their data type. /// The enum value of the prop is encoded in the bottom 8 bits, the data type of /// the property is encoding in the top remaining top bits. enum PropType { PropNull = 0, // Enum based props PropDisplay = TypeEnum << 8,// DisplayType PropFloat, // FloatType PropPosition, // PositionType PropOverflow, // OverflowType PropVisibility, // VisibilityType PropFontStyle, // FontStyleType PropFontVariant, // FontVariantType PropFontWeight, // FontWeightType PropBackgroundRepeat, // RepeatType PropBackgroundAttachment, // AttachmentType PropTextDecoration, // TextDecorType PropWordWrap, // WordWraptype PropListStyleType, // ListStyleTypes PropLetterSpacing, PropFont, PropListStyle, PropBorderCollapse, // BorderCollapseType PropBorderTopStyle, PropBorderRightStyle, PropBorderBottomStyle, PropBorderLeftStyle, // Length based props PropZIndex = TypeLen << 8, PropWidth, PropMinWidth, PropMaxWidth, PropHeight, PropMinHeight, PropMaxHeight, PropTop, PropRight, PropBottom, PropLeft, PropMargin, PropMarginTop, PropMarginRight, PropMarginBottom, PropMarginLeft, PropPadding, PropPaddingTop, PropPaddingRight, PropPaddingBottom, PropPaddingLeft, PropLineHeight, PropVerticalAlign, PropFontSize, PropBackgroundX, PropBackgroundY, PropBackgroundPos, PropTextAlign, PropBorderSpacing, PropBorderLeftWidth, PropBorderTopWidth, PropBorderRightWidth, PropBorderBottomWidth, PropBorderRadius, Prop_CellPadding, // not real CSS, but used by GHtml2 to store 'cellpadding' // GRect based props PropClip = TypeGRect<<8, PropXSubRect, // Background meta style PropBackground = TypeBackground << 8, // ColorDef based PropColor = TypeColor << 8, PropBackgroundColor, PropNoPaintColor, PropBorderTopColor, PropBorderRightColor, PropBorderBottomColor, PropBorderLeftColor, // ImageDef based PropBackgroundImage = TypeImage << 8, // BorderDef based PropBorder = TypeBorder << 8, PropBorderStyle, PropBorderColor, PropBorderTop, PropBorderRight, PropBorderBottom, PropBorderLeft, // StringsDef based PropFontFamily = TypeStrings << 8, }; PropTypes GetType(PropType p) { return (PropTypes) ((int)p >> 8); } enum BorderCollapseType { CollapseInherit, CollapseCollapse, CollapseSeparate, }; enum WordWrapType { WrapNormal, WrapBreakWord, WrapNone, // Not in the CSS standard but useful for apps anyway. }; enum DisplayType { DispInherit, DispBlock, DispInline, DispInlineBlock, DispListItem, DispNone, }; enum PositionType { PosInherit, PosStatic, PosRelative, PosAbsolute, PosFixed, }; enum LengthType { // Standard length types LenInherit, LenAuto, LenPx, LenPt, LenEm, LenEx, LenCm, LenPercent, LenNormal, // Horizontal alignment types AlignLeft, AlignRight, AlignCenter, AlignJustify, // Vertical alignment types VerticalBaseline, VerticalSub, VerticalSuper, VerticalTop, VerticalTextTop, VerticalMiddle, VerticalBottom, VerticalTextBottom, // Absolute font sizes SizeXXSmall, SizeXSmall, SizeSmall, SizeMedium, SizeLarge, SizeXLarge, SizeXXLarge, // Relitive font sizes, SizeSmaller, SizeLarger, }; enum FloatType { FloatInherit, FloatLeft, FloatRight, FloatNone, }; enum OverflowType { OverflowInherit, OverflowVisible, OverflowHidden, OverflowScroll, OverflowAuto, }; enum VisibilityType { VisibilityInherit, VisibilityVisible, VisibilityHidden, VisibilityCollapse }; enum ListStyleTypes { ListInherit, ListNone, ListDisc, ListCircle, ListSquare, ListDecimal, ListDecimalLeadingZero, ListLowerRoman, ListUpperRoman, ListLowerGreek, ListUpperGreek, ListLowerAlpha, ListUpperAlpha, ListArmenian, ListGeorgian, }; enum FontStyleType { FontStyleInherit, FontStyleNormal, FontStyleItalic, FontStyleOblique, }; enum FontVariantType { FontVariantInherit, FontVariantNormal, FontVariantSmallCaps, }; enum FontWeightType { FontWeightInherit, FontWeightNormal, FontWeightBold, FontWeightBolder, FontWeightLighter, FontWeight100, FontWeight200, FontWeight300, FontWeight400, FontWeight500, FontWeight600, FontWeight700, FontWeight800, FontWeight900, }; enum TextDecorType { TextDecorInherit, TextDecorNone, TextDecorUnderline, TextDecorOverline, TextDecorLineThrough, TextDecorSquiggle, // for spelling errors }; enum ColorType { ColorInherit, ColorTransparent, ColorRgb, ColorLinearGradient, ColorRadialGradient, }; enum RepeatType { RepeatInherit, RepeatBoth, RepeatX, RepeatY, RepeatNone, }; enum AttachmentType { AttachmentInherit, AttachmentScroll, AttachmentFixed, }; static double FontSizeTable[7]; // SizeXSmall etc #define _FloatAbs(a) \ (((a) < 0.0f) ? -(a) : (a)) #define FloatIsEqual(a, b) \ (_FloatAbs(a - b) < 0.0001) struct LgiClass Len { LengthType Type; float Value; Len(const char *init = NULL, ParsingStyle ParseType = ParseRelaxed) { Type = LenInherit; Value = 0.0; if (init) Parse(init, PropNull, ParseType); } Len(LengthType t, float v = 0.0) { Type = t; Value = v; } Len &operator =(const Len &l) { Type = l.Type; Value = l.Value; return *this; } bool Parse(const char *&s, PropType Prop = PropNull, ParsingStyle Type = ParseStrict); bool IsValid() const { return Type != LenInherit; } bool IsDynamic() const { return Type == LenPercent || Type == LenInherit || Type == LenAuto || Type == SizeSmaller || Type == SizeLarger; } bool operator ==(const Len &l) const { return Type == l.Type && FloatIsEqual(Value, l.Value); } bool operator !=(const Len &l) const { return !(*this == l); } bool ToString(GStream &p) const; /// Convert the length to pixels int ToPx ( /// The size of the parent box if known, or -1 if unknown. int Box = 0, /// Any relevant font GFont *Font = 0, /// The DPI of the relevant device if known, or -1 if unknown int Dpi = -1 ); Len operator *(const Len &l) const; }; struct LgiClass ColorStop { float Pos; - uint32 Rgb32; + uint32_t Rgb32; bool operator ==(const ColorStop &c) { return FloatIsEqual(Pos, c.Pos) && Rgb32 == c.Rgb32; } bool operator !=(const ColorStop &c) { return !(*this == c); } }; struct LgiClass ColorDef { ColorType Type; - uint32 Rgb32; + uint32_t Rgb32; GArray Stops; - ColorDef(ColorType ct = ColorInherit, uint32 rgb32 = 0) + ColorDef(ColorType ct = ColorInherit, uint32_t rgb32 = 0) { Type = ct; Rgb32 = rgb32; } ColorDef(const GColour &col) { Type = ColorRgb; Rgb32 = col.c32(); } ColorDef(const char *init) { Type = ColorInherit; Parse(init); } ColorDef(COLOUR col) { Type = ColorRgb; Rgb32 = Rgb24To32(col); } operator GColour() { GColour c; if (Type == ColorRgb) c.Set(Rgb32, 32); return c; } bool IsValid() { return Type != ColorInherit; } bool Parse(const char *&s); ColorDef &operator =(const ColorDef &c) { Type = c.Type; Rgb32 = c.Rgb32; Stops = c.Stops; return *this; } bool operator ==(const ColorDef &c) { if (Type != c.Type) return false; if (Type == ColorRgb) return Rgb32 == c.Rgb32; if (Stops.Length() != c.Stops.Length()) return false; - for (uint32 i=0; i { public: StringsDef(char *init = 0) { if (ValidStr(init)) *this = init; else LgiAssert(init == 0); } StringsDef(const StringsDef &c) { *this = c; } ~StringsDef() { Empty(); } StringsDef &operator =(const char *s) { Parse(s); return *this; } void Empty() { DeleteArrays(); } StringsDef &operator =(const StringsDef &s) { Empty(); for (unsigned i=0; i Start) { GAutoString n(NewStr(Start, s-Start)); if (ValidStr(n)) { if (_stricmp(n, "inherit")) Add(n.Release()); } else LgiAssert(!"Not a valid string."); } if (s) s++; } else { const char *Start = s; while (*s && !strchr(Delimiters, *s)) s++; if (s > Start) { GAutoString n(NewStr(Start, s-Start)); if (ValidStr(n)) { if (_stricmp(n, "inherit")) Add(n.Release()); } else LgiAssert(!"Not a valid string."); } } } return true; } }; class Store; /// This class parses and stores a selector. The job of matching selectors and /// hashing them is still the responsibility of the calling library. If an application /// needs some code to do that it can optionally use GCss::Store to do that. class LgiClass Selector { public: enum PartType { SelNull, SelType, SelUniversal, SelAttrib, SelClass, SelMedia, SelID, SelPseudo, SelFontFace, SelPage, SelList, SelImport, SelKeyFrames, SelIgnored, CombDesc, CombChild, CombAdjacent, }; enum MediaType { MediaNull = 0x0, MediaPrint = 0x1, MediaScreen = 0x2, }; struct Part { PartType Type; GAutoString Value; GAutoString Param; int Media; Part() { Type = SelNull; Media = MediaNull; } bool IsSel() { return Type == SelType || Type == SelUniversal || Type == SelAttrib || Type == SelClass || Type == SelMedia || Type == SelID || Type == SelPseudo; } Part &operator =(const Part &s) { Type = s.Type; Value.Reset(NewStr(s.Value)); Param.Reset(NewStr(s.Param)); return *this; } }; GArray Parts; GArray Combs; char *Style; int SourceIndex; GAutoString Raw; GAutoPtr Children; Selector() { Style = NULL; SourceIndex = 0; } bool TokString(GAutoString &a, const char *&s); const char *PartTypeToString(PartType p); GAutoString Print(); bool Parse(const char *&s); size_t GetSimpleIndex() { return Combs.Length() ? Combs[Combs.Length()-1] + 1 : 0; } bool IsAtMedia(); bool ToString(GStream &p); - uint32 GetSpecificity(); + uint32_t GetSpecificity(); Selector &operator =(const Selector &s); }; /// This hash table stores arrays of selectors by name. typedef GArray SelArray; typedef LHashTbl,SelArray*> SelMap; class SelectorMap : public SelMap { public: ~SelectorMap() { Empty(); } void Empty() { // for (SelArray *s = First(); s; s = Next()) for (auto s : *this) { s.value->DeleteObjects(); delete s.value; } SelMap::Empty(); } SelArray *Get(const char *s) { SelArray *a = Find(s); if (!a) Add(s, a = new SelArray); return a; } }; template struct ElementCallback { public: /// Returns the element name virtual const char *GetElement(T *obj) = 0; /// Returns the document unque element ID virtual const char *GetAttr(T *obj, const char *Attr) = 0; /// Returns the class virtual bool GetClasses(GString::Array &Classes, T *obj) = 0; /// Returns the parent object virtual T *GetParent(T *obj) = 0; /// Returns an array of child objects virtual GArray GetChildren(T *obj) = 0; }; /// This class parses and stores the CSS selectors and styles. class LgiClass Store { protected: /// This code matches a simple part of a selector, i.e. no combinatorial operators involved. template bool MatchSimpleSelector ( /// The full selector. GCss::Selector *Sel, /// The start index of the simple selector parts. Stop at the first comb operator or the end of the parts. ssize_t PartIdx, /// Our context callback to get properties of the object ElementCallback *Context, /// The object to match T *Obj ) { const char *Element = Context->GetElement(Obj); for (size_t n = PartIdx; nParts.Length(); n++) { GCss::Selector::Part &p = Sel->Parts[n]; switch (p.Type) { case GCss::Selector::SelType: { const char *Tag = Context->GetElement(Obj); if (!Tag || _stricmp(Tag, p.Value)) return false; break; } case GCss::Selector::SelUniversal: { // Match everything break; } case GCss::Selector::SelAttrib: { if (!p.Value) return false; char *e = strchr(p.Value, '='); if (!e) return false; GAutoString Var(NewStr(p.Value, e - p.Value)); const char *TagVal = Context->GetAttr(Obj, Var); if (!TagVal) return false; GAutoString Val(NewStr(e + 1)); if (_stricmp(Val, TagVal)) return false; break; } case GCss::Selector::SelClass: { // Check the class matches GString::Array Class; if (!Context->GetClasses(Class, Obj)) return false; if (Class.Length() == 0) return false; bool Match = false; for (unsigned i=0; iGetAttr(Obj, "id"); if (!Id || _stricmp(Id, p.Value)) return false; break; } case GCss::Selector::SelPseudo: { const char *Href = NULL; if ( ( Element != NULL && !_stricmp(Element, "a") && p.Value && !_stricmp(p.Value, "link") && (Href = Context->GetAttr(Obj, "href")) != NULL ) || ( p.Value && *p.Value == '-' ) ) break; return false; break; } default: { // Comb operator, so return the current match value return true; } } } return true; } /// This code matches a all the parts of a selector. template bool MatchFullSelector(GCss::Selector *Sel, ElementCallback *Context, T *Obj) { bool Complex = Sel->Combs.Length() > 0; ssize_t CombIdx = Complex ? (ssize_t)Sel->Combs.Length() - 1 : 0; ssize_t StartIdx = (Complex) ? Sel->Combs[CombIdx] + 1 : 0; bool Match = MatchSimpleSelector(Sel, StartIdx, Context, Obj); if (!Match) return false; if (Complex) { T *CurrentParent = Context->GetParent(Obj); for (; CombIdx >= 0; CombIdx--) { if (CombIdx >= (int)Sel->Combs.Length()) break; StartIdx = Sel->Combs[CombIdx]; LgiAssert(StartIdx > 0); if (StartIdx >= (ssize_t)Sel->Parts.Length()) break; GCss::Selector::Part &p = Sel->Parts[StartIdx]; switch (p.Type) { case GCss::Selector::CombChild: { // LgiAssert(!"Not impl."); return false; break; } case GCss::Selector::CombAdjacent: { // LgiAssert(!"Not impl."); return false; break; } case GCss::Selector::CombDesc: { // Does the parent match the previous simple selector ssize_t PrevIdx = StartIdx - 1; while (PrevIdx > 0 && Sel->Parts[PrevIdx-1].IsSel()) { PrevIdx--; } bool ParMatch = false; for (; !ParMatch && CurrentParent; CurrentParent = Context->GetParent(CurrentParent)) { ParMatch = MatchSimpleSelector(Sel, PrevIdx, Context, CurrentParent); } if (!ParMatch) return false; break; } default: { LgiAssert(!"This must be a comb."); return false; break; } } } } return Match; } // This stores the unparsed style strings. More than one selector // may reference this memory. GArray Styles; // Sort the styles into less specific to more specific order void SortStyles(GCss::SelArray &Styles); public: SelectorMap TypeMap, ClassMap, IdMap; SelArray Other; GString Error; ~Store() { Empty(); } /// Empty the data store void Empty() { TypeMap.Empty(); ClassMap.Empty(); IdMap.Empty(); Other.DeleteObjects(); Error.Empty(); Styles.DeleteArrays(); } /// Parse general CSS into selectors. bool Parse(const char *&s, int Depth = 0); /// Converts store back into string form bool ToString(GStream &p); /// Use to finding matching selectors for an element. template bool Match(GCss::SelArray &Styles, ElementCallback *Context, T *Obj) { SelArray *s; if (!Context || !Obj) return false; // An array of potential selector matches... GArray Maps; // Check element type const char *Type = Context->GetElement(Obj); if (Type && (s = TypeMap.Find(Type))) Maps.Add(s); // Check the ID const char *Id = Context->GetAttr(Obj, "id"); if (Id && (s = IdMap.Find(Id))) Maps.Add(s); // Check all the classes GString::Array Classes; if (Context->GetClasses(Classes, Obj)) { for (unsigned i=0; iLength(); i++) { GCss::Selector *Sel = (*s)[i]; if (!Styles.HasItem(Sel) && MatchFullSelector(Sel, Context, Obj)) { // Output the matching selector Styles.Add(Sel); } } } // Sort the selectors into less specific -> more specific order. SortStyles(Styles); return true; } /// For debugging: dumps a description of the store to a stream bool Dump(GStream &out); }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// GCss(); GCss(const GCss &c); virtual ~GCss(); #define Accessor(PropName, Type, Default, BaseProp) \ Type PropName() { Type *Member = (Type*)Props.Find(Prop##PropName); \ if (Member) return *Member; \ else if ((Member = (Type*)Props.Find(BaseProp))) return *Member; \ return Default; } \ void PropName(Type t) { Type *Member = (Type*)Props.Find(Prop##PropName); \ if (Member) *Member = t; \ else { Props.Add(Prop##PropName, Member = new Type); \ *Member = t; } \ OnChange(Prop##PropName); } Accessor(Display, DisplayType, DispInherit, PropNull); Accessor(Float, FloatType, FloatInherit, PropNull); Accessor(Position, PositionType, PosInherit, PropNull); Accessor(ZIndex, Len, Len(), PropNull); Accessor(TextAlign, Len, Len(), PropNull); Accessor(VerticalAlign, Len, Len(), PropNull); Accessor(Width, Len, Len(), PropNull); Accessor(MinWidth, Len, Len(), PropNull); Accessor(MaxWidth, Len, Len(), PropNull); Accessor(Height, Len, Len(), PropNull); Accessor(MinHeight, Len, Len(), PropNull); Accessor(MaxHeight, Len, Len(), PropNull); Accessor(LineHeight, Len, Len(), PropNull); Accessor(Top, Len, Len(), PropNull); Accessor(Right, Len, Len(), PropNull); Accessor(Bottom, Len, Len(), PropNull); Accessor(Left, Len, Len(), PropNull); Accessor(Margin, Len, Len(), PropNull); Accessor(MarginTop, Len, Len(), PropNull); Accessor(MarginRight, Len, Len(), PropNull); Accessor(MarginBottom, Len, Len(), PropNull); Accessor(MarginLeft, Len, Len(), PropNull); Accessor(Padding, Len, Len(), PropNull); Accessor(PaddingTop, Len, Len(), PropPadding); Accessor(PaddingRight, Len, Len(), PropPadding); Accessor(PaddingBottom, Len, Len(), PropPadding); Accessor(PaddingLeft, Len, Len(), PropPadding); Accessor(Border, BorderDef, BorderDef(), PropNull); Accessor(BorderTop, BorderDef, BorderDef(), PropBorder); Accessor(BorderRight, BorderDef, BorderDef(), PropBorder); Accessor(BorderBottom, BorderDef, BorderDef(), PropBorder); Accessor(BorderLeft, BorderDef, BorderDef(), PropBorder); Accessor(BorderSpacing, Len, Len(), PropNull); // 'cellspacing' Accessor(BorderRadius, Len, Len(), PropNull); Accessor(BorderCollapse, BorderCollapseType, CollapseInherit, PropBorderCollapse); Accessor(_CellPadding, Len, Len(), PropNull); // 'cellpadding' (not CSS) Accessor(Overflow, OverflowType, OverflowInherit, PropNull); Accessor(Clip, GRect, GRect(0, 0, -1, -1), PropNull); Accessor(XSubRect, GRect, GRect(0, 0, -1, -1), PropNull); Accessor(Visibility, VisibilityType, VisibilityInherit, PropNull); Accessor(ListStyleType, ListStyleTypes, ListInherit, PropNull); Accessor(FontFamily, StringsDef, StringsDef(), PropNull); Accessor(FontSize, Len, Len(), PropNull); Accessor(FontStyle, FontStyleType, FontStyleInherit, PropNull); Accessor(FontVariant, FontVariantType, FontVariantInherit, PropNull); Accessor(FontWeight, FontWeightType, FontWeightInherit, PropNull); Accessor(TextDecoration, TextDecorType, TextDecorInherit, PropNull); Accessor(WordWrap, WordWrapType, WrapNormal, PropNull); Accessor(Color, ColorDef, ColorDef(), PropNull); Accessor(NoPaintColor, ColorDef, ColorDef(), PropNull); Accessor(BackgroundColor, ColorDef, ColorDef(), PropNull); Accessor(BackgroundImage, ImageDef, ImageDef(), PropNull); Accessor(BackgroundRepeat, RepeatType, RepeatInherit, PropNull); Accessor(BackgroundAttachment, AttachmentType, AttachmentInherit, PropNull); Accessor(BackgroundX, Len, Len(), PropNull); Accessor(BackgroundY, Len, Len(), PropNull); Accessor(BackgroundPos, Len, Len(), PropNull); void Empty(); void DeleteProp(PropType p); size_t Length() { return Props.Length(); } virtual void OnChange(PropType Prop); bool CopyStyle(const GCss &c); bool operator ==(GCss &c); bool operator !=(GCss &c) { return !(*this == c); } GCss &operator =(const GCss &c) { Empty(); CopyStyle(c); return *this; } GCss &operator +=(const GCss &c) { CopyStyle(c); return *this; } GCss &operator -=(const GCss &c); void *PropAddress(PropType p) { return Props.Find(p); } GAutoString ToString(); const char *ToString(DisplayType dt); bool HasFontStyle(); void FontBold(bool b) { FontWeight(b ? FontWeightBold : FontWeightNormal); } // Parsing virtual bool Parse(const char *&Defs, ParsingStyle Type = ParseStrict); bool ParseDisplayType(const char *&s); void ParsePositionType(const char *&s); bool ParseFontStyle(PropType p, const char *&s); bool ParseFontVariant(PropType p, const char *&s); bool ParseFontWeight(PropType p, const char *&s); bool ParseBackgroundRepeat(const char *&s); template T *GetOrCreate(T *&ptr, PropType PropId) { ptr = (T*)Props.Find(PropId); if (!ptr) Props.Add(PropId, ptr = new T); return ptr; } // Inheritance calculation typedef GArray PropArray; typedef LHashTbl, PropArray*> PropMap; /// Copies valid properties from the node 'c' into the property collection 'Contrib'. /// Usually called for each node up the parent chain until the function returns false; bool InheritCollect(GCss &c, PropMap &Contrib); /// After calling InheritCollect on all the parent nodes, this method works out the final /// value of each property. e.g. multiplying percentages together etc. bool InheritResolve(PropMap &Map); /* Code sample: GCss::PropMap Map; Map.Add(PropFontFamily, new GCss::PropArray); Map.Add(PropFontSize, new GCss::PropArray); Map.Add(PropFontStyle, new GCss::PropArray); for (GTag *t = Parent; t; t = t->Parent) { if (!c.InheritCollect(*t, Map)) break; } GCss c; // Container for final values c.InheritResolve(Map); Map.DeleteObjects(); */ protected: inline void DeleteProp(PropType p, void *Ptr); LHashTbl, void*> Props; static LHashTbl, PropType> Lut; static LHashTbl, PropType> ParentProp; static const char *PropName(PropType p); virtual bool OnUnhandledColor(ColorDef *def, const char *&s) { return false; } template void StoreProp(PropType id, T *obj, bool own) { T *e = (T*)Props.Find(id); if (e) { *e = *obj; if (own) delete obj; } else if (own) { Props.Add(id, obj); } else { Props.Add(id, new T(*obj)); } } }; #ifndef LINUX #pragma pack(pop) #endif #endif \ No newline at end of file diff --git a/include/common/GCssTools.h b/include/common/GCssTools.h --- a/include/common/GCssTools.h +++ b/include/common/GCssTools.h @@ -1,76 +1,76 @@ #ifndef _GCSSTOOLS_H_ #define _GCSSTOOLS_H_ #include "GCss.h" class LgiClass GCssTools { GView *View; GCss *Css; GFont *Font; GColour Fore, Back; - uint8 ForeInit : 1, BackInit : 1; + uint8_t ForeInit : 1, BackInit : 1; bool SetLineStyle(GSurface *pDC, GCss::BorderDef &d); public: GCssTools(GCss *css, GFont *font) { View = NULL; Css = css; Font = font; ForeInit = 0; BackInit = 0; } GCssTools(GView *view) { View = view; Css = view->GetCss(true); Font = view->GetFont(); ForeInit = 0; BackInit = 0; } /// Gets the foreground colour for text GColour &GetFore(GColour *Default = NULL); /// Gets the background colour for filling GColour &GetBack(GColour *Default = NULL); /// Applies the margin to a rectangle GRect ApplyMargin(GRect &in); /// \returns the border in Px GRect GetBorder(GRect &box); /// Applies the border to a rectangle GRect ApplyBorder(GRect &in); /// \returns the padding in Px GRect GetPadding(GRect &box); /// Applies the padding to a rectangle GRect ApplyPadding(GRect &in); /// Paint the CSS border /// \returns the content area within the borders GRect PaintBorder(GSurface *pDC, GRect &in); /// Paint the CSS padding /// \returns the content area within the padding GRect PaintPadding(GSurface *pDC, GRect &in); GRect PaintBorderAndPadding(GSurface *pDC, GRect &in) { GRect r = PaintBorder(pDC, in); r = PaintPadding(pDC, r); return r; } /// Paint the content area void PaintContent(GSurface *pDC, GRect &in, const char *utf8, GSurface *img = NULL); }; #endif \ No newline at end of file diff --git a/include/common/GDisplayString.h b/include/common/GDisplayString.h --- a/include/common/GDisplayString.h +++ b/include/common/GDisplayString.h @@ -1,282 +1,282 @@ #ifndef _GDISPLAY_STRING_H_ #define _GDISPLAY_STRING_H_ #include "GFont.h" #if defined(LGI_SDL) #elif defined(LINUX) namespace Pango { #include "pango/pango.h" #include "pango/pangocairo.h" } #endif #define LGI_DSP_STR_CACHE 1 /// \brief Cache for text measuring, glyph substitution and painting /// /// To paint text onto the screen several stages need to be implemented to /// properly support unicode on multiple platforms. This class addresses all /// of those needs and then allows you to cache the results to reduce text /// related workload. /// /// The first stage is converting text into the native format for the /// OS's API. This usually involved converting the text to wide characters for /// Linux or Windows, or Utf-8 for BeOS. Then the text is converted into runs of /// characters that can be rendered in the same font. If glyph substitution is /// required to render the characters a separate run is used with a different /// font ID. Finally you can measure or paint these runs of text. Also tab characters /// are expanded to the current tab size setting. class LgiClass GDisplayString { protected: GSurface *pDC; OsChar *Str; GFont *Font; - uint32 x, y; + uint32_t x, y; ssize_t len; int xf, yf; int DrawOffsetF; // Flags - uint8 LaidOut : 1; - uint8 AppendDots : 1; - uint8 VisibleTab : 1; + uint8_t LaidOut : 1; + uint8_t AppendDots : 1; + uint8_t VisibleTab : 1; #if LGI_DSP_STR_CACHE // Wide char cache GAutoWString StrCache; #endif #if defined(LGI_SDL) GAutoPtr Img; #elif defined(MAC) #if USE_CORETEXT CTLineRef Hnd; CFAttributedStringRef AttrStr; void CreateAttrStr(); #else ATSUTextLayout Hnd; ATSUTextMeasurement fAscent; ATSUTextMeasurement fDescent; #endif #elif defined(__GTK_H__) Gtk::PangoLayout *Hnd; int LastTabOffset; void UpdateTabs(int Offset, int Size, bool Debug = false); #elif defined(WINNATIVE) || defined(BEOS) class CharInfo { public: OsChar *Str; int Len; int X; - uint8 FontId; + uint8_t FontId; int8 SizeDelta; CharInfo() { Str = 0; Len = 0; X = 0; FontId = 0; SizeDelta = 0; } - uint32 First() + uint32_t First() { auto s = (const uint16*)Str; ssize_t l = Len * sizeof(*Str); return LgiUtf16To32(s, l); } }; GArray Info; #endif void Layout(bool Debug = false); void DrawWhiteSpace(GSurface *pDC, char Ch, GRect &r); public: /// Constructor GDisplayString ( /// The base font. Must not be destroyed during the lifetime of this object. GFont *f, /// Utf-8 input string const char *s, /// Number of bytes in the input string or -1 for NULL terminated. ssize_t l = -1, GSurface *pdc = 0 ); /// Constructor GDisplayString ( /// The base font. Must not be destroyed during the lifetime of this object. GFont *f, /// A wide character input string const char16 *s, /// The number of characters in the input string (NOT the number of bytes) or -1 for NULL terminated ssize_t l = -1, GSurface *pdc = 0 ); #ifdef _MSC_VER /// Constructor GDisplayString ( /// The base font. Must not be destroyed during the lifetime of this object. GFont *f, /// A wide character input string - const uint32 *s, + const uint32_t *s, /// The number of characters in the input string (NOT the number of bytes) or -1 for NULL terminated ssize_t l = -1, GSurface *pdc = 0 ); #endif virtual ~GDisplayString(); GDisplayString &operator=(const GDisplayString &s) { LgiAssert(0); return *this; } /// \returns the draw offset used to calculate tab spacing (in Px) int GetDrawOffset(); /// Sets the draw offset used to calculate tab spacing (in Px) void SetDrawOffset(int Px); /// Returns the ShowVisibleTab setting. /// Treats Unicode-2192 (left arrow) as a tab char bool ShowVisibleTab(); /// Sets the ShowVisibleTab setting. /// Treats Unicode-2192 (left arrow) as a tab char void ShowVisibleTab(bool i); /// \returns the font used to create the layout GFont *GetFont() { return Font; }; /// Fits string into 'width' pixels, truncating with '...' if it's not going to fit void TruncateWithDots ( /// The number of pixels the string has to fit int Width ); /// Returns true if the string is trucated bool IsTruncated(); /// Returns the chars in the OsChar string ssize_t Length(); /// Sets the number of chars in the OsChar string void Length(int NewLen); /// Returns the pointer to the native string operator const char16*() { #ifdef LGI_DSP_STR_CACHE return StrCache; #else return Str; #endif } // API that use full pixel sizes: /// Returns the width of the whole string int X(); /// Returns the height of the whole string int Y(); /// Returns the width and height of the whole string GdcPt2 Size(); /// Returns the number of characters that fit in 'x' pixels. ssize_t CharAt(int x, LgiPxToIndexType Type = LgiTruncate); /// Draws the string onto a device surface void Draw ( /// The output device GSurface *pDC, /// The x coordinate of the top left corner of the output box int x, /// The y coordinate of the top left corner of the output box int y, /// An optional clipping rectangle. If the font is not transparent this rectangle will be filled with the background colour. GRect *r = NULL, /// Turn on debug logging bool Debug = false ); /// Draws the string onto a device surface void DrawCenter ( /// The output device GSurface *pDC, /// An rectangle to center in. If the font is not transparent this rectangle will be filled with the background colour. GRect *r ) { if (r) Draw(pDC, r->x1 + ((r->X() - X()) >> 1), r->y1 + ((r->Y() - Y()) >> 1), r); } // API that uses fractional pixel sizes. enum { #if defined LGI_SDL FShift = 6, FScale = 1 << FShift, #elif defined __GTK_H__ /// This is the value for 1 pixel. FScale = PANGO_SCALE, /// This is bitwise shift to convert between integer and fractional FShift = 10 #elif defined MAC /// This is the value for 1 pixel. FScale = 0x10000, /// This is bitwise shift to convert between integer and fractional FShift = 16 #else /// This is the value for 1 pixel. FScale = 1, /// This is bitwise shift to convert between integer and fractional FShift = 0 #endif }; /// \returns the draw offset used to calculate tab spacing (in fractional units) int GetDrawOffsetF(); /// Sets the draw offset used to calculate tab spacing (in fractional units) void SetDrawOffsetF(int Fpx); /// \returns the fractional width of the string int FX(); /// \returns the fractional height of the string int FY(); /// \returns both the fractional width and height of the string GdcPt2 FSize(); /// Draws the string using fractional co-ordinates. void FDraw ( /// The output device GSurface *pDC, /// The fractional x coordinate of the top left corner of the output box int fx, /// The fractional y coordinate of the top left corner of the output box int fy, /// [Optional] fractional clipping rectangle. If the font is not transparent /// this rectangle will be filled with the background colour. GRect *frc = NULL, /// [Optional] print debug info bool Debug = false ); }; #endif diff --git a/include/common/GDocView.h b/include/common/GDocView.h --- a/include/common/GDocView.h +++ b/include/common/GDocView.h @@ -1,541 +1,541 @@ /// \file /// \author Matthew Allen (fret@memecode.com) /// \brief This is the base data and code for all the text controls (inc. HTML) #ifndef __GDOCVIEW_H #define __GDOCVIEW_H #include "GVariant.h" #include "GNotifications.h" // Word wrap enum LDocWrapType { /// No word wrapping TEXTED_WRAP_NONE = 0, /// Dynamically wrap line to editor width TEXTED_WRAP_REFLOW = 1, }; // Util macros /// Returns true if 'c' is whitespace #define IsWhiteSpace(c) ((c) < 126 && strchr(GDocView::WhiteSpace, c) != 0) /// Returns true if 'c' is a delimiter #define IsDelimiter(c) ((c) < 126 && strchr(GDocView::Delimiters, c) != 0) /// Returns true if 'c' is a letter or number #define IsText(c) (IsDigit(c) || IsAlpha(c) || (c) == '_') /// Returns true if 'c' is word boundry #define IsWordBoundry(c) (strchr(GDocView::WhiteSpace, c) || strchr(GDocView::Delimiters, c)) /// Returns true if 'c' is alphanumeric or a digit #define AlphaOrDigit(c) (IsDigit(c) || IsAlpha(c)) /// Returns true if 'c' is a valid URL character #define UrlChar(c) ( \ strchr(GDocView::UrlDelim, (c)) || \ AlphaOrDigit((c)) || \ ((c) >= 256) \ ) /// Returns true if 'c' is email address character #define EmailChar(c) (strchr("._-:+", (c)) || AlphaOrDigit((c))) extern char16 *ConvertToCrLf(char16 *Text); /// This class contains infomation about a link. /// \sa LgiDetectLinks struct GLinkInfo { NativeInt Start; NativeInt Len; bool Email; void Set(NativeInt start, NativeInt len, bool email) { Start = start; Len = len; Email = email; } }; // Call back class to handle viewer events class GDocView; /// An environment class to handle requests from the text view to the outside world. class LgiClass GDocumentEnv : public LThreadOwner { GArray Viewers; public: GDocumentEnv(GDocView *v = 0); virtual ~GDocumentEnv(); enum LoadType { LoadError, LoadNotImpl, LoadImmediate, LoadDeferred, }; struct #ifdef MAC LgiClass #endif LoadJob : public LThreadJob { enum PrefFormat { FmtNone, FmtStream, FmtSurface, FmtFilename, }; enum JobStatus { JobInit, JobOk, JobErr_Uri, JobErr_Path, JobErr_FileOpen, JobErr_GetUri, JobErr_NoCachedFile, JobErr_ImageFilter, JobErr_NoMem, }; // View data GDocumentEnv *Env; void *UserData; - uint32 UserUid; + uint32_t UserUid; PrefFormat Pref; // Input data GAutoString Uri; GAutoString PostData; // Output data GAutoPtr Stream; GAutoPtr pDC; GString Filename; GString Error; JobStatus Status; GString MimeType, ContentId; LoadJob(LThreadTarget *o) : LThreadJob(o) { Env = NULL; UserUid = 0; UserData = NULL; Pref = FmtNone; Status = JobInit; } GStreamI *GetStream() { if (!Stream && Filename) { GFile *file = new GFile; if (file && file->Open(Filename, O_READ)) Stream.Reset(file); else DeleteObj(file); } return Stream; } }; LoadJob *NewJob() { return new LoadJob(this); } bool AttachView(GDocView *v) { if (!v) return false; if (!Lock(_FL)) return false; LgiAssert(!Viewers.HasItem(v)); Viewers.Add(v); Unlock(); return true; } bool DetachView(GDocView *v) { if (!v) return false; if (!Lock(_FL)) return false; LgiAssert(Viewers.HasItem(v)); Viewers.Delete(v); Unlock(); return true; } int NextUid(); /// Creating a context menu, usually when the user right clicks on the /// document. virtual bool AppendItems(GSubMenu *Menu, int Base = 1000) { return false; } /// Do something when the menu items created by GDocumentEnv::AppendItems /// are clicked. virtual bool OnMenu(GDocView *View, int Id, void *Context) { return false; } /// Asks the env to get some data linked from the document, e.g. a css file or an iframe source etc. /// If the GetContent implementation takes ownership of the job pointer then it should set 'j' to NULL. virtual LoadType GetContent(LoadJob *&j) { return LoadNotImpl; } /// After the env's thread loads the resource it calls this to pass it to the doc void OnDone(GAutoPtr j); /// Handle a click on URI virtual bool OnNavigate(GDocView *Parent, const char *Uri) { return false; } /// Handle a form post virtual bool OnPostForm(GDocView *Parent, const char *Uri, const char *Data) { return false; } /// Process dynamic content, returning a dynamically allocated string /// for the result of the executed script. Dynamic content is enclosed /// between <? and ?>. virtual char *OnDynamicContent(GDocView *Parent, const char *Code) { return 0; } /// Some script was received, the owner should compile it virtual bool OnCompileScript(GDocView *Parent, char *Script, const char *Language, const char *MimeType) { return false; } /// Some script needs to be executed, the owner should compile it virtual bool OnExecuteScript(GDocView *Parent, char *Script) { return false; } }; /// Default text view environment /// /// This class defines the default behaviour of the environment, /// However you will need to instantiate this yourself and call /// SetEnv with your instance. i.e. it's not automatic. class LgiClass GDefaultDocumentEnv : public GDocumentEnv { public: LoadType GetContent(LoadJob *&j); bool OnNavigate(GDocView *Parent, const char *Uri); }; /// Find params class GDocFindReplaceParams { public: virtual ~GDocFindReplaceParams() {} }; /// TextView class is a base for all text controls class LgiClass GDocView : public GLayout, virtual public GDom { friend class GDocumentEnv; protected: GDocumentEnv *Environment; GString Charset; public: // Static static const char *WhiteSpace; static const char *Delimiters; static const char *UrlDelim; /////////////////////////////////////////////////////////////////////// // Properties #define _TvMenuProp(Type, Name) \ protected: \ Type Name; \ public: \ virtual void Set##Name(Type i) { Name=i; } \ Type Get##Name() { return Name; } _TvMenuProp(uint16, WrapAtCol) _TvMenuProp(bool, UrlDetect) _TvMenuProp(bool, ReadOnly) _TvMenuProp(LDocWrapType, WrapType) - _TvMenuProp(uint8, TabSize) - _TvMenuProp(uint8, IndentSize) + _TvMenuProp(uint8_t, TabSize) + _TvMenuProp(uint8_t, IndentSize) _TvMenuProp(bool, HardTabs) _TvMenuProp(bool, ShowWhiteSpace) _TvMenuProp(bool, ObscurePassword) _TvMenuProp(bool, CrLf) _TvMenuProp(bool, AutoIndent) _TvMenuProp(bool, FixedWidthFont) _TvMenuProp(bool, LoadImages) _TvMenuProp(bool, OverideDocCharset) // This UID is used to match data load events with their source document. // Sometimes data will arrive after the document that asked for it has // already been unloaded. So by assigned each document an UID we can check // the job UID against it and discard old data. _TvMenuProp(int, DocumentUid) #undef _TvMenuProp virtual const char *GetCharset() { return Charset.Get() ? Charset.Get() : "utf-8"; } virtual void SetCharset(const char *s) { Charset = s; } virtual const char *GetMimeType() = 0; /////////////////////////////////////////////////////////////////////// // Object GDocView(GDocumentEnv *e = 0) { WrapAtCol = 0; UrlDetect = true; ReadOnly = false; WrapType = TEXTED_WRAP_REFLOW; TabSize = 4; IndentSize = 4; HardTabs = true; ShowWhiteSpace = false; ObscurePassword = false; CrLf = false; AutoIndent = true; FixedWidthFont = false; LoadImages = false; OverideDocCharset = false; Environment = 0; SetEnv(e); } virtual ~GDocView() { SetEnv(0); } const char *GetClass() { return "GDocView"; } /// Open a file handler virtual bool Open(const char *Name, const char *Cs = 0) { return false; } /// Save a file handler virtual bool Save(const char *Name, const char *Cs = 0) { return false; } /////////////////////////////////////////////////////////////////////// /// Find window handler virtual bool DoFind() { return false; } /// Replace window handler virtual bool DoReplace() { return false; } virtual GDocFindReplaceParams *CreateFindReplaceParams() { return 0; } virtual void SetFindReplaceParams(GDocFindReplaceParams *Params) { } /////////////////////////////////////////////////////////////////////// /// Get the current environment virtual GDocumentEnv *GetEnv() { return Environment; } /// Set the current environment virtual void SetEnv(GDocumentEnv *e) { if (Environment) Environment->DetachView(this); Environment = e; if (Environment) Environment->AttachView(this); } /// When the env has loaded a resource it can pass it to the doc control via this method. /// It MUST be thread safe. Often an environment will call this function directly from /// it's worker thread. virtual void OnContent(GDocumentEnv::LoadJob *Res) {} /////////////////////////////////////////////////////////////////////// // State / Selection /// Set the cursor position, to select an area, move the cursor with Select=false /// then set the other end of the region with Select=true. virtual void SetCaret(size_t i, bool Select, bool ForceFullUpdate = false) {} /// Cursor=false means the other end of the selection if any. The cursor is alwasy /// at one end of the selection. virtual ssize_t GetCaret(bool Cursor = true) { return 0; } /// True if there is a selection virtual bool HasSelection() { return false; } /// Unselect all the text virtual void UnSelectAll() {} /// Select the word from index 'From' virtual void SelectWord(size_t From) {} /// Select all the text in the control virtual void SelectAll() {} /// Get the selection as a dynamicially allocated utf-8 string virtual char *GetSelection() { return 0; } /// Returns the character index at the x,y location virtual ssize_t IndexAt(int x, int y) { return 0; } /// Index=-1 returns the x,y of the cursor, Index >=0 returns the specified x,y virtual bool GetLineColumnAtIndex(GdcPt2 &Pt, ssize_t Index = -1) { return false; } /// True if the document has changed virtual bool IsDirty() { return false; } /// Gets the number of lines of text virtual size_t GetLines() { return 0; } /// Gets the pixels required to display all the text virtual void GetTextExtent(int &x, int &y) {} /////////////////////////////////////////////////////////////////////// /// Cuts the selection from the document and puts it on the clipboard virtual bool Cut() { return false; } /// Copies the selection from the document to the clipboard virtual bool Copy() { return false; } /// Pastes the current contents of the clipboard into the document virtual bool Paste() { return false; } /////////////////////////////////////////////////////////////////////// /// Called when the user hits the escape key virtual void OnEscape(GKey &K) {} /// Called when the user hits the enter key virtual void OnEnter(GKey &k) {} /// Called when the user clicks a URL virtual void OnUrl(char *Url) {} /// Called to add styling to the document virtual void OnAddStyle(const char *MimeType, const char *Styles) {} /////////////////////////////////////////////////////////////////////// struct ContentMedia { GString Id; GString FileName; GString MimeType; GVariant Data; GAutoPtr Stream; bool Valid() { return MimeType.Get() != NULL && FileName.Get() != NULL && ( (Data.Type == GV_BINARY && Data.Value.Binary.Data != NULL) || (Stream.Get() != NULL) ); } }; /// Gets the document in format of a desired MIME type virtual bool GetFormattedContent ( /// [In] The desired mime type of the content const char *MimeType, /// [Out] The content in the specified mime type GString &Out, /// [Out/Optional] Any attached media files that the content references GArray *Media = NULL ) { return false; } }; /// Detects links in text, returning their location and type template bool LgiDetectLinks(GArray &Links, T *Text, ssize_t TextCharLen = -1) { if (!Text) return false; if (TextCharLen < 0) TextCharLen = Strlen(Text); T *End = Text + TextCharLen; static T Http[] = {'h', 't', 't', 'p', ':', '/', '/', 0 }; static T Https[] = {'h', 't', 't', 'p', 's', ':', '/', '/', 0}; for (int64 i=0; i= 7 && ( Strnicmp(Text+i, Http, 6) == 0 || Strnicmp(Text+i, Https, 7) == 0 ) ) { // find end T *s = Text + i; T *e = s + 6; for ( ; e < End && UrlChar(*e); e++) ; while ( e > s && ! ( IsAlpha(e[-1]) || IsDigit(e[-1]) || e[-1] == '/' ) ) e--; Links.New().Set(s - Text, e - s, false); i = e - Text; } break; } case '@': { // find start T *s = Text + (MAX(i, 1) - 1); for ( ; s > Text && EmailChar(*s); s--) ; if (s < Text + i) { if (!EmailChar(*s)) s++; bool FoundDot = false; T *Start = Text + i + 1; T *e = Start; for ( ; e < End && EmailChar(*e); e++) { if (*e == '.') FoundDot = true; } while (e > Start && e[-1] == '.') e--; if (FoundDot) { Links.New().Set(s - Text, e - s, true); i = e - Text; } } break; } } } return true; } #endif diff --git a/include/common/GEventTargetThread.h b/include/common/GEventTargetThread.h --- a/include/common/GEventTargetThread.h +++ b/include/common/GEventTargetThread.h @@ -1,435 +1,435 @@ #ifndef _GEVENTTARGETTHREAD_H_ #define _GEVENTTARGETTHREAD_H_ #include "LThread.h" #include "LMutex.h" #include "LThreadEvent.h" #include "LCancel.h" #include "LHashTable.h" #define PostThreadEvent GEventSinkMap::Dispatch.PostEvent class LgiClass GEventSinkMap : public LMutex { protected: LHashTbl,GEventSinkI*> ToPtr; LHashTbl,int> ToHnd; public: static GEventSinkMap Dispatch; GEventSinkMap(int SizeHint = 0) : ToPtr(SizeHint), ToHnd(SizeHint) { } virtual ~GEventSinkMap() { } int AddSink(GEventSinkI *s) { if (!s || !Lock(_FL)) return ToPtr.GetNullKey(); // Find free handle... int Hnd; while (ToPtr.Find(Hnd = LgiRand(10000) + 1)) ; // Add the new sink ToPtr.Add(Hnd, s); ToHnd.Add(s, Hnd); Unlock(); return Hnd; } bool RemoveSink(GEventSinkI *s) { if (!s || !Lock(_FL)) return false; bool Status = false; int Hnd = ToHnd.Find(s); if (Hnd > 0) { Status |= ToHnd.Delete(s); Status &= ToPtr.Delete(Hnd); } else LgiAssert(!"Not a member of this sink."); Unlock(); return Status; } bool RemoveSink(int Hnd) { if (!Hnd || !Lock(_FL)) return false; bool Status = false; void *Ptr = ToPtr.Find(Hnd); if (Ptr) { Status |= ToHnd.Delete(Ptr); Status &= ToPtr.Delete(Hnd); } else LgiAssert(!"Not a member of this sink."); Unlock(); return Status; } bool PostEvent(int Hnd, int Cmd, GMessage::Param a = 0, GMessage::Param b = 0) { if (!Hnd) return false; if (!Lock(_FL)) return false; GEventSinkI *s = (GEventSinkI*)ToPtr.Find(Hnd); bool Status = false; if (s) Status = s->PostEvent(Cmd, a, b); #if _DEBUG else // This is not fatal, but we might want to know about it in DEBUG builds: LgiTrace("%s:%i - Sink associated with handle '%i' doesn't exist.\n", _FL, Hnd); #endif Unlock(); return Status; } bool CancelThread(int Hnd) { if (!Hnd) return false; if (!Lock(_FL)) return false; GEventSinkI *s = (GEventSinkI*)ToPtr.Find(Hnd); bool Status = false; if (s) { LCancel *c = dynamic_cast(s); if (c) { Status = c->Cancel(true); } else { LgiTrace("%s:%i - GEventSinkI is not an LCancel object.\n", _FL); } } Unlock(); return Status; } }; class LgiClass GMappedEventSink : public GEventSinkI { protected: int Handle; GEventSinkMap *Map; public: GMappedEventSink() { Map = NULL; Handle = 0; SetMap(&GEventSinkMap::Dispatch); } virtual ~GMappedEventSink() { SetMap(NULL); } bool SetMap(GEventSinkMap *m) { if (Map) { if (!Map->RemoveSink(this)) return false; Map = 0; Handle = 0; } Map = m; if (Map) { Handle = Map->AddSink(this); return Handle > 0; } return true; } int GetHandle() { return Handle; } }; /// This class is a worker thread that accepts messages on it's GEventSinkI interface. /// To use, sub class and implement the OnEvent handler. class LgiClass GEventTargetThread : public LThread, public LMutex, public GMappedEventSink, public GEventTargetI // Sub-class has to implement OnEvent { GArray Msgs; LThreadEvent Event; bool Loop; // This makes the event name unique on windows to // prevent multiple instances clashing. GString ProcessName(GString obj, const char *desc) { OsProcessId process = LgiGetCurrentProcess(); OsThreadId thread = GetCurrentThreadId(); GString s; s.Printf("%s.%s.%i.%i", obj.Get(), desc, process, thread); return s; } protected: int PostTimeout; size_t Processing; - uint32 TimerMs; // Milliseconds between timer ticks. + uint32_t TimerMs; // Milliseconds between timer ticks. uint64 TimerTs; // Time for next tick public: GEventTargetThread(GString Name) : LThread(Name + ".Thread"), LMutex(Name + ".Mutex"), Event(ProcessName(Name, "Event")) { Loop = true; PostTimeout = 1000; Processing = 0; TimerMs = 0; TimerTs = 0; Run(); } virtual ~GEventTargetThread() { EndThread(); } /// Set or clear a timer. Every time the timer expires, the function /// OnPulse is called. Until SetPulse() is called. - bool SetPulse(uint32 Ms = 0) + bool SetPulse(uint32_t Ms = 0) { TimerMs = Ms; TimerTs = Ms ? LgiCurrentTime() + Ms : 0; return Event.Signal(); } /// Called roughly every 'TimerMs' milliseconds. /// Be aware however that OnPulse is called from the worker thread, not your main /// GUI thread. So best to send a message or something thread safe. virtual void OnPulse() {} void EndThread() { if (Loop) { // We can't be locked here, because GEventTargetThread::Main needs // to lock to check for messages... Loop = false; if (GetCurrentThreadId() == LThread::GetId()) { // Being called from within the thread, in which case we can't signal // the event because we'll be stuck in this loop and not waitin on it. #ifdef _DEBUG LgiTrace("%s:%i - EndThread called from inside thread.\n", _FL); #endif } else { Event.Signal(); uint64 Start = LgiCurrentTime(); while (!IsExited()) { LgiSleep(10); uint64 Now = LgiCurrentTime(); if (Now - Start > 2000) { #ifdef LINUX int val = 1111; int r = sem_getvalue(Event.Handle(), &val); printf("%s:%i - EndThread() hung waiting for %s to exit (caller.thread=%i, worker.thread=%i, event=%i, r=%i, val=%i).\n", _FL, LThread::GetName(), GetCurrentThreadId(), GetId(), Event.Handle(), r, val); #else printf("%s:%i - EndThread() hung waiting for %s to exit (caller.thread=0x%x, worker.thread=0x%x, event=%p).\n", _FL, LThread::GetName(), (int)GetCurrentThreadId(), (int)GetId(), (void*)(ssize_t)Event.Handle()); #endif Start = Now; } } } } } size_t GetQueueSize() { return Msgs.Length() + Processing; } bool PostEvent(int Cmd, GMessage::Param a = 0, GMessage::Param b = 0) { if (!Loop) return false; if (!Lock(_FL)) return false; Msgs.Add(new GMessage(Cmd, a, b)); Unlock(); return Event.Signal(); } int Main() { while (Loop) { int WaitLength = -1; if (TimerTs != 0) { uint64 Now = LgiCurrentTime(); if (TimerTs > Now) { WaitLength = (int) (TimerTs - Now); } else { OnPulse(); if (TimerMs) { TimerTs = Now + TimerMs; WaitLength = (int) TimerTs; } else WaitLength = -1; } } LThreadEvent::WaitStatus s = Event.Wait(WaitLength); if (s == LThreadEvent::WaitSignaled) { GArray m; if (Lock(_FL)) { if (Msgs.Length()) { m = Msgs; Msgs.Length(0); } Unlock(); } Processing = m.Length(); for (unsigned i=0; Loop && i < m.Length(); i++) { Processing--; OnEvent(m[i]); } m.DeleteObjects(); } else if (s == LThreadEvent::WaitError) { LgiTrace("%s:%i - Event.Wait failed.\n", _FL); break; } } return 0; } template bool PostObject(int Hnd, int Cmd, GAutoPtr A) { uint64 Start = LgiCurrentTime(); bool Status; while (!(Status = GEventSinkMap::Dispatch.PostEvent(Hnd, Cmd, (GMessage::Param) A.Get()))) { LgiSleep(2); if (LgiCurrentTime() - Start >= PostTimeout) break; } if (Status) A.Release(); return Status; } template bool PostObject(int Hnd, int Cmd, GMessage::Param A, GAutoPtr B) { uint64 Start = LgiCurrentTime(); bool Status; while (!(Status = GEventSinkMap::Dispatch.PostEvent(Hnd, Cmd, A, (GMessage::Param) B.Get()))) { LgiSleep(2); if (LgiCurrentTime() - Start >= PostTimeout) break; } if (Status) B.Release(); return Status; } template bool PostObject(int Hnd, int Cmd, GAutoPtr A, GAutoPtr B) { uint64 Start = LgiCurrentTime(); bool Status; while (!(Status = GEventSinkMap::Dispatch.PostEvent(Hnd, Cmd, (GMessage::Param) A.Get(), (GMessage::Param) B.Get()))) { LgiSleep(2); if (LgiCurrentTime() - Start >= PostTimeout) break; } if (Status) { A.Release(); B.Release(); } return Status; } template bool ReceiveA(GAutoPtr &Obj, GMessage *m) { return Obj.Reset((T*)m->A()); } template bool ReceiveB(GAutoPtr &Obj, GMessage *m) { return Obj.Reset((T*)m->B()); } }; #endif diff --git a/include/common/GFile.h b/include/common/GFile.h --- a/include/common/GFile.h +++ b/include/common/GFile.h @@ -1,666 +1,666 @@ /** \file \author Matthew Allen \date 24/5/2002 \brief Common file system header Copyright (C) 1995-2002, Matthew Allen */ #ifndef __FILE_H #define __FILE_H #include #include "GMem.h" #include "LgiClass.h" #include "GStream.h" #include "GArray.h" #include "GRefCount.h" #include "GStringClass.h" #include "LError.h" #ifdef WIN32 typedef HANDLE OsFile; #define INVALID_HANDLE INVALID_HANDLE_VALUE #define ValidHandle(hnd) ((hnd) != INVALID_HANDLE_VALUE) #define DIR_PATH_SIZE 512 #define O_READ GENERIC_READ #define O_WRITE GENERIC_WRITE #define O_READWRITE (GENERIC_READ | GENERIC_WRITE) #define O_SHARE 0x01000000 #define O_NO_CACHE 0x00800000 #else #include #include #include #include typedef int OsFile; #define INVALID_HANDLE -1 #define ValidHandle(hnd) ((hnd) >= 0) #define O_READ O_RDONLY #define O_WRITE O_WRONLY #ifdef MAC #define O_SHARE O_SHLOCK #else #define O_SHARE 0 #endif #define O_READWRITE O_RDWR #endif ///////////////////////////////////////////////////////////////////// // Defines #define FileDev (GFileSystem::GetInstance()) #define MAX_PATH 260 // File system types (used by GDirectory and GVolume) #define VT_NONE 0 #define VT_3_5FLOPPY 1 #define VT_5_25FLOPPY 2 #define VT_HARDDISK 3 #define VT_CDROM 4 #define VT_RAMDISK 5 #define VT_REMOVABLE 6 #define VT_FOLDER 7 #define VT_FILE 8 #define VT_DESKTOP 9 #define VT_NETWORK_NEIGHBOURHOOD 10 #define VT_NETWORK_MACHINE 11 #define VT_NETWORK_SHARE 12 #define VT_NETWORK_PRINTER 13 #define VT_NETWORK_GROUP 14 // e.g. workgroup #define VT_MAX 15 // Volume attributes #define VA_CASE_SENSITIVE 0x0001 #define VA_CASE_PRESERVED 0x0002 #define VA_UNICODE_ON_DISK 0x0004 #define VA_LFN_API 0x4000 #define VA_COMPRESSED 0x8000 // File attributes #define FA_NORMAL 0x0000 #define FA_READONLY 0x0001 #define FA_HIDDEN 0x0002 #define FA_SYSTEM 0x0004 #define FA_VOLUME 0x0008 #define FA_DIRECTORY 0x0010 #define FA_ARCHIVE 0x0020 #define FA_COMPUTER 0x0040 ///////////////////////////////////////////////////////////////////// // Abstract classes /// Generic directory iterator class LgiClass GDirectory { struct GDirectoryPriv *d; public: GDirectory(); virtual ~GDirectory(); /// \brief Starts the search. The entries '.' and '..' are never returned. /// The default pattern returns all files. /// \return Non zero on success virtual int First ( /// The path of the directory const char *Name, /// The pattern to match files against. /// \sa The default LGI_ALL_FILES matchs all files. const char *Pattern = LGI_ALL_FILES ); /// \brief Get the next match /// \return Non zero on success virtual int Next(); /// \brief Finish the search /// \return Non zero on success virtual int Close(); /// \brief Constructs the full path of the current directory entry /// \return Non zero on success virtual bool Path ( // The buffer to write to char *s, // The size of the output buffer in bytes int BufSize ) const; virtual const char *FullPath(); /// Gets the current entries attributes (platform specific) virtual long GetAttributes() const; /// Gets the name of the current entry. (Doesn't include the path). virtual char *GetName() const; virtual GString FileName() const; /// Gets the user id of the current entry. (Doesn't have any meaning on Win32). virtual int GetUser ( /// If true gets the group id instead of the user id. bool Group ) const; /// Gets the entries creation time. You can convert this to an easy to read for using LDateTime. virtual uint64 GetCreationTime() const; /// Gets the entries last access time. You can convert this to an easy to read for using LDateTime. virtual uint64 GetLastAccessTime() const; /// Gets the entries last modified time. You can convert this to an easy to read for using LDateTime. virtual uint64 GetLastWriteTime() const; /// Returns the uncompressed size of the entry. virtual uint64 GetSize() const; /// Returns the size of file on disk. This can be both larger and smaller than the logical size. virtual int64 GetSizeOnDisk(); /// Returns true if the entry is a sub-directory. virtual bool IsDir() const; /// Returns true if the entry is a symbolic link. virtual bool IsSymLink() const; /// Returns true if the entry is read only. virtual bool IsReadOnly() const; /// \brief Returns true if the entry is hidden. /// This is equivilant to a attribute flag on win32 and a leading '.' on unix. virtual bool IsHidden() const; /// Creates an copy of this type of GDirectory class. virtual GDirectory *Clone(); /// Gets the type code of the current entry. See the VT_?? defines for possible values. virtual int GetType() const; /// Converts a string to the 64-bit value returned from the date functions. bool ConvertToTime(char *Str, int SLen, uint64 Time) const; /// Converts the 64-bit value returned from the date functions to a string. bool ConvertToDate(char *Str, int SLen, uint64 Time) const; }; /// Describes a volume connected to the system class LgiClass GVolume { friend class GFileSystem; protected: GString _Name; GString _Path; int _Type; // VT_?? int _Flags; // VA_?? int64 _Size; int64 _Free; public: GVolume(); virtual ~GVolume() {} char *Name() { return _Name; } char *Path() { return _Path; } int Type() { return _Type; } // VT_?? int Flags() { return _Flags; } uint64 Size() { return _Size; } uint64 Free() { return _Free; } virtual bool IsMounted() = 0; virtual bool SetMounted(bool Mount) = 0; virtual GVolume *First() = 0; virtual GVolume *Next() = 0; virtual GDirectory *GetContents() = 0; virtual void Insert(GAutoPtr v) = 0; }; typedef int (*CopyFileCallback)(void *token, int64 Done, int64 Total); /// A singleton class for accessing the file system class LgiClass GFileSystem { friend class GFile; static GFileSystem *Instance; class GFileSystemPrivate *d; GVolume *Root; public: GFileSystem(); ~GFileSystem(); /// Return the current instance of the file system. The shorthand for this is "FileDev". static GFileSystem *GetInstance() { return Instance; } /// Call this when the devices on the system change. For instance on windows /// when you receive WM_DEVICECHANGE. void OnDeviceChange(char *Reserved = 0); /// Gets the root volume of the system. GVolume *GetRootVolume(); /// Copies a file bool Copy ( /// The file to copy from... const char *From, /// The file to copy to. Any existing file there will be overwritten without warning. const char *To, /// The error code or zero on success LError *Status = 0, /// Optional callback when some data is copied. CopyFileCallback Callback = 0, /// A user defined token passed to the callback function void *Token = 0 ); /// Delete file bool Delete(const char *FileName, bool ToTrash = true); /// Delete files bool Delete ( /// The list of files to delete GArray &Files, /// A list of status codes where 0 means success and non-zero is an error code, usually an OS error code. NULL if not required. GArray *Status = NULL, /// true if you want the files moved to the trash folder, false if you want them deleted directly bool ToTrash = true ); /// Create a directory bool CreateFolder(const char *PathName, bool CreateParentFoldersIfNeeded = false, LError *Err = NULL); /// Remove's a directory bool RemoveFolder ( /// The path to remove const char *PathName, /// True if you want this function to recursively delete all contents of the path passing in. bool Recurse = false ); GString GetCurrentFolder(); bool SetCurrentFolder(const char *PathName); /// Moves a file to a new location. Only works on the same device. bool Move(const char *OldName, const char *NewName, LError *Err = NULL); }; #ifdef BEOS #define GFileOps() \ GFilePre char GFilePost; \ GFilePre int8 GFilePost; \ - GFilePre uint8 GFilePost; \ + GFilePre uint8_t GFilePost; \ GFilePre int16 GFilePost; \ GFilePre uint16 GFilePost; \ GFilePre signed int GFilePost; \ GFilePre unsigned int GFilePost; \ GFilePre signed long GFilePost; \ GFilePre unsigned long GFilePost; \ GFilePre int64 GFilePost; \ GFilePre uint64 GFilePost; \ GFilePre double GFilePost #else #define GFileOps() \ GFilePre char GFilePost; \ GFilePre int8 GFilePost; \ - GFilePre uint8 GFilePost; \ + GFilePre uint8_t GFilePost; \ GFilePre int16 GFilePost; \ GFilePre uint16 GFilePost; \ GFilePre signed int GFilePost; \ GFilePre unsigned int GFilePost; \ GFilePre signed long GFilePost; \ GFilePre unsigned long GFilePost; \ GFilePre int64 GFilePost; \ GFilePre uint64 GFilePost; \ GFilePre float GFilePost; \ GFilePre double GFilePost #endif /// Generic file access class class LgiClass GFile : public GStream, public GRefCount { protected: class GFilePrivate *d; ssize_t SwapRead(uchar *Buf, ssize_t Size); ssize_t SwapWrite(uchar *Buf, ssize_t Size); public: GFile(const char *Path = NULL, int Mode = 0); virtual ~GFile(); OsFile Handle(); /// \brief Opens a file /// \return Non zero on success int Open ( /// The path of the file to open const char *Name, /// The mode to open the file with. One of O_READ, O_WRITE or O_READWRITE. int Attrib ) override; /// Returns non zero if the class is associated with an open file handle. bool IsOpen() override; /// Returns the most recent error code encountered. int GetError(); /// Closes the file. int Close() override; /// Gets the mode that the file was opened with. int GetOpenMode(); /// Gets the block size int GetBlockSize(); /// \brief Gets the current file pointer. /// \return The file pointer or -1 on error. int64 GetPos() override; /// \brief Sets the current file pointer. /// \return The new file pointer or -1 on error. int64 SetPos(int64 Pos) override; /// \brief Gets the file size. /// \return The file size or -1 on error. int64 GetSize() override; /// \brief Sets the file size. /// \return The new file size or -1 on error. int64 SetSize(int64 Size) override; /// \brief Reads bytes into memory from the current file pointer. /// \return The number of bytes read or <= 0. ssize_t Read(void *Buffer, ssize_t Size, int Flags = 0) override; /// \brief Writes bytes from memory to the current file pointer. /// \return The number of bytes written or <= 0. ssize_t Write(const void *Buffer, ssize_t Size, int Flags = 0) override; /// Gets the path used to open the file virtual char *GetName(); /// Moves the current file pointer. virtual int64 Seek(int64 To, int Whence); /// Returns true if the current file pointer is at the end of the file. virtual bool Eof(); /// Resets the status value. virtual void SetStatus(bool s = false); /// \brief Returns true if all operations were successful since the file was openned or SetStatus /// was used to reset the file's status. virtual bool GetStatus(); /// \brief Sets the swap option. When switched on all integer reads/writes will have their bytes /// swaped. virtual void SetSwap(bool s); /// Gets the current swap setting. virtual bool GetSwap(); // String virtual ssize_t ReadStr(char *Buf, ssize_t Size); virtual ssize_t WriteStr(char *Buf, ssize_t Size); // Helpers ssize_t Write(GString s) { return Write(s.Get(), s.Length()); } // Operators #define GFilePre virtual GFile &operator >> ( #define GFilePost &i) GFileOps(); #undef GFilePre #undef GFilePost #define GFilePre virtual GFile &operator << ( #define GFilePost i) GFileOps(); #undef GFilePre #undef GFilePost // GDom impl bool GetVariant(const char *Name, GVariant &Value, char *Array = NULL) override; bool SetVariant(const char *Name, GVariant &Value, char *Array = NULL) override; bool CallMethod(const char *Name, GVariant *ReturnValue, GArray &Args) override; // Path handling class LgiClass Path : public GString::Array { GString Full; public: enum State { TypeNone = 0, TypeFolder = 1, TypeFile = 2, }; Path(const char *init = NULL, const char *join = NULL) { SetFixedLength(false); if (init) *this = init; if (join) *this += join; } Path(GAutoString Init) { SetFixedLength(false); if (Init) *this = Init.Get(); } Path(LgiSystemPath Which, int WordSize = 0) { SetFixedLength(false); *this = GetSystem(Which, WordSize); } Path &operator =(const char *p) { GString s(p); *((GString::Array*)this) = s.SplitDelimit("\\/"); SetFixedLength(false); return *this; } Path &operator =(const GString &s) { *((GString::Array*)this) = s.SplitDelimit("\\/"); SetFixedLength(false); return *this; } Path &operator =(const Path &p) { *((GString::Array*)this) = p; Full = p.Full; SetFixedLength(false); return *this; } Path &operator +=(Path &a) { SetFixedLength(false); if (a.Length() == 0) return *this; for (unsigned i=0; i 0 && nsz == sz && s.Set(NULL, nsz)) { int Block = 1 << 30; NativeInt i; for (i = 0; i < nsz; ) { int Len = MIN(Block, (int) (nsz - i)); ssize_t rd = Read(s.Get() + i, Len); if (rd <= 0) break; i += rd; } s.Get()[i] = 0; } return s; } /// Sets the file to zero size. /// Useful for this sort of thing: /// GFile(MyPath, O_WRITE).Empty().Write(MyData); GFile &Empty() { SetSize(0); return *this; } }; // Functions LgiFunc int64 LgiFileSize(const char *FileName); /// This function checks for the existance of a file (will return false for a folder). LgiFunc bool FileExists(const char *File, char *CorrectCase = NULL); /// This function checks for the existance of a directory. LgiFunc bool DirExists(const char *Dir, char *CorrectCase = NULL); LgiFunc bool ResolveShortcut(const char *LinkFile, char *Path, ssize_t Len); LgiFunc void WriteStr(GFile &f, const char *s); LgiFunc char *ReadStr(GFile &f DeclDebugArgs); LgiFunc ssize_t SizeofStr(const char *s); LgiFunc char *ReadTextFile(const char *File); LgiFunc bool LgiTrimDir(char *Path); LgiExtern const char *LgiGetLeaf(const char *Path); LgiExtern char *LgiGetLeaf(char *Path); LgiFunc bool LgiIsRelativePath(const char *Path); LgiClass GAutoString LgiMakeRelativePath(const char *Base, const char *Path); LgiFunc bool LgiMakePath(char *Str, int StrBufLen, const char *Dir, const char *File); LgiFunc char *LgiGetExtension(const char *File); LgiFunc bool LgiIsFileNameExecutable(const char *FileName); LgiFunc bool LgiIsFileExecutable(const char *FileName, GStreamI *f, int64 Start, int64 Len); /// Get information about the disk that a file resides on. LgiFunc bool LgiGetDriveInfo(char *Path, uint64 *Free, uint64 *Size = 0, uint64 *Available = 0); /// Shows the file's properties dialog LgiFunc void LgiShowFileProperties(OsView Parent, const char *Filename); /// Opens to the file or folder in the OS file browser (Explorer/Finder etc) LgiFunc bool LgiBrowseToFile(const char *Filename); /// Returns the physical device a file resides on LgiExtern GString LGetPhysicalDevice(const char *Path); #endif diff --git a/include/common/GFilter.h b/include/common/GFilter.h --- a/include/common/GFilter.h +++ b/include/common/GFilter.h @@ -1,226 +1,226 @@ /** \file \author Matthew Allen \date 1/4/97 \brief Graphics file filters */ #ifndef FILTER2_H #define FILTER2_H #include #include "GContainers.h" // #include "GProperties.h" // These are properties defined for use in the GFilter property list #define LGI_FILTER_ERROR "ErrorMsg" #define LGI_FILTER_COLOUR_PROF "ColourProfile" #define LGI_FILTER_PARENT_WND "Parent" #define LGI_FILTER_FOREGROUND "Fore" #define LGI_FILTER_BACKGROUND "Back" #define LGI_FILTER_TRANSPARENT "Transparent" #define LGI_FILTER_QUALITY "Quality" #define LGI_FILTER_SUBSAMPLE "SubSample" #define LGI_FILTER_INFO "Info" #define LGI_FILTER_DPI_X "DpiX" #define LGI_FILTER_DPI_Y "DpiY" // These must be returned by a GFilter's GetVariant method /// A descriptive name for a GFilter #define LGI_FILTER_TYPE "Type" /// A comma separated list of extensions the GFilter can handle #define LGI_FILTER_EXTENSIONS "Extension" LgiFunc int FindHeader(int Offset, const char *Str, GStream *f); /// Filter can read an image #define FILTER_CAP_READ 0x0001 /// Filter can write an image #define FILTER_CAP_WRITE 0x0002 /// Filter can get information about an image #define FILTER_CAP_INFO 0x0004 /// General base class for image filters. If you are creating a new filter you will /// need to also create a factory for it by inheriting another singleton class from /// GFilterFactory. class LgiClass GFilter : public GDom { - GArray Buf; + GArray Buf; protected: GBmpMem *GetSurface(GSurface *pDC) { return pDC->pMem; } GRect *GetClip(GSurface *pDC) { return &pDC->Clip; } ssize_t GetLineLength(GSurface *pDC) { return (pDC->pMem) ? pDC->pMem->Line : 0; } Progress *Meter; - inline void Swap(uint8 *out, const void *in, ssize_t len) + inline void Swap(uint8_t *out, const void *in, ssize_t len) { #ifdef __BIG_ENDIAN__ // Swap the bytes... uint8 *o = out + len - 1; const uint8 *i = in; while (i < in + len) { *o++ = *i++; } #else // Copy the byte memcpy(out, in, len); #endif } bool Read(GStream *s, void *p, ssize_t len) { - if (Buf.Length() < (uint32)len) + if (Buf.Length() < (uint32_t)len) Buf.Length(len); ssize_t r = s->Read(&Buf[0], len); if (r != len) return false; - Swap((uint8*)p, &Buf[0], len); + Swap((uint8_t*)p, &Buf[0], len); return true; } bool Write(GStream *s, const void *p, ssize_t len) { - if (Buf.Length() < (uint32)len) + if (Buf.Length() < (uint32_t)len) Buf.Length(len); Swap(&Buf[0], p, len); ssize_t w = s->Write(&Buf[0], len); return w == len; } public: GDom *Props; enum Format { FmtJpeg, FmtPng, FmtBmp, FmtIco, FmtTiff, FmtGif, FmtPcx, FmtSpi, FmtTga, }; enum IoStatus { IoError, IoSuccess, IoComponentMissing, IoUnsupportedFormat, IoCancel }; GFilter() { Meter = 0; Props = 0; } virtual ~GFilter() { } virtual Format GetFormat() = 0; /// Get the progress meter Progress *GetProgress() { return Meter; } /// Set the progress meter void SetProgress(Progress *Prg) { Meter = Prg; } /// Override this to return the capabilities of your filter. /// \returns some combination of #FILTER_CAP_READ, #FILTER_CAP_WRITE and #FILTER_CAP_INFO. virtual int GetCapabilites() { return 0; } /// \returns the number of images in the file virtual int GetImages() { return 1; } /// Reads an image into the specified surface. Override to implement reading an image. /// Also you need to return FILTER_CAP_READ from GetCapabilites if implemented. virtual IoStatus ReadImage(GSurface *Out, GStream *In) = 0; /// Writes an image from the specified surface. Override to implement writing an image. /// Also you need to return FILTER_CAP_WRITE from GetCapabilites if implemented. virtual IoStatus WriteImage(GStream *Out, GSurface *In) = 0; /// Returns the name of the external component needed to read/write images. virtual const char *GetComponentName() { return NULL; } }; #define GDC_RLE_COLOUR 0x0001 #define GDC_RLE_MONO 0x0002 #define GDC_RLE_READONLY 0x0004 #define GDC_RLE_KEY 0x0008 // set if valid class LgiClass GdcRleDC : public GMemDC { protected: COLOUR Key; int Flags; ssize_t Length; ssize_t Alloc; uchar *Data; uchar **ScanLine; bool SetLength(ssize_t Len); bool FindScanLines(); void Empty(); public: GdcRleDC(); virtual ~GdcRleDC(); void Move(GdcRleDC *pDC); bool Create(int x, int y, GColourSpace cs, int flags = SurfaceCreateNone); bool CreateInfo(int x, int y, GColourSpace cs); void ReadOnly(bool Read); bool ReadOnly(); void Mono(bool Mono); bool Mono(); void Update(int Flags); void Draw(GSurface *Dest, int x, int y); ssize_t SizeOf(); bool Read(GFile &F); bool Write(GFile &F); }; //////////////////////////////////////////////////////////////// /// Factory class for creating filter objects. You should create a static /// instance of a class inheriting from GFilterFactory in your filter code /// that can create instances of your filter. class LgiClass GFilterFactory { static class GFilterFactory *First; class GFilterFactory *Next; /// Override this to detect whether you can handle openning a file. /// \returns true if this filter can handle a given file. virtual bool CheckFile ( /// The filename, useful for extension checking. /// \sa LgiGetExtension. const char *File, /// The access mode that is desired, either #FILTER_CAP_READ or #FILTER_CAP_WRITE. int Access, /// The first 16 bytes of the file, or NULL if not available. Useful for checking /// magic numbers etc. const uchar *Hint ) = 0; /// Override to return an new (heap alloced) instance of your filter. virtual GFilter *NewObject() = 0; public: GFilterFactory(); virtual ~GFilterFactory(); static GFilter *New(const char *File, int Access, const uchar *Hint); static GFilter *NewAt(int i); static int GetItems(); }; #endif diff --git a/include/common/GFont.h b/include/common/GFont.h --- a/include/common/GFont.h +++ b/include/common/GFont.h @@ -1,475 +1,475 @@ /// \file /// \author Matthew Allen #ifndef _GDCFONT_H_ #define _GDCFONT_H_ #include "string.h" #include "GRect.h" #include "LgiOsClasses.h" #include "GColour.h" #include "GCapabilities.h" #include "GCss.h" ////////////////////////////////////////////////////////////// // Defines #ifndef WIN32 // font weights #define FW_DONTCARE 0 #define FW_THIN 100 #define FW_EXTRALIGHT 200 #define FW_ULTRALIGHT 200 #define FW_LIGHT 300 /// The default font weight #define FW_NORMAL 400 #define FW_REGULAR 400 #define FW_MEDIUM 500 #define FW_SEMIBOLD 600 #define FW_DEMIBOLD 600 /// Bold font weight #define FW_BOLD 700 #define FW_EXTRABOLD 800 #define FW_ULTRABOLD 800 #define FW_HEAVY 900 #define FW_BLACK 900 // quality /// Default font quality #define DEFAULT_QUALITY 0 /// Specifically anti-aliased font #define ANTIALIASED_QUALITY 1 /// Specifically not anti-alias font #define NONANTIALIASED_QUALITY 2 #elif defined WIN32 #define WESTEUROPE_CHARSET BALTIC_CHARSET // ??? don't know #endif // OS specific font type #if defined(LGI_SDL) #define PrevOsChar(Ptr) Ptr-- #define NextOsChar(Ptr) Ptr++ #elif defined __GTK_H__ #include "LgiOsClasses.h" #define PrevOsChar(Ptr) Ptr-- #define NextOsChar(Ptr) Ptr++ #elif defined(WIN32) #define PrevOsChar(Ptr) Ptr-- #define NextOsChar(Ptr) Ptr++ #elif defined(BEOS) #define PrevOsChar(Ptr) LgiPrevUtf8((char*&)Ptr) #define NextOsChar(Ptr) LgiNextUtf8((char*&)Ptr) #endif #define LGI_WHITESPACE_WEIGHT 0.15f // amount of foreground colour in visible whitespace #define MAX_UNICODE 0x10FFFF // maximum unicode char I can handle #define _HasUnicodeGlyph(map, u) ( (map[(u)>>3] & (1 << ((u) & 7))) != 0 ) enum LgiPxToIndexType { LgiTruncate, LgiNearest }; ////////////////////////////////////////////////////////////// // Classes class GFontType; /// Font parameters collection class LgiClass GTypeFace { protected: class GTypeFacePrivate *d; // Methods virtual void _OnPropChange(bool c) {} // if false then it's just a property change public: GTypeFace(); virtual ~GTypeFace(); /// Sets the font face name void Face(const char *s); /// Sets the font size void Size(GCss::Len); /// Sets the point size void PointSize(int i); /// Sets the tab size in device units (pixels) void TabSize(int i); /// Sets the quality resquested, use one of #DEFAULT_QUALITY, #ANTIALIASED_QUALITY or #NONANTIALIASED_QUALITY. void Quality(int i); /// Sets the foreground colour as a 24 bit RGB value void Fore(COLOUR c); void Fore(GColour c); /// Sets the background colour as a 24 bit RGB value. In most systems this is not important, /// but on BeOS the anti-aliasing is done from the foreground colour to the background colour /// with no regards for what is underneath the rendered text, thus you need to set the back /// colour correctly void Back(COLOUR c); void Back(GColour c); /// Get the whitespace rendering colour GColour WhitespaceColour(); /// Sets the rendering colour of whitespace characters when GDisplayString::ShowVisibleTab() is true. void WhitespaceColour(GColour c); /// Sets the font's weight, use one the weight defines in GFont.h, e.g. #FW_NORMAL, #FW_BOLD void SetWeight(int Weight); /// Set a bold font void Bold(bool i) { SetWeight(i ? FW_BOLD : FW_NORMAL); } /// Sets the font to italic void Italic(bool i); /// Draws with an underline void Underline(bool i); /// Makes the text have no background. void Transparent(bool i); /// Turns glyph substitution on or off void SubGlyphs(bool i); /// \returns the font face char *Face(); /// \returns the point size (avoid, use 'Size' instead) int PointSize(); /// \returns the size GCss::Len Size(); /// \returns the tabsize in pixels int TabSize(); /// \returns the quality setting int Quality(); /// \returns the foreground colour. GColour Fore(); /// \returns the background colour. GColour Back(); /// \returns the font weight. int GetWeight(); /// \returns true if this is a bold font. bool Bold() { return GetWeight() >= FW_BOLD; } /// \returns true if this is a italic font. bool Italic(); /// \returns true if this font is drawn with an underline. bool Underline(); /// \returns true if no background will be drawn. bool Transparent(); /// \returns true if glyph substitution will be done. bool SubGlyphs(); /// \returns the amount of space above the baseline. double Ascent(); /// \returns the amount of space below the baseline. double Descent(); /// \returns the amount of normally unused space at the top of the Ascent. double Leading(); /// \returns true if the font types are the same bool operator ==(GTypeFace &t); /// Set the foreground and background in 24-bit colour. /// \sa GTypeFace::Fore() and GTypeFace::Back() virtual void Colour(COLOUR Fore, COLOUR Back = 0xFFFFFFFF); /// Set the foreground and background colour. virtual void Colour(GColour Fore, GColour Back); }; /// \brief Font class. class LgiClass GFont : public GTypeFace { friend class GFontSystem; friend class GDisplayString; protected: class GFontPrivate *d; // Methods bool IsValid(); void _OnPropChange(bool Change); char16 *_ToUnicode(char *In, ssize_t &Len); bool GetOwnerUnderline(); virtual void _Measure(int &x, int &y, OsChar *Str, int Len); virtual int _CharAt(int x, OsChar *Str, int Len, LgiPxToIndexType Type); virtual void _Draw(GSurface *pDC, int x, int y, OsChar *Str, int Len, GRect *r, GColour &fore); public: /// Construct from face/pt size. GFont ( /// Font face name const char *face = 0, /// Size of the font GCss::Len size = GCss::LenInherit ); /// Construct from OS font handle GFont(OsFont Handle); /// Construct from type information GFont(GFontType &Type); /// Copy constructor GFont(GFont &Fnt); virtual ~GFont(); /// Creates a new font handle with the specified face / pt size virtual bool Create ( /// The new font face const char *Face = 0, /// The pt size GCss::Len Size = GCss::LenInherit, /// Creating a font for a particular surface (e.g. printing). GSurface *pSurface = 0 ); /// Creates a new font from type infomation bool Create(GFontType *Type, GSurface *pSurface = NULL); /// Creates the font from CSS defs. bool CreateFromCss(const char *Css); /// Creates the font from a CSS object. bool CreateFromCss(GCss *Css); /// Clears any handles and memory associated with the object. virtual bool Destroy(); /// Returns the OS font handle virtual OsFont Handle(); /// Copies the font virtual GFont &operator =(GFont &f); /// Returns the pixel height of the font virtual int GetHeight(); /// Gets the creation parameter passed in (0 by default). GSurface *GetSurface(); /// Get supported glyphs uchar *GetGlyphMap(); /// Converts printable characters to unicode. GAutoString ConvertToUnicode(char16 *Input, ssize_t Len = -1); #if defined(BEOS) GdcPt2 StringBounds(const char *s, int len = -1); #elif defined(MAC) CFDictionaryRef GetAttributes(); #endif }; /// Font type information and system font query tools. class LgiClass GFontType { friend class GFont; friend class GTypeFace; protected: #if defined WINNATIVE LOGFONTW Info; #else GTypeFace Info; #endif GString Buf; public: GFontType(const char *face = 0, int pointsize = 0); virtual ~GFontType(); #ifdef WINNATIVE LOGFONTW *Handle() { return &Info; } #else GTypeFace *Handle() { return &Info; } #endif /// Gets the type face name char *GetFace(); /// Sets the type face name void SetFace(const char *Face); /// Sets the point size int GetPointSize(); /// Sets the point size void SetPointSize(int PointSize); /// Put a user interface for the user to edit the font def bool DoUI(GView *Parent); /// Describe the font to the user as a string bool GetDescription(char *Str, int SLen); /// Read/Write the font def to storage // bool Serialize(ObjProperties *Options, char *OptName, bool Write); /// Read/Write the font def to storage bool Serialize(GDom *Options, const char *OptName, bool Write); /// Read the font from the LGI config bool GetConfigFont(const char *Tag); /// Read the font from LGI config (if there) and then the system settings bool GetSystemFont(const char *Which); /// Read from OS reference bool GetFromRef(OsFont Handle); /// Create a font based on this font def virtual GFont *Create(GSurface *pSurface = NULL); }; /// Charset definitions enum GCharSetType { CpNone, CpMapped, CpUtf8, CpUtf16, CpUtf32, CpIconv, CpWindowsDb }; /// Charset information class class LgiClass GCharset { public: /// Standard charset name const char *Charset; /// Human description const char *Description; /// 128 shorts that map the 0x80-0xff range to unicode short *UnicodeMap; /// Charset's name for calling iconv const char *IconvName; /// Comma separated list of alternate names used for this charset const char *AlternateNames; /// General type of the charset GCharSetType Type; /// Constructor GCharset(const char *cp = 0, const char *des = 0, short *map = 0, const char *alt = 0); /// Returns true if the charset is a unicode variant bool IsUnicode(); /// Gets the iconv name const char *GetIconvName(); /// Returns whether Lgi can convert to/from this charset at the moment. bool IsAvailable(); }; /// Charset table manager class class LgiClass GCharsetSystem { struct GCharsetSystemPriv *d; public: GCharsetSystem(); ~GCharsetSystem(); // Get the charset info GCharset *GetCsInfo(const char *Cp); GCharset *GetCsList(); }; /// Returns information about a charset. LgiFunc GCharset *LgiGetCsInfo(const char *Cs); /// Returns the start of an array of supported charsets, terminated by /// one with a NULL 'Charset' member. LgiFunc GCharset *LgiGetCsList(); /// Returns the charset that best fits the input data LgiFunc const char *LgiDetectCharset ( /// The input text const char *Utf8, /// The byte length of the input text ssize_t Len = -1, /// An optional list of prefered charsets to look through first List *Prefs = 0 ); #ifndef LGI_STATIC /// Overall font system class class LgiClass GFontSystem : public GCapabilityClient { friend class GApp; friend class GDisplayString; static GFontSystem *Me; private: // System Font List GString::Array AllFonts; GString::Array SubFonts; // Fonts yet to be scanned for substitution // Missing Glyph Substitution uchar Lut[MAX_UNICODE+1]; GFont *Font[256]; class GFontSystemPrivate *d; public: /// Get a pointer to the font system. static GFontSystem *Inst(); // Object GFontSystem(); ~GFontSystem(); /// Enumerate all installed fonts bool EnumerateFonts(GString::Array &Fonts); /// Returns whether the current Lgi implementation supports glyph sub bool GetGlyphSubSupport(); /// Returns whether glyph sub is currently turned on bool GetDefaultGlyphSub(); /// Turns the glyph sub feature on or off void SetDefaultGlyphSub(bool i); /// Returns a font that can render the specified unicode code point GFont *GetGlyph ( /// A utf-32 character - uint32 u, + uint32_t u, /// The base font used for rendering GFont *UserFont ); /// This looks for a font that can contains the most glyphs for a given string, /// ideally it can render the whole thing. But the next best alternative is returned /// when no font matches all characters in the string. GFont *GetBestFont(char *Str); /// Add a custom font to the glyph lookup table bool AddFont(GAutoPtr Fnt); #ifdef __GTK_H__ // Pango stuff Gtk::PangoFontMap *GetFontMap(); Gtk::PangoContext *GetContext(); #endif /// \returns true if iconv services are available. bool HasIconv(bool Quiet); /// Converts a string into a buffer using iconv ssize_t IconvConvert(const char *OutCs, char *Out, ssize_t OutLen, const char *InCs, const char *&In, ssize_t InLen); /// Converts a string into a stream using iconv ssize_t IconvConvert(const char *OutCs, GStreamI *Out, const char *InCs, const char *&In, ssize_t InLen); }; #endif #ifdef LINUX extern bool _GetSystemFont(char *FontType, char *Font, int FontBufSize, int &PointSize); #endif #endif diff --git a/include/common/GPalette.h b/include/common/GPalette.h --- a/include/common/GPalette.h +++ b/include/common/GPalette.h @@ -1,82 +1,82 @@ #ifndef _GPALETTE_H_ #define _GPALETTE_H_ #include "GColourSpace.h" #include "GFile.h" #ifdef WIN32 #pragma pack(push, before_pack) #pragma pack(1) #endif /// RGB Colour typedef GRgba32 GdcRGB; #ifdef WIN32 #pragma pack(pop, before_pack) #endif /// Palette of colours class LgiClass GPalette { protected: #if WINNATIVE HPALETTE hPal; LOGPALETTE *Data; #else int Size; GRgba32 *Data; #endif uchar *Lut; public: GPalette(); virtual ~GPalette(); #if WINNATIVE HPALETTE Handle() { return hPal; } #endif GPalette(GPalette *pPal); GPalette(uchar *pPal, int s = 256); void Set(GPalette *pPal); void Set(uchar *pPal, int s = 256); void Set(int Index, int r, int g, int b); bool Update(); bool SetSize(int s = 256); void SwapRAndB(); - int MatchRgb(uint32 Rgb); + int MatchRgb(uint32_t Rgb); void CreateCube(); void CreateGreyScale(); bool Load(GFile &F); bool Save(GFile &F, int Format); uchar *MakeLut(int Bits = 16); bool operator ==(GPalette &p); bool operator !=(GPalette &p); #if WINNATIVE GRgba32 *operator [](int i) { return (i >= 0 && i < GetSize() && Data) ? (GdcRGB*) (Data->palPalEntry + i) : NULL; } int GetSize() { return (Data) ? Data->palNumEntries : 0; } #else GRgba32 *operator [](int i) { return (i >= 0 && i < GetSize() && Data) ? (GdcRGB*) (Data + i) : NULL; } int GetSize() { return Size; } #endif }; #endif \ No newline at end of file diff --git a/include/common/GPath.h b/include/common/GPath.h --- a/include/common/GPath.h +++ b/include/common/GPath.h @@ -1,1014 +1,1014 @@ #ifndef __GPath_h__ #define __GPath_h__ #include #include "GMatrix.h" #define GPATH_DBG 1 class GSeg; class GVector; extern bool _Disable_ActiveList; extern bool _Disable_XSort; extern bool _Disable_Alpha; extern bool _Disable_Rops; enum GPathFillRule { FILLRULE_ODDEVEN, FILLRULE_NONZERO }; class LgiClass GPointF { public: static double Threshold; double x, y; GPointF() { x = y = 0; } GPointF(double X, double Y) { x = X; y = Y; } GPointF(const GPointF &p) { x = p.x; y = p.y; } GPointF(const GdcPt2 &p) { x = p.x; y = p.y; } GPointF &operator =(GPointF &p) { x = p.x; y = p.y; return *this; } GPointF &operator -(GPointF &p) { static GPointF Result; Result.x = x - p.x; Result.y = y - p.y; return Result; } GPointF &operator +(GPointF &p) { static GPointF Result; Result.x = x + p.x; Result.y = y + p.y; return Result; } bool operator ==(GPointF &p) { double dx = x - p.x; if (dx < 0) dx = -dx; double dy = y - p.y; if (dy < 0) dy = -dy; return dx= x1 && y2 >= y1; } bool Valid() { return Defined && IsNormal(); } void Normalize(); void Union(GPointF &p); void Union(GRectF &p); void Intersect(GRectF &p); bool Overlap(GPointF &p); bool Overlap(GRectF &p); void Offset(double x, double y); void Size(double dx, double dy); char *Describe(); GRectF &operator =(GRect &f); GRectF &operator =(GRectF &f); GRectF &operator =(GPointF &p); }; class LgiClass GBrush { friend class GPath; protected: uchar AlphaLut[65]; class GRopArgs { public: uchar *Pixels; uchar *EndOfMem; uchar *Alpha; int Len; int x, y; GColourSpace Cs; int BytesPerPixel; GSurface *pDC; }; virtual bool Start(GRopArgs &a) { return true; } virtual void Rop(GRopArgs &a) = 0; virtual int Alpha() { return 255; } void MakeAlphaLut(); public: GBrush() {} virtual ~GBrush() {} }; class LgiClass GEraseBrush : public GBrush { void Rop(GRopArgs &a); public: GEraseBrush() { MakeAlphaLut(); } }; // In premultiplied: // Dca' = Sca + Dca.(1 - Sa) // -or- // In non-premultiplied: // Dc' = (Sc.Sa + Dc.Da.(1 - Sa))/Da' #define NonPreMulOver16(c, sh) d->c = (((s.c * sa) + (DivLut[(d->c << sh) * 255] * o)) / 255) >> sh #define NonPreMulOver32(c) d->c = ((s.c * sa) + (DivLut[d->c * da] * o)) / d->a #define NonPreMulOver24(c) d->c = ((s.c * sa) + (DivLut[d->c * 255] * o)) / 255 #define NonPreMulAlpha d->a = (d->a + sa) - DivLut[d->a * sa] // Define 'uint8 dc, sc;' first. Takes 16bit source and 8bit dest. #define Rgb16to8PreMul(c) \ sc = DivLut[(s->c >> 8) * (s->a >> 8)]; \ dc = DivLut[d->c * d->a]; \ d->c = sc + DivLut[dc * o] class LgiClass GSolidBrush : public GBrush { COLOUR c32; void Rop(GRopArgs &a); int Alpha() { return A32(c32); } public: GSolidBrush(COLOUR c) { c32 = c; MakeAlphaLut(); } GSolidBrush(GColour c) { c32 = c.c32(); MakeAlphaLut(); } GSolidBrush(int r, int g, int b, int a = 255) { c32 = Rgba32(r, g, b, a); MakeAlphaLut(); } template - void SolidRop16(T *d, int Len, uint8 *Alpha, COLOUR c32) + void SolidRop16(T *d, int Len, uint8_t *Alpha, COLOUR c32) { uchar *DivLut = Div255Lut; T *end = d + Len; GRgb24 s; s.r = R32(c32); s.g = G32(c32); s.b = B32(c32); while (d < end) { uchar sa = AlphaLut[*Alpha++]; if (sa == 255) { d->r = s.r >> 3; d->g = s.g >> 2; d->b = s.b >> 3; } else if (sa) { uchar o = 0xff - sa; NonPreMulOver16(r, 3); NonPreMulOver16(g, 2); NonPreMulOver16(b, 3); } d++; } } template - void SolidRop24(T *d, int Len, uint8 *Alpha, COLOUR c32) + void SolidRop24(T *d, int Len, uint8_t *Alpha, COLOUR c32) { uchar *DivLut = Div255Lut; T *end = d + Len; T s; s.r = R32(c32); s.g = G32(c32); s.b = B32(c32); while (d < end) { uchar sa = AlphaLut[*Alpha++]; if (sa == 255) { *d = s; } else if (sa) { uchar o = 0xff - sa; NonPreMulOver24(r); NonPreMulOver24(g); NonPreMulOver24(b); } d++; } } template - void SolidRop32(T *d, int Len, uint8 *Alpha, COLOUR c32) + void SolidRop32(T *d, int Len, uint8_t *Alpha, COLOUR c32) { uchar *DivLut = Div255Lut; T *end = d + Len; T s; s.r = R32(c32); s.g = G32(c32); s.b = B32(c32); s.a = A32(c32); while (d < end) { uchar sa = DivLut[s.a * AlphaLut[*Alpha++]]; if (sa == 255) { *d = s; } else if (sa) { int o = 0xff - sa; int da = d->a; NonPreMulAlpha; NonPreMulOver32(r); NonPreMulOver32(g); NonPreMulOver32(b); } d++; } } }; struct GBlendStop { double Pos; COLOUR c32; }; class LgiClass GBlendBrush : public GBrush { protected: int Stops; GBlendStop *Stop; COLOUR Lut[256]; double Base, IncX, IncY; GPointF p[2]; bool Start(GRopArgs &a); public: GBlendBrush(int stops, GBlendStop *stop) { MakeAlphaLut(); Stops = 0; Stop = 0; if (stop) { SetStops(stops, stop); } } ~GBlendBrush() { DeleteArray(Stop); } int GetStops(GBlendStop **stop = 0) { if (stop) *stop = Stop; return Stops; } void SetStops(int stops, GBlendStop *stop) { Stops = stops; DeleteArray(Stop); Stop = new GBlendStop[Stops]; if (Stop) memcpy(Stop, stop, sizeof(*Stop) * Stops); } }; class LgiClass GLinearBlendBrush : public GBlendBrush { bool Start(GRopArgs &Args); void Rop(GRopArgs &Args); public: GLinearBlendBrush(GPointF a, GPointF b, int stops = 0, GBlendStop *stop = 0) : GBlendBrush(stops, stop) { p[0] = a; p[1] = b; } template void Linear16(T *d, GRopArgs &Args) { uchar *DivLut = Div255Lut; uchar *Alpha = Args.Alpha; double DPos = Base + (Args.x * IncX) + (Args.y * IncY); double Scale = (double)0x10000; int Pos = (int) (DPos * Scale); int Inc = (int) (IncX * Scale); T *e = d + Args.Len; GRgb24 s; while (d < e) { if (*Alpha) { // work out colour COLOUR c32; int Ci = ((Pos << 8) - Pos) >> 16; if (Ci < 0) c32 = Lut[0]; else if (Ci > 255) c32 = Lut[255]; else c32 = Lut[Ci]; s.r = R32(c32); s.g = G32(c32); s.b = B32(c32); // assign pixel uchar sa = DivLut[AlphaLut[*Alpha] * A32(c32)]; if (sa == 0xff) { d->r = s.r >> 3; d->g = s.g >> 2; d->b = s.b >> 3; } else if (sa) { uchar o = 0xff - sa; NonPreMulOver16(r, 3); NonPreMulOver16(g, 2); NonPreMulOver16(b, 3); } } d++; Alpha++; Pos += Inc; } } template void Linear24(T *d, GRopArgs &Args) { uchar *DivLut = Div255Lut; uchar *Alpha = Args.Alpha; double DPos = Base + (Args.x * IncX) + (Args.y * IncY); double Scale = (double)0x10000; int Pos = (int) (DPos * Scale); int Inc = (int) (IncX * Scale); T *e = d + Args.Len; T s; while (d < e) { if (*Alpha) { // work out colour COLOUR c32; int Ci = ((Pos << 8) - Pos) >> 16; if (Ci < 0) c32 = Lut[0]; else if (Ci > 255) c32 = Lut[255]; else c32 = Lut[Ci]; s.r = R32(c32); s.g = G32(c32); s.b = B32(c32); // assign pixel uchar sa = DivLut[AlphaLut[*Alpha] * A32(c32)]; if (sa == 0xff) { *d = s; } else if (sa) { uchar o = 0xff - sa; NonPreMulOver24(r); NonPreMulOver24(g); NonPreMulOver24(b); } } d++; Alpha++; Pos += Inc; } } template void Linear32(T *d, GRopArgs &Args) { uchar *DivLut = Div255Lut; uchar *Alpha = Args.Alpha; double DPos = Base + (Args.x * IncX) + (Args.y * IncY); double Scale = (double)0x10000; int Pos = (int) (DPos * Scale); int Inc = (int) (IncX * Scale); T *End = d + Args.Len; T s; while (d < End) { if (*Alpha) { // work out colour COLOUR c; int Ci = ((Pos << 8) - Pos) >> 16; if (Ci < 0) c = Lut[0]; else if (Ci > 255) c = Lut[255]; else c = Lut[Ci]; s.r = R32(c); s.g = G32(c); s.b = B32(c); s.a = A32(c); // assign pixel uchar sa = DivLut[AlphaLut[*Alpha] * A32(c)]; if (sa == 0xff) { *d = s; } else if (sa) { uchar o = 0xff - sa; int da = d->a; NonPreMulAlpha; NonPreMulOver32(r); NonPreMulOver32(g); NonPreMulOver32(b); } } d++; Alpha++; Pos += Inc; } } }; class LgiClass GRadialBlendBrush : public GBlendBrush { void Rop(GRopArgs &Args); public: GRadialBlendBrush(GPointF center, GPointF rim, int stops = 0, GBlendStop *stop = 0) : GBlendBrush(stops, stop) { p[0] = center; p[1] = rim; } template void Radial16(T *d, GRopArgs &Args) { uchar *DivLut = Div255Lut; uchar *Alpha = Args.Alpha; double dx = p[1].x - p[0].x; double dy = p[1].y - p[0].y; double Radius = sqrt((dx*dx)+(dy*dy)) / 255; // scaled by 255 to index into the Lut int dysq = (int) (Args.y - p[0].y); dysq *= dysq; int x = (int) (Args.x - p[0].x); int dxsq = x * x; // forward difference the (x * x) calculation int xd = 2 * x + 1; // to remove a MUL from the inner loop int Ci = 0; COLOUR c32; uchar sa; T *End = (T*) d + Args.Len; GRgb24 s; if (Radius < 0.0000000001) { // No blend... just the end colour c32 = Lut[255]; s.r = R32(c32); s.g = G32(c32); s.b = B32(c32); for (; dr = s.r >> 3; d->g = s.g >> 2; d->b = s.b >> 3; } else if (sa) { uchar o = 0xff - sa; NonPreMulOver16(r, 3); NonPreMulOver16(g, 2); NonPreMulOver16(b, 3); } } d++; } } else { // Radial blend for (; d 255) c32 = Lut[255]; else c32 = Lut[Ci]; s.r = R32(c32); s.g = G32(c32); s.b = B32(c32); // composite the pixel sa = DivLut[AlphaLut[*Alpha] * A32(c32)]; if (sa == 0xff) { d->r = s.r >> 3; d->g = s.g >> 2; d->b = s.b >> 3; } else if (sa) { uchar o = 0xff - sa; NonPreMulOver16(r, 3); NonPreMulOver16(g, 2); NonPreMulOver16(b, 3); } } // apply forward difference to the x values x++; dxsq += xd; xd += 2; d++; } } } template void Radial24(T *d, GRopArgs &Args) { uchar *DivLut = Div255Lut; uchar *Alpha = Args.Alpha; double dx = p[1].x - p[0].x; double dy = p[1].y - p[0].y; double Radius = sqrt((dx*dx)+(dy*dy)) / 255; // scaled by 255 to index into the Lut int dysq = (int) (Args.y - p[0].y); dysq *= dysq; int x = (int) (Args.x - p[0].x); int dxsq = x * x; // forward difference the (x * x) calculation int xd = 2 * x + 1; // to remove a MUL from the inner loop int Ci = 0; COLOUR c32; uchar sa; T *End = (T*) d + Args.Len; T s; if (Radius < 0.0000000001) { // No blend... just the end colour c32 = Lut[255]; s.r = R32(c32); s.g = G32(c32); s.b = B32(c32); for (; d 255) c32 = Lut[255]; else c32 = Lut[Ci]; s.r = R32(c32); s.g = G32(c32); s.b = B32(c32); // composite the pixel sa = DivLut[AlphaLut[*Alpha] * A32(c32)]; if (sa == 0xff) { *d = s; } else if (sa) { uchar o = 0xff - sa; NonPreMulOver24(r); NonPreMulOver24(g); NonPreMulOver24(b); } } // apply forward difference to the x values x++; dxsq += xd; xd += 2; d++; } } } template void Radial32(T *d, GRopArgs &Args) { uchar *DivLut = Div255Lut; uchar *Alpha = Args.Alpha; double dx = p[1].x - p[0].x; double dy = p[1].y - p[0].y; double Radius = sqrt((dx*dx)+(dy*dy)) / 255; // scaled by 255 to index into the Lut int dysq = (int) (Args.y - p[0].y); dysq *= dysq; int x = (int) (Args.x - p[0].x); int dxsq = x * x; // forward difference the (x * x) calculation int xd = 2 * x + 1; // to remove a MUL from the inner loop int Ci = 0; COLOUR c; uchar sa; T *End = d + Args.Len; T s; if (Radius < 0.0000000001) { // No blend... just the end colour c = Lut[255]; s.r = R32(c); s.g = G32(c); s.b = B32(c); s.a = A32(c); for (; da; NonPreMulAlpha; NonPreMulOver32(r); NonPreMulOver32(g); NonPreMulOver32(b); } } } } else { // Radial blend for (; d 255) c = Lut[255]; else c = Lut[Ci]; s.r = R32(c); s.g = G32(c); s.b = B32(c); s.a = A32(c); // composite the pixel sa = DivLut[AlphaLut[*Alpha] * A32(c)]; if (sa == 0xff) { *d = s; } else if (sa) { uchar o = 0xff - sa; int da = d->a; NonPreMulAlpha; NonPreMulOver32(r); NonPreMulOver32(g); NonPreMulOver32(b); } } // apply forward difference to the x values x++; dxsq += xd; xd += 2; } } } }; class LgiClass GPath { public: class Matrix : public GMatrix { public: Matrix() { SetIdentity(); } Matrix &Translate(double x, double y) { m[2][0] = x; m[2][1] = y; return *this; } Matrix &Rotate(double angle, bool anti_clockwise = false) { m[0][0] = m[1][1] = cos(angle); m[0][1] = (anti_clockwise ? -1 : 1) * sin(angle); m[1][0] = (anti_clockwise ? 1 : -1) * sin(angle); return *this; } }; protected: // Data List Segs; List Vecs; GRectF Bounds; bool Aa; GPathFillRule FillRule; Matrix Mat; // Flattened representation int Points; GArray Outline; GPointF *Point; // Methods void Unflatten(); void Append(GPath &p); public: GPath(bool aa = true); virtual ~GPath(); // Primitives void MoveTo( double x, double y); void MoveTo( GPointF &pt); void LineTo( double x, double y); void LineTo( GPointF &pt); void QuadBezierTo( double cx, double cy, double px, double py); void QuadBezierTo( GPointF &c, GPointF &p); void CubicBezierTo( double c1x, double c1y, double c2x, double c2y, double px, double py); void CubicBezierTo( GPointF &c1, GPointF &c2, GPointF &p); void Rectangle( double x1, double y1, double x2, double y2); void Rectangle( GPointF &tl, GPointF &rb); void Rectangle( GRectF &r); void RoundRect( GRectF &b, double r); void Circle( double cx, double cy, double radius); void Circle( GPointF &c, double radius); void Ellipse( double cx, double cy, double x, double y); void Ellipse( GPointF &c, double x, double y); bool Text( GFont *Font, double x, double y, char *Utf8, int Bytes = -1); // Properties int Segments(); void GetBounds(GRectF *b); GPathFillRule GetFillRule() { return FillRule; } void SetFillRule(GPathFillRule r) { FillRule = r; } // Methods bool IsClosed(); void Close(); bool IsFlattened(); bool Flatten(); bool IsEmpty(); void Empty(); void Transform(Matrix &m); void DeleteSeg(int i); // Colouring (windows: need to call ConvertPreMulAlpha(true) on the pDC after using these) void Fill(GSurface *pDC, GBrush &Brush); void Stroke(GSurface *pDC, GBrush &Brush, double Width); #if GPATH_DBG GMemDC DbgDsp; #endif }; void FlattenQuadratic(GPointF *&Out, GPointF &p1, GPointF &p2, GPointF &p3, int Steps); void FlattenCubic(GPointF *&Out, GPointF &p1, GPointF &p2, GPointF &p3, GPointF &p4, int Steps); #endif diff --git a/include/common/GPixelRops.h b/include/common/GPixelRops.h --- a/include/common/GPixelRops.h +++ b/include/common/GPixelRops.h @@ -1,130 +1,130 @@ // // GPixelRops.h // Lgi // // Created by Matthew Allen on 1/03/14. // Copyright (c) 2014 Memecode. All rights reserved. // #ifndef _GPixelRops_h #define _GPixelRops_h #ifdef MAC #define REG #else #define REG register #endif ////////////////////////////////////////////////////////////////////////// // 24 bit over ////////////////////////////////////////////////////////////////////////// #define OverNpm24toNpm32(s,d,sa) \ { \ - REG uint8 oma = 0xff - sa; \ - REG uint8 da = sa + DivLut[d->a * oma]; \ + REG uint8_t oma = 0xff - sa; \ + REG uint8_t da = sa + DivLut[d->a * oma]; \ d->r = ((s->r * sa) + (DivLut[d->r * da] * oma)) / da; \ d->g = ((s->g * sa) + (DivLut[d->g * da] * oma)) / da; \ d->b = ((s->b * sa) + (DivLut[d->b * da] * oma)) / da; \ d->a = da; \ } ////////////////////////////////////////////////////////////////////////// // 32 bit over ////////////////////////////////////////////////////////////////////////// #define OverNpm32toNpm24(s,d) \ { \ - REG uint8 sa = (s)->a; \ - REG uint8 oma = 0xff - sa; \ + REG uint8_t sa = (s)->a; \ + REG uint8_t oma = 0xff - sa; \ (d)->r = DivLut[((s)->r * sa) + ((d)->r * oma)]; \ (d)->g = DivLut[((s)->g * sa) + ((d)->g * oma)]; \ (d)->b = DivLut[((s)->b * sa) + ((d)->b * oma)]; \ } #define OverNpm32toNpm32(s,d) \ { \ - REG uint8 sa = s->a; \ - REG uint8 oma = 0xff - sa; \ - REG uint8 da = sa + DivLut[d->a * oma]; \ + REG uint8_t sa = s->a; \ + REG uint8_t oma = 0xff - sa; \ + REG uint8_t da = sa + DivLut[d->a * oma]; \ d->r = ((s->r * sa) + (DivLut[d->r * da] * oma)) / da; \ d->g = ((s->g * sa) + (DivLut[d->g * da] * oma)) / da; \ d->b = ((s->b * sa) + (DivLut[d->b * da] * oma)) / da; \ d->a = da; \ } #define OverNpm32toNpm48(s,d) \ { \ REG uint16 sa = G8bitTo16bit((s)->a); \ REG uint16 oma = 0xffff - sa; \ (d)->r = ( (G8bitTo16bit((s)->r) * sa) + ((d)->r * oma) ) / 0xffff; \ (d)->g = ( (G8bitTo16bit((s)->g) * sa) + ((d)->g * oma) ) / 0xffff; \ (d)->b = ( (G8bitTo16bit((s)->b) * sa) + ((d)->b * oma) ) / 0xffff; \ } #define OverNpm32toNpm64(s,d) \ { \ REG uint16 sa = G8bitTo16bit((s)->a); \ REG uint16 oma = 0xffff - sa; \ - REG uint16 da = sa + ((uint32)(d)->a * oma) / 0xffff; \ - d->r = ( (G8bitTo16bit(s->r) * sa) + ( ((uint32)(d->r * da) / 0xffff) * oma)) / da; \ - d->g = ( (G8bitTo16bit(s->g) * sa) + ( ((uint32)(d->g * da) / 0xffff) * oma)) / da; \ - d->b = ( (G8bitTo16bit(s->b) * sa) + ( ((uint32)(d->b * da) / 0xffff) * oma)) / da; \ + REG uint16 da = sa + ((uint32_t)(d)->a * oma) / 0xffff; \ + d->r = ( (G8bitTo16bit(s->r) * sa) + ( ((uint32_t)(d->r * da) / 0xffff) * oma)) / da; \ + d->g = ( (G8bitTo16bit(s->g) * sa) + ( ((uint32_t)(d->g * da) / 0xffff) * oma)) / da; \ + d->b = ( (G8bitTo16bit(s->b) * sa) + ( ((uint32_t)(d->b * da) / 0xffff) * oma)) / da; \ d->a = da; \ } #define OverPm32toPm24(s,d) \ { \ - REG uint8 oma = 0xff - s->a; \ + REG uint8_t oma = 0xff - s->a; \ d->r = s->r + DivLut[d->r * oma]; \ d->g = s->g + DivLut[d->g * oma]; \ d->b = s->b + DivLut[d->b * oma]; \ } #define OverPm32toPm32(s,d) \ { \ - REG uint8 sa = s->a; \ - REG uint8 oma = 0xff - sa; \ + REG uint8_t sa = s->a; \ + REG uint8_t oma = 0xff - sa; \ d->r = s->r + DivLut[d->r * oma]; \ d->g = s->g + DivLut[d->g * oma]; \ d->b = s->b + DivLut[d->b * oma]; \ d->a = sa + DivLut[d->a * oma]; \ } ////////////////////////////////////////////////////////////////////////// // 64 bit over ////////////////////////////////////////////////////////////////////////// #define OverNpm64toNpm24(s,d) \ { \ - REG uint8 sa = (s)->a >> 8; \ - REG uint8 oma = 0xff - sa; \ + REG uint8_t sa = (s)->a >> 8; \ + REG uint8_t oma = 0xff - sa; \ (d)->r = DivLut[( ((s)->r >> 8) * sa) + ((d)->r * oma)]; \ (d)->g = DivLut[( ((s)->g >> 8) * sa) + ((d)->g * oma)]; \ (d)->b = DivLut[( ((s)->b >> 8) * sa) + ((d)->b * oma)]; \ } #define OverNpm64toNpm32(s,d) \ { \ - REG uint8 sa = s->a >> 8; \ - REG uint8 oma = 0xff - sa; \ - REG uint8 da = sa + DivLut[d->a * oma]; \ + REG uint8_t sa = s->a >> 8; \ + REG uint8_t oma = 0xff - sa; \ + REG uint8_t da = sa + DivLut[d->a * oma]; \ d->r = (( (s->r>>8) * sa) + (DivLut[d->r * da] * oma)) / da; \ d->g = (( (s->g>>8) * sa) + (DivLut[d->g * da] * oma)) / da; \ d->b = (( (s->b>>8) * sa) + (DivLut[d->b * da] * oma)) / da; \ d->a = da; \ } #define OverNpm64toNpm48(s,d) \ { \ REG uint16 sa = (s)->a; \ REG uint16 oma = 0xffff - sa; \ (d)->r = ( ((s)->r * sa) + ((d)->r * oma) ) / 0xffff; \ (d)->g = ( ((s)->g * sa) + ((d)->g * oma) ) / 0xffff; \ (d)->b = ( ((s)->b * sa) + ((d)->b * oma) ) / 0xffff; \ } #define OverNpm64toNpm64(s,d) \ { \ REG uint16 sa = (s)->a; \ REG uint16 oma = 0xffff - sa; \ - REG uint16 da = sa + ((uint32)(d)->a * oma) / 0xffff; \ - (d)->r = (( (uint32)(s)->r * sa) + ( ((uint32)d->r * da) / 0xffff * oma)) / da; \ - (d)->g = (( (uint32)(s)->g * sa) + ( ((uint32)d->g * da) / 0xffff * oma)) / da; \ - (d)->b = (( (uint32)(s)->b * sa) + ( ((uint32)d->b * da) / 0xffff * oma)) / da; \ + REG uint16 da = sa + ((uint32_t)(d)->a * oma) / 0xffff; \ + (d)->r = (( (uint32_t)(s)->r * sa) + ( ((uint32_t)d->r * da) / 0xffff * oma)) / da; \ + (d)->g = (( (uint32_t)(s)->g * sa) + ( ((uint32_t)d->g * da) / 0xffff * oma)) / da; \ + (d)->b = (( (uint32_t)(s)->b * sa) + ( ((uint32_t)d->b * da) / 0xffff * oma)) / da; \ (d)->a = da; \ } #endif diff --git a/include/common/GProcess.h b/include/common/GProcess.h --- a/include/common/GProcess.h +++ b/include/common/GProcess.h @@ -1,60 +1,60 @@ /// \file #ifndef _GPROCESS_H_ #define _GPROCESS_H_ #include "GStream.h" LgiFunc bool LgiIsProcess(OsProcessId Pid); /// A process wrapper class class LgiClass GProcess { class GProcessPrivate *d; public: GProcess(); ~GProcess(); /// \returns the process handle OsProcess Handle(); /// \returns the process ID [Win32] OsProcessId GetId(); /// \returns the value the process exited with ulong ExitValue(); /// \returns the any error code - uint32 GetErrorCode(); + uint32_t GetErrorCode(); /// Stops the process right now, use with care bool Terminate(); /// \returns true if still running, else false. bool IsRunning(); /// Starts a new process bool Run ( /// The path to the executable to run const char *Exe, /// The arguments to pass to the program const char *Args, /// The current directory to start the program in const char *Dir, /// If true, calling Run will block until the process /// exits, else Run will exit after starting the process. /// /// The parameters In and Out are only used if Wait=true bool Wait, /// A stream to supply input to the process. Only used if /// Wait=true. GStream *In = 0, /// A stream to capture output from the process. Only used if /// Wait=true. GStream *Out = 0, /// The priority to run the process at. ///
  • -2 = idle ///
  • -1 = low ///
  • 0 = normal ///
  • 1 = high ///
  • 2 = realtime(ish) int Priority = 0 ); }; #endif diff --git a/include/common/GRegKey.h b/include/common/GRegKey.h --- a/include/common/GRegKey.h +++ b/include/common/GRegKey.h @@ -1,71 +1,71 @@ #if !defined(_GREGKEY_H_) && defined(WIN32) #define _GREGKEY_H_ /// Registry access class #include "GContainers.h" class LgiClass GRegKey { HKEY k, Root; char s[256]; GString KeyName; public: static bool AssertOnError; /// Constructor GRegKey ( /// The access type required bool WriteAccess, /// The key name: you can use printf style syntax and extra arguments char *Key, ... ); ~GRegKey(); /// Returns true if the key was openned bool IsOk(); /// Creates the key if not present bool Create(); /// Returns the key name char *Name(); /// Return a string value char *GetStr ( /// Name of the subkey or NULL for the default string. const char *Name = 0 ); /// Return a string value bool GetStr ( /// Name of the subkey or NULL for the default string. const char *Name, /// The string's value GString &Str ); /// Sets a string value bool SetStr(const char *Name, const char *Value); /// Get an int value - bool GetInt(const char *Name, uint32 &Value); + bool GetInt(const char *Name, uint32_t &Value); /// Set an int value - bool SetInt(const char *Name, uint32 Value); + bool SetInt(const char *Name, uint32_t Value); /// Get a binary value bool GetBinary(char *Name, void *&Ptr, int &Len); /// Set a binary value bool SetBinary(char *Name, void *Ptr, int Len); /// Delete a value bool DeleteValue(char *Name = 0); /// Delete a key bool DeleteKey(); /// List all the key names under this key bool GetKeyNames(List &n); /// List all the key value name under this key bool GetValueNames(List &n); }; #endif \ No newline at end of file diff --git a/include/common/GRops.h b/include/common/GRops.h --- a/include/common/GRops.h +++ b/include/common/GRops.h @@ -1,1320 +1,1320 @@ #ifndef _GROPS_H_ #define _GROPS_H_ #include "GPixelRops.h" -#define IsOverlapping() ((uint8*)dst == (uint8*)src) +#define IsOverlapping() ((uint8_t*)dst == (uint8_t*)src) #define OverlapCheck() \ if (IsOverlapping()) \ { \ LgiAssert(!"regions can't overlap."); \ return; \ } ////////////////////////////////////////////////////////////////// // To 15 bits template void GRop15To15(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = s->r; d->g = s->g; d->b = s->b; s++; d++; } } template void GRop16To15(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = s->r; d->g = s->g >> 1; d->b = s->b; s++; d++; } } template void GRop24To15(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = s->r >> 3; d->g = s->g >> 3; d->b = s->b >> 3; s++; d++; } } template void GRop32To15(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = s->r >> 3; d->g = s->g >> 3; d->b = s->b >> 3; s++; d++; } } template void GRop48To15(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = s->r >> 11; d->g = s->g >> 11; d->b = s->b >> 11; s++; d++; } } template void GRop64To15(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = s->r >> 11; d->g = s->g >> 11; d->b = s->b >> 11; s++; d++; } } ////////////////////////////////////////////////////////////////// // To 16 bits template void GRop15To16(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; if (IsOverlapping()) { - REG uint8 r, g, b; + REG uint8_t r, g, b; while (Px--) { r = s->r; g = s->g; b = s->b; d->r = r; d->g = (g << 1) | (g >> 4); d->b = b; s++; d++; } } else { while (Px--) { d->r = s->r; d->g = (uint16)(s->g << 1) | (s->g >> 4); d->b = s->b; s++; d++; } } } template void GRop16To16(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; if (IsOverlapping()) { - REG uint8 r, g, b; + REG uint8_t r, g, b; while (Px--) { r = s->r; g = s->g; b = s->b; d->r = r; d->g = g; d->b = b; s++; d++; } } else { while (Px--) { d->r = s->r; d->g = s->g; d->b = s->b; s++; d++; } } } template void GRop24To16(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = s->r >> 3; d->g = s->g >> 2; d->b = s->b >> 3; s++; d++; } } template void GRop32To16(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = s->r >> 3; d->g = s->g >> 2; d->b = s->b >> 3; s++; d++; } } template void GRop48To16(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = s->r >> 11; d->g = s->g >> 10; d->b = s->b >> 11; s++; d++; } } template void GRop64To16(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = s->r >> 11; d->g = s->g >> 10; d->b = s->b >> 11; s++; d++; } } ////////////////////////////////////////////////////////////////// // To 24 bits template void GRop15To24(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = G5bitTo8bit(s->r); d->g = G5bitTo8bit(s->g); d->b = G5bitTo8bit(s->b); s++; d++; } } template void GRop16To24(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = G5bitTo8bit(s->r); d->g = G6bitTo8bit(s->g); d->b = G5bitTo8bit(s->b); s++; d++; } } template void GRop24To24(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; - if ((uint8*)d == (uint8*)s) + if ((uint8_t*)d == (uint8_t*)s) { - REG uint8 r, g, b; + REG uint8_t r, g, b; d += px - 1; s += px - 1; while (Px--) { r = s->r; g = s->g; b = s->b; d->r = r; d->g = g; d->b = b; s--; d--; } } else { while (Px--) { d->r = s->r; d->g = s->g; d->b = s->b; s++; d++; } } } template void GRop32To24(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = s->r; d->g = s->g; d->b = s->b; s++; d++; } } template void GRop48To24(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = s->r >> 8; d->g = s->g >> 8; d->b = s->b >> 8; s++; d++; } } template void GRop64To24(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = s->r >> 8; d->g = s->g >> 8; d->b = s->b >> 8; s++; d++; } } ////////////////////////////////////////////////////////////////// // To 32 bits template void GRop15To32(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = G5bitTo8bit(s->r); d->g = G5bitTo8bit(s->g); d->b = G5bitTo8bit(s->b); d->a = 255; s++; d++; } } template void GRop16To32(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = G5bitTo8bit(s->r); d->g = G6bitTo8bit(s->g); d->b = G5bitTo8bit(s->b); d->a = 255; s++; d++; } } template void GRop24To32(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; if (IsOverlapping()) { - REG uint8 r, g, b; + REG uint8_t r, g, b; d += px - 1; s += px - 1; while (Px--) { r = s->r; g = s->g; b = s->b; d->r = r; d->g = g; d->b = b; d->a = 255; s--; d--; } } else { while (Px--) { d->r = s->r; d->g = s->g; d->b = s->b; d->a = 255; s++; d++; } } } template void GRop32To32(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; if (IsOverlapping()) { - REG uint8 r, g, b, a; + REG uint8_t r, g, b, a; while (Px--) { r = s->r; g = s->g; b = s->b; a = s->a; d->r = r; d->g = g; d->b = b; d->a = a; s++; d++; } } else { while (Px--) { d->r = s->r; d->g = s->g; d->b = s->b; d->a = s->a; s++; d++; } } } template void GRop48To32(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = s->r >> 8; d->g = s->g >> 8; d->b = s->b >> 8; d->a = 255; s++; d++; } } template void GRop64To32(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = s->r >> 8; d->g = s->g >> 8; d->b = s->b >> 8; d->a = s->a >> 8; s++; d++; } } ////////////////////////////////////////////////////////////////// // To 48 bits template void GRop15To48(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = (int)G5bitTo8bit(s->r) << 8; d->g = (int)G5bitTo8bit(s->g) << 8; d->b = (int)G5bitTo8bit(s->b) << 8; s++; d++; } } template void GRop16To48(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = (int)G5bitTo8bit(s->r) << 8; d->g = (int)G6bitTo8bit(s->g) << 8; d->b = (int)G5bitTo8bit(s->b) << 8; s++; d++; } } template void GRop24To48(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = G8bitTo16bit(s->r); d->g = G8bitTo16bit(s->g); d->b = G8bitTo16bit(s->b); s++; d++; } } template void GRop32To48(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = G8bitTo16bit(s->r); d->g = G8bitTo16bit(s->g); d->b = G8bitTo16bit(s->b); s++; d++; } } template void GRop48To48(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = s->r; d->g = s->g; d->b = s->b; s++; d++; } } template void GRop64To48(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = s->r; d->g = s->g; d->b = s->b; s++; d++; } } ////////////////////////////////////////////////////////////////// // To 64 bits template void GRop15To64(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = (int)G5bitTo8bit(s->r) << 8; d->g = (int)G5bitTo8bit(s->g) << 8; d->b = (int)G5bitTo8bit(s->b) << 8; d->a = 0xffff; s++; d++; } } template void GRop16To64(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = (int)G5bitTo8bit(s->r) << 8; d->g = (int)G6bitTo8bit(s->g) << 8; d->b = (int)G5bitTo8bit(s->b) << 8; d->a = 0xffff; s++; d++; } } template void GRop24To64(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = G8bitTo16bit(s->r); d->g = G8bitTo16bit(s->g); d->b = G8bitTo16bit(s->b); d->a = 0xffff; s++; d++; } } template void GRop32To64(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = G8bitTo16bit(s->r); d->g = G8bitTo16bit(s->g); d->b = G8bitTo16bit(s->b); d->a = G8bitTo16bit(s->a); s++; d++; } } template void GRop48To64(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = s->r; d->g = s->g; d->b = s->b; d->a = 0xffff; s++; d++; } } template void GRop64To64(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { d->r = s->r; d->g = s->g; d->b = s->b; d->a = s->a; s++; d++; } } template void GComposite15To24(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { if (s->a) { d->r = G5bitTo8bit(s->r); d->g = G5bitTo8bit(s->g); d->b = G5bitTo8bit(s->b); } s++; d++; } } template void GComposite15To32(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { if (s->a) { d->r = G5bitTo8bit(s->r); d->g = G5bitTo8bit(s->g); d->b = G5bitTo8bit(s->b); d->a = 0xff; } s++; d++; } } template void GComposite15To48(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { if (s->a) { d->r = G5bitTo8bit(s->r)<<8; d->g = G5bitTo8bit(s->g)<<8; d->b = G5bitTo8bit(s->b)<<8; } s++; d++; } } template void GComposite15To15(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { if (s->a) { d->r = s->r; d->g = s->g; d->b = s->b; } s++; d++; } } template void GComposite15To16(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { if (s->a) { d->r = s->r; d->g = s->g << 1; d->b = s->b; } s++; d++; } } template void GComposite15To64(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { if (s->a) { d->r = G5bitTo8bit(s->r) << 8; d->g = G5bitTo8bit(s->g) << 8; d->b = G5bitTo8bit(s->b) << 8; } s++; d++; } } template void GComposite32To15(OutPx *d, InPx *s, int Len) { InPx *end = s + Len; - uint8 *DivLut = Div255Lut; - REG uint8 sa; + uint8_t *DivLut = Div255Lut; + REG uint8_t sa; while (s < end) { sa = s->a; if (sa == 255) { // Copy pixel d->r = G8bitTo5bit(s->r); d->g = G8bitTo5bit(s->g); d->b = G8bitTo5bit(s->b); } else if (sa) { // Composite pixel // Dc' = (Sca + Dc.Da.(1 - Sa)) / Da' // Da' = Sa + Da.(1 - Sa) - REG uint8 o = 0xff - sa; - REG uint8 val; + REG uint8_t o = 0xff - sa; + REG uint8_t val; #define NonPreMul(c) \ val = DivLut[(s->c * sa) + (G5bitTo8bit(d->c) * o)]; \ d->c = G8bitTo5bit(val) NonPreMul(r); NonPreMul(g); NonPreMul(b); #undef NonPreMul } s++; d++; } } template void GComposite32To16(OutPx *d, InPx *s, int Len) { InPx *end = s + Len; - uint8 *DivLut = Div255Lut; - REG uint8 sa; + uint8_t *DivLut = Div255Lut; + REG uint8_t sa; while (s < end) { sa = s->a; if (sa == 255) { // Copy pixel d->r = G8bitTo5bit(s->r); d->g = G8bitTo6bit(s->g); d->b = G8bitTo5bit(s->b); } else if (sa) { // Composite pixel // Dc' = (Sca + Dc.Da.(1 - Sa)) / Da' // Da' = Sa + Da.(1 - Sa) - REG uint8 o = 0xff - sa; - REG uint8 val; + REG uint8_t o = 0xff - sa; + REG uint8_t val; #define NonPreMul(c, up, down) \ val = DivLut[(s->c * sa) + (G##up(d->c) * o)]; \ d->c = G##down(val) NonPreMul(r, 5bitTo8bit, 8bitTo5bit); NonPreMul(g, 6bitTo8bit, 8bitTo6bit); NonPreMul(b, 5bitTo8bit, 8bitTo5bit); #undef NonPreMul } s++; d++; } } template void GComposite32To24(OutPx *d, InPx *s, int Len) { InPx *end = s + Len; - uint8 *DivLut = Div255Lut; - REG uint8 sa; + uint8_t *DivLut = Div255Lut; + REG uint8_t sa; while (s < end) { sa = s->a; if (sa == 255) { // Copy pixel d->r = s->r; d->g = s->g; d->b = s->b; } else if (sa) { // Composite pixel // Dc' = (Sca + Dc.Da.(1 - Sa)) / Da' // Da' = Sa + Da.(1 - Sa) - REG uint8 o = 0xff - sa; + REG uint8_t o = 0xff - sa; #define NonPreMul(c) \ d->c = DivLut[(s->c * sa) + (d->c * o)] NonPreMul(r); NonPreMul(g); NonPreMul(b); #undef NonPreMul } s++; d++; } } template void GComposite32To32(OutPx *d, InPx *s, int Len) { InPx *end = s + Len; - uint8 *DivLut = Div255Lut; - REG uint8 sa; + uint8_t *DivLut = Div255Lut; + REG uint8_t sa; while (s < end) { sa = s->a; if (sa == 255) { // Copy pixel d->r = s->r; d->g = s->g; d->b = s->b; d->a = sa; } else if (sa) { // Composite pixel // Dc' = (Sca + Dc.Da.(1 - Sa)) / Da' // Da' = Sa + Da.(1 - Sa) OverNpm32toNpm32(s, d); } s++; d++; } } template void GComposite32To48(OutPx *d, InPx *s, int Len) { InPx *end = s + Len; - REG uint8 sa; + REG uint8_t sa; while (s < end) { sa = s->a; if (sa == 255) { // Copy pixel d->r = G8bitTo16bit(s->r); d->g = G8bitTo16bit(s->g); d->b = G8bitTo16bit(s->b); } else if (sa) { // Composite pixel // Da' = Sa + Da.(1 - Sa) // Dc' = (Sc.Sa + Dc.Da.(1 - Sa)) / Da' OverNpm32toNpm48(s, d); } s++; d++; } } template void GComposite32To64(OutPx *d, InPx *s, int Len) { InPx *end = s + Len; - REG uint8 sa; + REG uint8_t sa; while (s < end) { sa = s->a; if (sa == 255) { // Copy pixel d->r = G8bitTo16bit(s->r); d->g = G8bitTo16bit(s->g); d->b = G8bitTo16bit(s->b); d->a = G8bitTo16bit(sa); } else if (sa) { // Composite pixel // Dc' = (Sca + Dc.Da.(1 - Sa)) / Da' // Da' = Sa + Da.(1 - Sa) OverNpm32toNpm64(s, d); } s++; d++; } } template void GComposite32to24(OutPx *d, InPx *s, int Len) { REG OutPx *dst = d; REG InPx *src = s; REG InPx *end = src + Len; uint8 *DivLut = Div255Lut; REG uint8 sa; while (src < end) { sa = src->a; if (sa == 255) { // Copy pixel dst->r = src->r; dst->g = src->g; dst->b = src->b; } else if (sa) { // Composite pixel // Dc' = (Sc.Sa + Dc.Da.(1 - Sa)) / Da' // Da' = Sa + Da.(1 - Sa) REG uint8 o = 0xff - sa; #define NonPreMul24(c) \ dst->c = DivLut[(src->c * sa) + (dst->c * o)] NonPreMul24(r); NonPreMul24(g); NonPreMul24(b); } src++; dst++; } } template void GComposite64To15(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; - REG uint8 *DivLut = Div255Lut; + REG uint8_t *DivLut = Div255Lut; OverlapCheck() while (Px--) { REG GRgb24 dst = { G5bitTo8bit(d->r), G5bitTo8bit(d->g), G5bitTo8bit(d->b) }; OverNpm64toNpm24(s, &dst); d->r = dst.r >> 3; d->g = dst.g >> 3; d->b = dst.b >> 3; s++; d++; } } template void GComposite64To16(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; - REG uint8 *DivLut = Div255Lut; + REG uint8_t *DivLut = Div255Lut; OverlapCheck() while (Px--) { REG GRgb24 dst = { G5bitTo8bit(d->r), G6bitTo8bit(d->g), G5bitTo8bit(d->b) }; OverNpm64toNpm24(s, &dst); d->r = dst.r >> 3; d->g = dst.g >> 2; d->b = dst.b >> 3; s++; d++; } } template void GComposite64To24(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; - REG uint8 *DivLut = Div255Lut; + REG uint8_t *DivLut = Div255Lut; OverlapCheck() while (Px--) { OverNpm64toNpm24(s, d); s++; d++; } } template void GComposite64To32(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; - REG uint8 *DivLut = Div255Lut; + REG uint8_t *DivLut = Div255Lut; OverlapCheck() while (Px--) { OverNpm64toNpm32(s, d); s++; d++; } } template void GComposite64To48(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { OverNpm64toNpm48(s, d); s++; d++; } } template void GComposite64To64(DstPx *dst, SrcPx *src, int px) { REG DstPx *d = dst; REG SrcPx *s = src; REG int Px = px; OverlapCheck() while (Px--) { OverNpm64toNpm64(s, d); s++; d++; } } #endif \ No newline at end of file diff --git a/include/common/GScripting.h b/include/common/GScripting.h --- a/include/common/GScripting.h +++ b/include/common/GScripting.h @@ -1,429 +1,429 @@ /// \file #ifndef _LGI_SCRIPTING_H_ #define _LGI_SCRIPTING_H_ #include "GVariant.h" #include "LList.h" class GScriptContext; class GScriptEnginePrivate; class GVmDebuggerCallback; class GVirtualMachine; class LScriptArguments : public GArray { friend class GScriptEngine; friend class GVirtualMachine; GVirtualMachine *Vm; GStream *Console; GVariant _Return; GVariant *PtrRet; public: static GStream NullConsole; LScriptArguments(GVirtualMachine *vm, GVariant *ret = NULL, GStream *console = NULL) { Vm = vm; if (ret) PtrRet = ret; else PtrRet = &_Return; if (console) Console = console; else Console = &NullConsole; } GVariant *GetReturn() { return PtrRet; } GStream *GetConsole() { return Console; } bool Throw(const char *File, int Line, const char *Msg, ...); }; typedef bool (GScriptContext::*ScriptCmd)(LScriptArguments &Args); #define SCOPE_REGISTER 0 #define SCOPE_LOCAL 1 #define SCOPE_GLOBAL 2 #define SCOPE_OBJECT 3 #define SCOPE_RETURN 4 #define SCOPE_MAX 5 /// Execution status enum GExecutionStatus { ScriptError, ScriptWarning, ScriptSuccess, }; /// Various type of methods enum GFuncType { /// No method type. NullFunc, /// This method is provided by the hosting application. HostFunc, /// This method is defined in the script itself. ScriptFunc, /// This method is defined in an external library. ExternFunc }; struct GFunc { GFuncType Type; GString Method; GFunc(const char *m = 0, GFuncType t = NullFunc) { Type = t; Method = m; } virtual ~GFunc() { } virtual GExecutionStatus Call(GScriptContext *Ctx, LScriptArguments &Args) = 0; }; struct GHostFunc : public GFunc { GScriptContext *Context; GString Args; ScriptCmd Func; GHostFunc(const GHostFunc &f) { Context = f.Context; Args = f.Args; Method = f.Method; Func = f.Func; } GHostFunc(const char *method, const char *args, ScriptCmd proc) : GFunc(method, HostFunc) { Args = args; Func = proc; } GExecutionStatus Call(GScriptContext *Ctx, LScriptArguments &Args) override; }; struct GExternFunc : public GFunc { struct ExternType { int Ptr; bool Unsigned; bool Out; int ArrayLen; GVariantType Base; }; GAutoString Lib; ExternType ReturnType; GArray ArgType; GExecutionStatus Call(GScriptContext *Ctx, LScriptArguments &Args) override; }; class GFunctionInfo : public GRefCount { friend class GVirtualMachinePriv; friend class GCompilerPriv; static int _Infos; int32 StartAddr; GString Name; // The reason why this is a pointer is because during the function compilation the frame // size is actually unknown. If the function calls itself then it won't know what // frame size to insert in the assembly (this is NULL). // In which case it has to insert a post compilation fix-up for the frame size. GAutoPtr FrameSize; // The number and names of the parameters to the function. GArray Params; public: GFunctionInfo(const char *name) { StartAddr = 0; if (name) Name = name; } ~GFunctionInfo() { } char *GetName() { return Name; } size_t GetParams() { return Params.Length(); } GFunctionInfo &operator =(GFunctionInfo &f) { StartAddr = f.StartAddr; FrameSize = f.FrameSize; Name = f.Name; for (unsigned i=0; i { friend class GVirtualMachinePriv; LHashTbl,int> Lut; public: int Scope; int NullIndex; GCustomType *Obj; GVariables(int scope) { Scope = scope; NullIndex = -1; Obj = NULL; } GVariables(GCustomType *obj) { Scope = SCOPE_OBJECT; NullIndex = -1; Obj = obj; } int Var(const char *n, bool create = false) { if (Obj) { return Obj->IndexOf(n); } else { int p = Lut.Find(n); if (p) { return p - 1; } if (create) { int Len = (int)Length(); Lut.Add(n, Len + 1); Length(Len + 1); return Len; } } return -1; } }; /// A block of compile byte code class GCompiledCode { friend class GCompilerPriv; friend class GVirtualMachinePriv; friend class GCompiler; friend class GVmDebuggerWnd; /// The global variables GVariables Globals; /// The byte code of all the instructions - GArray ByteCode; + GArray ByteCode; /// All the methods defined in the byte code and their arguments. GArray< GAutoRefPtr > Methods; /// All the externs defined in the code. GArray Externs; /// All the user types defined LHashTbl, class GCustomType*> Types; /// The original script details GString FileName; GString Source; /// The system context (all the system functions) GScriptContext *SysContext; /// Any user context (application functions) GScriptContext *UserContext; /// Debug info to map instruction address back to source line numbers LHashTbl, int> Debug; public: GCompiledCode(); GCompiledCode(GCompiledCode ©); ~GCompiledCode(); /// Size of the byte code size_t Length() { return ByteCode.Length(); } /// Assignment operator GCompiledCode &operator =(const GCompiledCode &c); /// Gets a method defined in the code GFunctionInfo *GetMethod(const char *Name, bool Create = false); /// Sets a global variable GVariant *Set(const char *Name, GVariant &v); /// Gets the definition of a struct or custom type GCustomType *GetType(char16 *Name) { return Types.Find(Name); } /// Gets the file name this code was compiled from const char *GetFileName() { return FileName; } /// Gets the source const char *GetSource() { return Source; } /// Gets the source line number associated with an address int ObjectToSourceAddress(size_t ObjAddr); /// Turns an object address into a FileName:LineNumber string. const char *AddrToSourceRef(size_t ObjAddr); /// Sets the file name this code was compiled from void SetSource(const char *file, const char *src) { if (file != FileName.Get()) FileName = file; Source = src; } }; /// New compiler/byte code/VM scripting engine class GScriptEngine { class GScriptEnginePrivate *d; public: GScriptEngine(GViewI *parent, GScriptContext *UserContext, GVmDebuggerCallback *Callback); ~GScriptEngine(); GStream *GetConsole(); bool SetConsole(GStream *t); GCompiledCode *GetCurrentCode(); bool Compile( GAutoPtr &Obj, GScriptContext *UserContext, char *Script, const char *FileName = NULL, GDom *Args = NULL); GExecutionStatus Run(GCompiledCode *Obj, GVariant *Ret = NULL, const char *TempPath = NULL); GExecutionStatus RunTemporary(GCompiledCode *Obj, char *Script, GVariant *Ret = NULL); bool EvaluateExpression(GVariant *Result, GDom *VariableSource, char *Expression); bool CallMethod(GCompiledCode *Obj, const char *Method, LScriptArguments &Args); GScriptContext *GetSystemContext(); }; class GVirtualMachine; class GVmDebugger : public GDom { public: /// Set the VM ownership flag. virtual void OwnVm(bool Own) = 0; /// Makes the debugger the owner of the compiled code virtual void OwnCompiledCode(GAutoPtr Cc) = 0; /// Gets the code owned by the debugger virtual GCompiledCode *GetCode() = 0; /// Set the source and asm virtual void SetSource(const char *Mixed) = 0; /// Show UI and wait for user response virtual void Run() = 0; // Events /// Called to update the debugger UI to the new current execution position virtual void OnAddress(size_t Addr) = 0; /// Called when an error occurs executing the script virtual void OnError(const char *Msg) = 0; /// Called when execution starts or ends virtual void OnRun(bool Running) = 0; }; class GVmDebuggerCallback : public GDom { public: /// Start a debugger instance to handle the execution in 'Vm' virtual GVmDebugger *AttachVm(GVirtualMachine *Vm, GCompiledCode *Code, const char *Assembly) = 0; /// Compile a new script virtual bool CompileScript(GAutoPtr &Output, const char *FileName, const char *Source) = 0; }; /// Debugger for vm script class GVmDebuggerWnd : public GWindow, public GVmDebugger { struct GScriptVmDebuggerPriv *d; void UpdateVariables(LList *Lst, GVariant *Arr, ssize_t Len, char Prefix); public: GVmDebuggerWnd(GView *Parent, GVmDebuggerCallback *Callback, GVirtualMachine *Vm, GCompiledCode *Code, const char *Assembly); ~GVmDebuggerWnd(); void OwnVm(bool Own); void OnAddress(size_t Addr); void OnError(const char *Msg); void OnRun(bool Running); void SetSource(const char *Mixed); int OnNotify(GViewI *Ctrl, int Flags); int OnCommand(int Cmd, int Event, OsView Wnd); bool OnRequestClose(bool OsShuttingDown); GMessage::Param OnEvent(GMessage *Msg); void LoadFile(const char *File); GStream *GetLog(); void OwnCompiledCode(GAutoPtr Cc); GCompiledCode *GetCode(); void Run(); }; #endif diff --git a/include/common/GSkinEngine.h b/include/common/GSkinEngine.h --- a/include/common/GSkinEngine.h +++ b/include/common/GSkinEngine.h @@ -1,147 +1,147 @@ #ifndef _GSKIN_ENGINE_H_ #define _GSKIN_ENGINE_H_ #include "LList.h" // Feature support flags #define GSKIN_COLOUR 0x00000001 #define GSKIN_BUTTON 0x00000002 #define GSKIN_EDIT 0x00000004 #define GSKIN_CHECKBOX 0x00000008 #define GSKIN_RADIO 0x00000010 #define GSKIN_GROUP 0x00000020 #define GSKIN_SLIDER 0x00000040 #define GSKIN_COMBO 0x00000080 #define GSKIN_BITMAP 0x00000100 #define GSKIN_PROGRESS 0x00000200 #define GSKIN_TREE 0x00000400 #define GSKIN_LIST 0x00000800 #define GSKIN_LISTCOL 0x00001000 #define GSKIN_TABVIEW 0x00002000 // Drawing state class GSkinState { GArray Tmp; public: int Size; // Class size, for version checking GSurface *pScreen; // Output surface GArray *aText; // Array of display strings for the view GDisplayString **ptrText; // Ptr to ptr for display string GRect Rect; // Region to paint (if relevant) bool MouseOver; // TRUE if the mouse is over the view int64 Value; // Value of the control if available bool Enabled; // TRUE if the control is enabled GSurface *Image; // Any icon that needs displaying GSkinState() { aText = NULL; ptrText = NULL; Value = 0; Enabled = true; Size = sizeof(*this); pScreen = 0; MouseOver = false; Image = NULL; } size_t TextObjects() { if (aText) return aText->Length(); if (ptrText) return *ptrText != NULL ? 1 : 0; return 0; } GDisplayString *FirstText() { if (aText) { return aText->Length() > 0 ? aText->First() : NULL; } else if (ptrText) { return *ptrText; } return NULL; } GArray *AllText() { if (aText) return aText; if (ptrText && *ptrText && Tmp.Length(1)) { Tmp[0] = *ptrText; return &Tmp; } return NULL; } }; typedef void (*ProcColumnPaint)(void *UserData, GSurface *pDC, GRect &r, bool FillBackground); // Engine class class GSkinEngine { public: virtual ~GSkinEngine() {} // Return the features the skin supports. Return the // bitwise OR of all the features you support (the GSKIN_?? flags) // // The class is designed to be extended by adding more virtual functions // on the bottom and then Lgi queries the class to see if it can call them // by using the flags returned from this function. At no time should the // existing functions change order or be removed. - virtual uint32 GetFeatures() { return 0; } + virtual uint32_t GetFeatures() { return 0; } // Return the RGB24 for the LC_??? colour index // Will only be called if you return GSKIN_COLOUR from GetFeatures() - virtual uint32 GetColour(int i) { return 0; } + virtual uint32_t GetColour(int i) { return 0; } // Do painting for the various controls, the relevant GSKIN_??? flag needs to // be returned from GetFeatures before you can call any of these. virtual void OnPaint_GText (GTextLabel *Ctrl, GSkinState *State) {}; virtual void OnPaint_GButton (GButton *Ctrl, GSkinState *State) {}; virtual void OnPaint_GEdit (GEdit *Ctrl, GSkinState *State) {}; virtual void OnPaint_GCheckBox (GCheckBox *Ctrl, GSkinState *State) {}; virtual void OnPaint_GRadioGroup (GRadioGroup *Ctrl, GSkinState *State) {}; virtual void OnPaint_GRadioButton (GRadioButton *Ctrl, GSkinState *State) {}; virtual void OnPaint_GTabView (GTabView *Ctrl, GSkinState *State) {}; virtual void OnPaint_GSlider (GSlider *Ctrl, GSkinState *State) {}; virtual void OnPaint_GCombo (GCombo *Ctrl, GSkinState *State) {}; virtual void OnPaint_GBitmap (GBitmap *Ctrl, GSkinState *State) {}; virtual void OnPaint_GProgress (GProgress *Ctrl, GSkinState *State) {}; virtual void OnPaint_GTree (GTree *Ctrl, GSkinState *State) {}; virtual void OnPaint_LList (LList *Ctrl, GSkinState *State) {}; // 'Col' may be NULL in the case that the GList control wants to draw the // header beyond the last column. This function can use the call back // GListColumn::OnPaint_Content to draw the text, icon and mark. virtual void OnPaint_ListColumn(ProcColumnPaint Callback, void *UserData, GSkinState *State) = 0; // Get the default font for a control virtual GFont *GetDefaultFont(char *Class) { return SysFont; } // Fills an abitary path with the skin's default fill... virtual void FillPath(class GPath *Path, GSurface *pDC, bool Down, bool Enabled = true) {} // Draws a button virtual void DrawBtn(GSurface *pDC, GRect &r, GColour *Base, bool Down, bool Enabled, bool Default = false) = 0; /////////////////////////////////////////////////////////////////////////////// // Add new features down here with an associated feature flag defined above. // /////////////////////////////////////////////////////////////////////////////// }; #define LgiSkinEntryPoint "CreateSkinEngine" typedef GSkinEngine *(*Proc_CreateSkinEngine)(class GApp *App); #endif \ No newline at end of file diff --git a/include/common/GSlider.h b/include/common/GSlider.h --- a/include/common/GSlider.h +++ b/include/common/GSlider.h @@ -1,53 +1,53 @@ /// \file /// \author Matthew Allen (fret@memecode.com) /// \brief A slider control #ifndef _GSLIDER_H_ #define _GSLIDER_H_ /// Slider class LgiClass GSlider : public GControl, public ResObject { #if WINNATIVE - uint32 Style(); + uint32_t Style(); // int SysOnNotify(int Msg, int Code); #endif bool Vertical; int64 Min, Max; int64 Val; GRect Thumb; int Tx, Ty; public: GSlider(int id, int x = 0, int y = 0, int cx = 100, int cy = 20, const char *name = NULL, bool vert = false); ~GSlider(); const char *GetClass() { return "GSlider"; } /// Sets the position of the slider void Value(int64 i); /// Gets the position of the slider int64 Value(); /// Gets the limits of the slider void GetLimits(int64 &x, int64 &y); /// Sets the limits of the slider void SetLimits(int64 x, int64 y); GMessage::Result OnEvent(GMessage *Msg); #if !WINNATIVE void OnPaint(GSurface *pDC); void OnMouseClick(GMouse &m); void OnMouseMove(GMouse &m); #endif }; #endif diff --git a/include/common/GString.h b/include/common/GString.h --- a/include/common/GString.h +++ b/include/common/GString.h @@ -1,314 +1,314 @@ /// \file /// \author Matthew Allen (fret@memecode.com) #ifndef GSTRING_H #define GSTRING_H // #include "LgiInc.h" #include "LgiDefs.h" #include "LgiOsDefs.h" ///////////////////////////////////////////////////////////////////// // Strings // Externs LgiExtern char WhiteSpace[]; // Defines #ifndef ToUpper #define ToUpper(c) ((c) >= 'a' && (c) <= 'z' ? (c)-'a'+'A' : (c)) #endif #ifndef ToLower #define ToLower(c) ((c) >= 'A' && (c) <= 'Z' ? (c)-'A'+'a' : (c)) #endif // Functions template Char *StrLwr(Char *c) { for (Char *s = c; s && *s; s++) { if (*s >= 'A' && *s <= 'Z') *s = *s - 'A' + 'a'; } return c; } template Char *StrUpr(Char *c) { for (Char *s = c; s && *s; s++) { if (*s >= 'a' && *s <= 'z') *s = *s - 'a' + 'A'; } return c; } template int StringLen(T *s) { if (!s) return 0; int len = 0; while (s[len]) len++; return len; } // 8 bit strings /// Returns a pointer to the char 'c' if found in the first 'Len' bytes of the string 's' LgiFunc char *strnchr ( /// The string to search const char *s, /// The character to find char c, /// The maximum number of bytes to search NativeInt Len ); #if defined(MAC) LgiFunc char *strncpy_s(char *dest, size_t dest_size, const char *src, size_t src_size); #else /// \brief Search for a substring in another string. /// /// The search is case sensitive. /// /// \returns A pointer to the sub-string or NULL if not found LgiFunc char *strnstr ( /// The string to search const char *a, /// The string to find const char *b, /// The maximum number of bytes in 'a' to seach through size_t n ); #endif /// \brief Search for a case insensitive sub-string in another string /// /// The search is not case sensitive. /// /// \returns A pointer to the sub-string or NULL if not found. LgiFunc char *strnistr ( /// The string to search char *a, /// The string to find const char *b, /// The maximum number of bytes of 'a' to search. size_t n ); /// \brief Case insensitive sub-string search. /// /// The search is not case sensitive. /// /// \returns A pointer to the sub-string or NULL if not found. // LgiFunc LgiFunc char *stristr ( /// The string to search const char *a, /// The string to find const char *b ); #if LGI_DRMEMORY /// String compare function #define strcmp strcompare LgiFunc int strcompare(const char *a, const char *b, bool case_sensitive = true); /// Find character in string function #define strchr strchar LgiFunc char *strchar(const char *a, int ch); /// Append to a string #define strcat strconcat LgiFunc char *strconcat(char *dst, const char *src); #endif #if !defined(_MSC_VER) #if !defined(WIN32) LgiFunc int strnicmp(const char *a, const char *b, ssize_t i); #endif /// \brief Safe string copy /// /// This function should be used anytime the size of the destination /// buffer is known when using strcpy. It will truncate the resultant /// string to fit in the output buffer, properly NULL terminating it. LgiFunc char *strcpy_s ( /// The destination string buffer char *dst, /// The size in bytes of 'dst' size_t len, /// The string to append to 'dst' const char *src ); /// \brief Safe string copy LgiFunc char *strncpy_s ( /// The destination string buffer char *dst, /// The size in bytes of 'dst' size_t dst_len, /// The string to append to 'dst' const char *src, /// Max size of src size_t src_len ); /// \brief Safe string append /// /// This function should be used anytime the size of the destination /// buffer is known when using strcat. It will truncate the resultant /// string to fit in the output buffer, properly NULL terminating it. LgiFunc char *strcat_s ( /// The destination string buffer char *dst, /// The size in bytes of 'dst' size_t len, /// The string to append to 'dst' const char *src ); #endif /// \brief Converts a hex string into a integer. /// /// Stops scanning when it hits a NULL or a non-hex character. Accepts /// input characters in the ranges 0-9, a-f and A-F. template Ret HexToInt ( /// The string of hex characters const Char *a ) { Ret Status = 0; if (a[0] == '0' && (a[1] == 'x' || a[1] == 'X')) a += 2; for (; a && *a; a++) { if (*a >= '0' && *a <= '9') { Status <<= 4; Status |= *a - '0'; } else if (*a >= 'a' && *a <= 'f') { Status <<= 4; Status |= *a - 'a' + 10; } else if (*a >= 'A' && *a <= 'F') { Status <<= 4; Status |= *a - 'A' + 10; } else break; } return Status; } template -uint32 htoi(const Char *s) +uint32_t htoi(const Char *s) { - return HexToInt(s); + return HexToInt(s); } template uint64 htoi64(const Char *s) { return HexToInt(s); } /// Trims delimiter characters off a string. /// /// \returns A dynamically allocated copy of the input without any delimiter characters /// on the start or end. LgiFunc char *TrimStr(const char *s, const char *Delim = " \r\n\t"); /// Returns true if the string points to something with one or more non-whitespace characters. LgiFunc bool ValidStr(const char *s); /// Makes a heap allocated copy of a string. LgiFunc char *NewStr ( /// The input string const char *s, /// The maximum number of bytes in the input string to use or -1 for the whole string. ptrdiff_t Len = -1 ); /// Does a wildcard match. LgiFunc bool MatchStr ( /// The wildcard template const char *Template, /// The string to test against. const char *Data ); /// Find a character in a wide string LgiFunc char16 *StrchrW(const char16 *s, char16 c); /// Find the last instance of a character in a wide string LgiFunc char16 *StrrchrW(char16 *s, char16 c); /// Find a character in the first 'n' characters of a wide string LgiFunc char16 *StrnchrW(char16 *s, char16 c, int Len); /// Find a sub-string in a wide string (case sensitive) LgiFunc char16 *StrstrW(char16 *a, const char16 *b); /// Find a sub-string in a wide string (case insensitive) LgiFunc char16 *StristrW(char16 *a, const char16 *b); /// Find a sub-string in the first 'n' characters of a wide string (case sensitive) LgiFunc char16 *StrnstrW(char16 *a, const char16 *b, ssize_t n); /// Find a sub-string in the first 'n' characters of a wide string (case insensitive) LgiFunc char16 *StrnistrW(char16 *a, const char16 *b, ssize_t n); /// Compare wide strings (case sensitive) LgiFunc int StrcmpW(const char16 *a, const char16 *b); /// Compare wide strings (case insensitive) LgiFunc int StricmpW(const char16 *a, const char16 *b); /// Compare 'n' characters of 2 wide strings (case sensitive) LgiFunc int StrncmpW(const char16 *a, const char16 *b, ssize_t n); /// Compare 'n' characters of 2 wide strings (case insensitive) LgiFunc int StrnicmpW(const char16 *a, const char16 *b, ssize_t n); /// String copy one wide string to another LgiFunc char16 *StrcpyW(char16 *a, const char16 *b); /// String copy a maximum of 'n' characters of one wide string to another LgiFunc char16 *StrncpyW(char16 *a, const char16 *b, ssize_t n); /// Count the number of char16's in a wide string LgiFunc ssize_t StrlenW(const char16 *a); /// Append a wide string to another LgiFunc void StrcatW(char16 *a, const char16 *b); /// Convert a wide string to an integer LgiFunc int AtoiW(const char16 *a); /// Convert a wide hex string to an integer LgiFunc int HtoiW(const char16 *a); /// Convert a wide hex string to an 64bit integer LgiFunc int64 HtoiW64(const char16 *a); /// Makes a heap allocated copy of a wide string. LgiFunc char16 *NewStrW ( /// The input string const char16 *s, /// The maximum number of characters in the input string to use or -1 for the whole string. ssize_t CharLen = -1 ); /// Trim delimiters from a wide string. Returns a heap allocated string. LgiFunc char16 *TrimStrW(const char16 *s, const char16 *Delim = 0); /// Returns true if 's' points to a wide string with at least 1 non-whitespace character. LgiFunc bool ValidStrW(const char16 *s); /// Does a widecard match between wide strings. LgiFunc bool MatchStrW(const char16 *Template, const char16 *Data); #endif diff --git a/include/common/GStringClass.h b/include/common/GStringClass.h --- a/include/common/GStringClass.h +++ b/include/common/GStringClass.h @@ -1,1187 +1,1187 @@ /* * A mis-guided attempt to make a string class look and feel like a python string. * * Author: Matthew Allen * Email: fret@memecode.com * Created: 16 Sept 2014 */ #ifndef _GSTRING_CLASS_H_ #define _GSTRING_CLASS_H_ #include #include #include #ifdef _MSC_VER // This fixes compile errors in VS2008/Gtk #undef _SIGN_DEFINED #undef abs #endif #include #if defined(_MSC_VER) && _MSC_VER < 1800/*_MSC_VER_VS2013*/ #include #define PRId64 "I64i" #else #define __STDC_FORMAT_MACROS 1 #include #include #ifndef PRId64 #warning "PRId64 not defined." #define PRId64 "Ld" #endif #endif #include "GUnicode.h" #include "GArray.h" #include "LgiClass.h" #ifndef IsDigit #define IsDigit(ch) ((ch) >= '0' && (ch) <= '9') #endif LgiExtern int LgiPrintf(class GString &Str, const char *Format, va_list &Arg); /// A pythonic string class. class GString { protected: /// This structure holds the string's data itself and is shared /// between one or more GString instances. struct RefStr { /// A reference count int32 Refs; /// The bytes in 'Str' not including the NULL terminator size_t Len; /// The first byte of the string. Further bytes are allocated /// off the end of the structure using malloc. This must always /// be the last element in the struct. char Str[1]; } *Str; inline void _strip(GString &ret, const char *set, bool left, bool right) { if (!Str) return; char *s = Str->Str; char *e = s + Str->Len; if (!set) set = " \t\r\n"; if (left) { while (s < e && strchr(set, *s)) s++; } if (right) { while (e > s && strchr(set, e[-1])) e--; } if (e > s) ret.Set(s, e - s); } public: #ifdef LGI_UNIT_TESTS static int32 RefStrCount; #endif /// A copyable array of strings class Array : public GArray { public: Array(size_t PreAlloc = 0) : GArray(PreAlloc) {} Array(const Array &a) { *this = (Array&)a; } Array &operator =(const Array &a) { SetFixedLength(false); *((GArray*)this) = a; SetFixedLength(true); return *this; } }; /// Empty constructor GString() { Str = NULL; } // This odd looking constructor allows the object to be used as the value type // in a GHashTable, where the initialiser is '0', an integer. GString(int i) { Str = NULL; } #ifndef _MSC_VER // This odd looking constructor allows the object to be used as the value type // in a GHashTable, where the initialiser is '0', an integer. GString(long int i) { Str = NULL; } #endif /// String constructor GString(const char *str, ptrdiff_t bytes) { Str = NULL; Set(str, bytes); } /// const char* constructor GString(const char *str) { Str = NULL; Set(str); } /// const char16* constructor GString(const wchar_t *str, ptrdiff_t wchars = -1) { Str = NULL; SetW(str, wchars); } #if defined(_WIN32) || defined(MAC) /// const uint32* constructor - GString(const uint32 *str, ptrdiff_t chars = -1) + GString(const uint32_t *str, ptrdiff_t chars = -1) { Str = NULL; if (chars < 0) chars = Strlen(str); ptrdiff_t utf_len = 0; - const uint32 *end = str + chars; - const uint32 *c = str; + const uint32_t *end = str + chars; + const uint32_t *c = str; while (c < end) { - uint8 utf[6], *u = utf; + uint8_t utf[6], *u = utf; ssize_t len = sizeof(utf); if (!LgiUtf32To8(*c++, u, len)) break; utf_len += u - utf; } - if (Length((uint32)utf_len)) + if (Length((uint32_t)utf_len)) { c = str; - uint8 *u = (uint8*)Str->Str; + uint8_t *u = (uint8_t*)Str->Str; ssize_t len = Str->Len; while (c < end) { if (!LgiUtf32To8(*c++, u, len)) break; } *u++ = 0; } } #endif /// GString constructor GString(const GString &s) { Str = s.Str; if (Str) Str->Refs++; } ~GString() { Empty(); } /// Removes a reference to the string and deletes if needed void Empty() { if (!Str) return; Str->Refs--; assert(Str->Refs >= 0); if (Str->Refs == 0) { free(Str); #ifdef LGI_UNIT_TESTS RefStrCount--; #endif } Str = NULL; } /// Returns the pointer to the string data char *Get() const { return Str ? Str->Str : NULL; } /// Sets the string to a new value bool Set ( /// Can be a pointer to string data or NULL to create an empty buffer (requires valid length) const char *str, /// Byte length of input string or -1 to copy till the NULL terminator. ptrdiff_t bytes = -1 ) { Empty(); if (bytes < 0) { if (str) bytes = strlen(str); else return false; } Str = (RefStr*)malloc(sizeof(RefStr) + bytes); if (!Str) return false; Str->Refs = 1; - Str->Len = (uint32)bytes; + Str->Len = (uint32_t)bytes; #ifdef LGI_UNIT_TESTS RefStrCount++; #endif if (str) memcpy(Str->Str, str, bytes); Str->Str[bytes] = 0; return true; } /// Sets the string to a new value bool SetW ( /// Can be a pointer to string data or NULL to create an empty buffer (requires valid length) const wchar_t *str, /// Number of 'char16' values in the input string or -1 to copy till the NULL terminator. ptrdiff_t wchars = -1 ) { size_t Sz = WideToUtf8Len(str, wchars); if (Length(Sz)) { #ifdef _MSC_VER const uint16 *i = (const uint16*) str; ssize_t InLen = wchars >= 0 ? wchars << 1 : 0x7fffffff; assert(sizeof(*i) == sizeof(*str)); - uint8 *o = (uint8*)Str->Str; + uint8_t *o = (uint8_t*)Str->Str; ssize_t OutLen = Str->Len; - for (uint32 ch; ch = LgiUtf16To32(i, InLen); ) + for (uint32_t ch; ch = LgiUtf16To32(i, InLen); ) { if (!LgiUtf32To8(ch, o, OutLen)) { *o = 0; break; } } #else uint8 *o = (uint8*)Str->Str; ssize_t OutLen = Str->Len; if (wchars >= 0) { const wchar_t *end = str + wchars; for (const wchar_t *ch = str; ch < end; ch++) { if (!LgiUtf32To8(*ch, o, OutLen)) { *o = 0; break; } } } else { for (const wchar_t *ch = str; *ch; ch++) { if (!LgiUtf32To8(*ch, o, OutLen)) { *o = 0; break; } } } #endif *o = 0; } return true; } /// Equality operator (case sensitive) bool operator ==(const GString &s) { const char *a = Get(); const char *b = s.Get(); if (!a && !b) return true; if (!a || !b) return false; return !strcmp(a, b); } bool operator !=(const GString &s) { return !(*this == s); } // Equality function (default: case insensitive, as the operator== is case sensitive) bool Equals(const char *b, bool CaseInsensitive = true) const { const char *a = Get(); if (!a && !b) return true; if (!a || !b) return false; return !(CaseInsensitive ? _stricmp(a, b) : strcmp(a, b)); } // Equality function (default: case insensitive, as the operator== is case sensitive) bool Equals(const GString &s, bool CaseInsensitive = true) const { const char *a = Get(); const char *b = s.Get(); if (!a && !b) return true; if (!a || !b) return false; return !(CaseInsensitive ? _stricmp(a, b) : strcmp(a, b)); } /// Assignment operator to copy one string to another GString &operator =(const GString &s) { if (this != &s) { Empty(); Str = s.Str; if (Str) Str->Refs++; } return *this; } /// Equality with a C string (case sensitive) bool operator ==(const char *b) { const char *a = Get(); if (!a && !b) return true; if (!a || !b) return false; return !strcmp(a, b); } bool operator !=(const char *b) { return !(*this == b); } /// Assignment operators GString &operator =(const char *s) { if (Str == NULL || s < Str->Str || s > Str->Str + Str->Len) { Empty(); Set(s); } else if (s != Str->Str) { // Special case for setting it to part of itself // If you try and set a string to the start, it's a NOP ptrdiff_t Off = s - Str->Str; memmove(Str->Str, s, Str->Len - Off + 1); - Str->Len -= (uint32)Off; + Str->Len -= (uint32_t)Off; } return *this; } GString &operator =(const wchar_t *s) { SetW(s); return *this; } GString &operator =(int val) { char n[32]; sprintf_s(n, sizeof(n), "%i", val); Set(n); return *this; } GString &operator =(int64 val) { char n[32]; sprintf_s(n, sizeof(n), "%" PRId64, (int64_t)val); Set(n); return *this; } /// Cast to C string operator operator char *() { return Str ? Str->Str : NULL; } operator const char *() const { return Str ? Str->Str : NULL; } /// Concatenation operator GString operator +(const GString &s) { GString Ret; size_t Len = Length() + s.Length(); if (Ret.Set(NULL, Len)) { char *p = Ret.Get(); if (p) { if (Str) { memcpy(p, Str->Str, Str->Len); p += Str->Len; } if (s.Str) { memcpy(p, s.Str->Str, s.Str->Len); p += s.Str->Len; } *p++ = 0; } } return Ret; } /// Concatenation / assignment operator GString &operator +=(const GString &s) { ssize_t Len = Length() + s.Length(); ssize_t Alloc = sizeof(RefStr) + Len; RefStr *rs = (RefStr*)malloc(Alloc); if (rs) { rs->Refs = 1; rs->Len = Len; #ifdef LGI_UNIT_TESTS RefStrCount++; #endif char *p = rs->Str; if (Str) { memcpy(p, Str->Str, Str->Len); p += Str->Len; } if (s.Str) { memcpy(p, s.Str->Str, s.Str->Len); p += s.Str->Len; } *p++ = 0; assert(p - (char*)rs <= Alloc); Empty(); Str = rs; } return *this; } /// Gets the length in bytes size_t Length() const { return Str ? Str->Len : 0; } size_t Length(size_t NewLen) { if (Str) { if (NewLen < Str->Len) { Str->Len = NewLen; Str->Str[NewLen] = 0; } else { RefStr *n = (RefStr*)malloc(sizeof(RefStr) + NewLen); if (n) { n->Len = NewLen; n->Refs = 1; memcpy(n->Str, Str->Str, Str->Len); n->Str[Str->Len] = 0; // NULL terminate... Empty(); // Deref the old string... Str = n; } else return 0; } } else { Str = (RefStr*)malloc(sizeof(RefStr) + NewLen); if (Str) { Str->Len = NewLen; Str->Refs = 1; Str->Str[0] = 0; // NULL terminate... } else return 0; } return Str->Len; } /// Splits the string into parts using a separator Array Split(const char *Sep, int Count = -1, bool CaseSen = false) { Array a; if (Str && Sep) { const char *s = Str->Str, *Prev = s; const char *end = s + Str->Len; size_t SepLen = strlen(Sep); if (s[Str->Len] == 0) { while ((s = CaseSen ? strstr(s, Sep) : Stristr(s, Sep))) { if (s > Prev) a.New().Set(Prev, s - Prev); s += SepLen; Prev = s; - if (Count > 0 && a.Length() >= (uint32)Count) + if (Count > 0 && a.Length() >= (uint32_t)Count) break; } if (Prev < end) a.New().Set(Prev, end - Prev); a.SetFixedLength(); } else assert(!"String not NULL terminated."); } return a; } /// Splits the string into parts using a separator Array RSplit(const char *Sep, int Count = -1, bool CaseSen = false) { Array a; if (Str && Sep) { const char *s = Get(); size_t SepLen = strlen(Sep); GArray seps; while ((s = CaseSen ? strstr(s, Sep) : Stristr(s, Sep))) { seps.Add(s); s += SepLen; } ssize_t i, Last = seps.Length() - 1; GString p; for (i=Last; i>=0; i--) { const char *part = seps[i] + SepLen; if (i == Last) p.Set(part); else p.Set(part, seps[i+1]-part); a.AddAt(0, p); - if (Count > 0 && a.Length() >= (uint32)Count) + if (Count > 0 && a.Length() >= (uint32_t)Count) break; } const char *End = seps[i > 0 ? i : 0]; p.Set(Get(), End - Get()); a.AddAt(0, p); } a.SetFixedLength(); return a; } /// Splits the string into parts using delimiter chars Array SplitDelimit(const char *Delimiters = NULL, int Count = -1, bool GroupDelimiters = true) const { Array a; if (Str) { const char *delim = Delimiters ? Delimiters : " \t\r\n"; const char *s = Get(); while (*s) { // Skip over non-delimiters const char *e = s; while (*e && !strchr(delim, *e)) e++; if (e > s || !GroupDelimiters) a.New().Set(s, e - s); s = e; if (*s) s++; if (GroupDelimiters) { // Skip any delimiters while (*s && strchr(delim, *s)) s++; } // Create the string - if (Count > 0 && a.Length() >= (uint32)Count) + if (Count > 0 && a.Length() >= (uint32_t)Count) break; } if ( *s || ( !GroupDelimiters && s > Get() && strchr(delim, s[-1]) ) ) a.New().Set(s); } a.SetFixedLength(); return a; } /// Joins an array of strings using a separator GString Join(Array &a) { GString ret; if (a.Length() == 0) return ret; char *Sep = Get(); size_t SepLen = Sep ? strlen(Sep) : 0; size_t Bytes = SepLen * (a.Length() - 1); GArray ALen; for (unsigned i=0; iStr; GArray Matches; while ((Match = (CaseSen ? strstr(Match, Old) : Stristr(Match, Old)))) { Matches.Add(Match); if (Count >= 0 && (int)Matches.Length() >= Count) break; Match += OldLen; } size_t NewSize = Str->Len + (Matches.Length() * (NewLen - OldLen)); - s.Length((uint32)NewSize); + s.Length((uint32_t)NewSize); char *Out = s.Get(); char *In = Str->Str; // For each match... for (unsigned i=0; iStr + Str->Len; if (In < End) { ptrdiff_t Bytes = End - In; memcpy(Out, In, Bytes); Out += Bytes; } assert(Out - s.Get() == NewSize); // Check we got the size right... *Out = 0; // Null terminate } else { s = *this; } return s; } /// Convert string to double double Float() { return Str ? atof(Str->Str) : NAN; } /// Convert to integer int64 Int(int Base = 10) { if (!Str) return -1; if ( Str->Len > 2 && Str->Str[0] == '0' && ( Str->Str[1] == 'x' || Str->Str[1] == 'X' ) ) { return Atoi(Str->Str+2, 16); } return Atoi(Str->Str, Base); } /// Checks if the string is a number bool IsNumeric() { if (!Str) return false; for (char *s = Str->Str; *s; s++) { if (!IsDigit(*s) && !strchr("e-+.", *s)) return false; } return true; } /// Reverses all the characters in the string GString Reverse() { GString s; if (Length() > 0) { s = Str->Str; for (char *a = s, *b = s.Get() + s.Length() - 1; a < b; a++, b--) { char t = *a; *a = *b; *b = t; } } return s; } /// Find a sub-string ssize_t Find(const char *needle, ssize_t start = 0, ssize_t end = -1) { if (!needle) return -1; char *c = Get(); if (!c) return -1; char *pos = c + start; while (c < pos) { if (!*c) return -1; c++; } char *found = (end > 0) ? Strnstr(c, needle, end - start) : strstr(c, needle); return (found) ? found - Get() : -1; } /// Reverse find a string (starting from the end) ssize_t RFind(const char *needle, int start = 0, ssize_t end = -1) { if (!needle) return -1; char *c = Get(); if (!c) return -1; char *pos = c + start; while (c < pos) { if (!*c) return -1; c++; } char *found, *prev = NULL; size_t str_len = strlen(needle); while (( found = ( (end > 0) ? Strnstr(c, needle, end - start) : strstr(c, needle) ) )) { prev = found; c = found + str_len; } return (prev) ? prev - Get() : -1; } /// Returns a copy of the string with all the characters converted to lower case GString Lower() { GString s; if (Str && s.Set(Str->Str, Str->Len)) Strlwr(s.Get()); return s; } /// Returns a copy of the string with all the characters converted to upper case GString Upper() { GString s; if (Str && s.Set(Str->Str, Str->Len)) Strupr(s.Get()); return s; } void Swap(GString &s) { LSwap(Str, s.Str); } /// Gets the character at 'index' int operator() (ssize_t index) const { if (!Str) return 0; char *c = Str->Str; if (index < 0) { size_t idx = Str->Len + index; return c[idx]; } else if (index < (int)Str->Len) { return c[index]; } return 0; } /// Gets the string between at 'start' and 'end' (not including the end'th character) GString operator() (ptrdiff_t start, ptrdiff_t end) { GString s; if (Str) { ptrdiff_t start_idx = start < 0 ? Str->Len + start + 1 : start; - if (start_idx >= 0 && (uint32)start_idx < Str->Len) + if (start_idx >= 0 && (uint32_t)start_idx < Str->Len) { ptrdiff_t end_idx = end < 0 ? Str->Len + end + 1 : end; - if (end_idx >= start_idx && (uint32)end_idx <= Str->Len) + if (end_idx >= start_idx && (uint32_t)end_idx <= Str->Len) s.Set(Str->Str + start_idx, end_idx - start_idx); } } return s; } /// Strip off any leading and trailing whitespace GString Strip(const char *set = NULL) { GString ret; _strip(ret, set, true, true); return ret; } /// Strip off any leading whitespace GString LStrip(const char *set = NULL) { GString ret; _strip(ret, set, true, false); return ret; } /// Strip off any trailing whitespace GString RStrip(const char *set = NULL) { GString ret; _strip(ret, set, false, true); return ret; } /// Prints a formatted string to this object int Printf(const char *Fmt, ...) { Empty(); va_list Arg; va_start(Arg, Fmt); int Bytes = Printf(Arg, Fmt); va_end(Arg); return Bytes; } /// Prints a varargs string int Printf(va_list &Arg, const char *Fmt) { Empty(); return LgiPrintf(*this, Fmt, Arg); } static GString Escape(const char *In, ssize_t Len = -1, const char *Chars = "\r\n\b\\\'\"") { GString s; if (In && Chars) { char Buf[256]; int Ch = 0; if (Len < 0) Len = strlen(In); while (Len-- > 0) { if (Ch > sizeof(Buf)-4) { // Buffer full, add substring to 's' Buf[Ch] = 0; s += Buf; Ch = 0; } if (strchr(Chars, *In)) { Buf[Ch++] = '\\'; switch (*In) { #undef EscChar #define EscChar(from, to) \ case from: Buf[Ch++] = to; break EscChar('\n', 'n'); EscChar('\r', 'r'); EscChar('\\', '\\'); EscChar('\b', 'b'); EscChar('\a', 'a'); EscChar('\t', 't'); EscChar('\v', 'v'); EscChar('\'', '\''); EscChar('\"', '\"'); EscChar('?', '?'); #undef EscChar default: Ch += sprintf_s(Buf+Ch, sizeof(Buf)-Ch, "x%02x", *In); break; } } else Buf[Ch++] = *In; In++; } if (Ch > 0) { Buf[Ch] = 0; s += Buf; } } return s; } template static GString UnEscape(const T *In, ssize_t Len = -1) { GString s; if (In) { T Buf[256]; int Ch = 0; const T *End = Len >= 0 ? In + Len : NULL; while ( (!End || In < End) && *In ) { if (Ch > sizeof(Buf)-4) { // Buffer full, add substring to 's' Buf[Ch] = 0; s += Buf; Ch = 0; } if (*In == '\\') { In++; switch (*In) { case 'n': case 'N': Buf[Ch++] = '\n'; break; case 'r': case 'R': Buf[Ch++] = '\r'; break; case 'b': case 'B': Buf[Ch++] = '\b'; break; case 't': case 'T': Buf[Ch++] = '\t'; break; default: Buf[Ch++] = *In; break; case 0: break; } if (*In) In++; else break; } else Buf[Ch++] = *In++; } if (Ch > 0) { Buf[Ch] = 0; s += Buf; } } return s; } static GString UnEscape(GString s) { return UnEscape(s.Get(), s.Length()); } #if defined(MAC) // && __COREFOUNDATION_CFBASE__ GString(const CFStringRef r) { Str = NULL; *this = r; } GString &operator =(CFStringRef r) { CFIndex length = CFStringGetLength(r); CFRange range = CFRangeMake(0, length); CFIndex usedBufLen = 0; CFIndex slen = CFStringGetBytes(r, range, kCFStringEncodingUTF8, '?', false, NULL, 0, &usedBufLen); if (Set(NULL, usedBufLen)) { slen = CFStringGetBytes( r, range, kCFStringEncodingUTF8, '?', false, (UInt8*)Str->Str, Str->Len, &usedBufLen); Str->Str[usedBufLen] = 0; // NULL terminate } return *this; } CFStringRef CreateStringRef() { char *s = Get(); if (!s) return NULL; return CFStringCreateWithCString(kCFAllocatorDefault, s, kCFStringEncodingUTF8); } #ifdef __OBJC__ NSString *NsStr() { if (Str) return [[NSString alloc] initWithBytes:Str->Str length:Str->Len encoding:NSUTF8StringEncoding]; return nil; } #endif #endif }; #endif diff --git a/include/common/GSubProcess.h b/include/common/GSubProcess.h --- a/include/common/GSubProcess.h +++ b/include/common/GSubProcess.h @@ -1,148 +1,148 @@ /** \file \brief Sub-process wrapper. This class runs one or more sub-processes chained together by pipes. Example: GSubProcess p1("ls", "-l"); GSubProcess p2("grep", "string"); p1.Connect(&p2); p1.Start(true, false); int r; char Buf[256]; while ((r = p1.Read(Buf, sizeof(Buf))) > 0) { // So something with 'Buf' } */ #ifndef _SUB_PROCESS_H_ #define _SUB_PROCESS_H_ #ifdef WIN32 #define USE_SIMPLE_FORK 0 #else #define USE_SIMPLE_FORK 1 #endif #if USE_SIMPLE_FORK #include #endif #if defined(MAC) #include #define GSUBPROCESS_ERROR EBADEXEC #elif defined(LINUX) #include #define GSUBPROCESS_ERROR ECHILD #elif defined(WINDOWS) #define GSUBPROCESS_ERROR ERROR_PROCESS_ABORTED #endif class GSubProcess : public GStreamI { public: #if defined(WIN32) typedef HANDLE PipeHandle; typedef DWORD ProcessId; #else typedef int PipeHandle; typedef pid_t ProcessId; #endif union Pipe { PipeHandle Handles[2]; struct { PipeHandle Read; PipeHandle Write; }; Pipe(); bool Create ( #ifdef WIN32 LPSECURITY_ATTRIBUTES pAttr #else void *UnusedParam #endif ); void Close(); }; protected: GString Exe; GArray Args; GString InitialFolder; bool NewGroup; struct Variable { GString Var, Val; }; bool EnvironmentChanged; GArray Environment; - uint32 ErrorCode; + uint32_t ErrorCode; PipeHandle ExternIn, ExternOut; Variable *GetEnvVar(const char *Var, bool Create = false); ProcessId ChildPid; #if defined(POSIX) Pipe Io; int ExitValue; // was uint32 bool Dupe(PipeHandle Old, PipeHandle New); #elif defined(WIN32) HANDLE ChildHnd; DWORD ExitValue; Pipe ChildOutput, ChildInput; bool Dupe(PipeHandle Old, PipeHandle &New); #endif GSubProcess *Parent, *Child; public: // Object GSubProcess(const char *exe, const char *args = NULL); ~GSubProcess(); // Environment void SetInitFolder(const char *f); const char *GetEnvironment(const char *Var); bool SetEnvironment(const char *Var, const char *Value); // Dom (support StreamReadable/StreamWritable) bool GetValue(const char *Var, GVariant &Value) override; // Handles void SetStdin(PipeHandle Hnd); void SetStdout(PipeHandle Hnd); // Process lifecycle bool GetNewGroup() { return NewGroup; } void SetNewGroup(bool ng) { NewGroup = ng; } ProcessId Handle() { return ChildPid; } bool IsRunning(); - uint32 GetErrorCode(); + uint32_t GetErrorCode(); int32 GetExitValue(); void Connect(GSubProcess *child); bool Start(bool ReadAccess = true, bool WriteAccess = false, bool MapStderrToStdout = true); int Wait(); bool Interrupt(); bool Kill(); // IO int Peek(); GString Read(); ssize_t Read(void *Buf, ssize_t Size, int Flags = 0) override; bool Write(GString s); ssize_t Write(const void *Buf, ssize_t Size, int Flags = 0) override; }; #endif diff --git a/include/common/GTextFile.h b/include/common/GTextFile.h --- a/include/common/GTextFile.h +++ b/include/common/GTextFile.h @@ -1,311 +1,311 @@ #ifndef _TEXT_FILE_H_ #define _TEXT_FILE_H_ class GTextFile : public GFile { public: enum EncodingType { Unknown, Utf8, Utf16BE, Utf16LE, Utf32BE, Utf32LE, }; protected: bool First; size_t Used; bool InEndOfLine; GPointer Pos; EncodingType Type; - GArray Buf; + GArray Buf; GAutoString Charset; public: GTextFile(const char *charset = NULL) { First = true; InEndOfLine = false; Used = 0; Pos.u8 = NULL; Type = Unknown; if (charset) Charset.Reset(NewStr(charset)); } EncodingType GetType() { return Type; } const char *GetTypeString() { switch (Type) { case Utf8: return "utf-8"; case Utf16BE: return "utf-16be"; case Utf16LE: return "utf-16"; case Utf32BE: return "utf-32be"; case Utf32LE: return "utf-32"; default: break; } return NULL; } bool GetVariant(const char *Name, GVariant &Value, char *Array = NULL) { if (LgiStringToDomProp(Name) == FileEncoding) { Value = (int)Type; return true; } return GFile::GetVariant(Name, Value, Array); } /// Read the whole file as utf-8 GAutoString Read() { GAutoString Ret; int64 Sz = GetSize(); if (Sz > 0) { - GAutoPtr Buf(new uint8[Sz]); + GAutoPtr Buf(new uint8_t[Sz]); if (Buf) { ssize_t Rd = Read(Buf, (ssize_t)Sz); if (Rd > 0) { const char *Cs = GetTypeString(); if (Cs) { // printf("Text file read: %s\n", GetName()); Ret.Reset((char*)LgiNewConvertCp("utf-8", Buf, Cs, Rd)); } } } } return Ret; } /// Read the whole file as wchar_t GAutoWString ReadW() { GAutoWString Ret; int Sz = (int)GetSize(); if (Sz > 0) { - GAutoPtr Buf(new uint8[Sz]); + GAutoPtr Buf(new uint8_t[Sz]); if (Buf) { ssize_t Rd = Read(Buf, Sz); if (Rd > 0) { const char *Cs = GetTypeString(); if (Cs) Ret.Reset((char16*)LgiNewConvertCp(LGI_WideCharset, Buf, Cs, Sz)); } } } return Ret; } template - bool CheckForNull(T *ptr, uint8 *end) + bool CheckForNull(T *ptr, uint8_t *end) { - while ((uint8*)ptr < (end-sizeof(T))) + while ((uint8_t*)ptr < (end-sizeof(T))) { if (*ptr == 0) return false; ptr++; } return true; } ssize_t Read(void *Buffer, ssize_t Size, int Flags = 0) { ssize_t Rd = GFile::Read(Buffer, Size, Flags); if (First) { if (Rd < 4) LgiAssert(!"Initial read is too small"); else { First = false; - uint8 *buf = (uint8*)Buffer; - uint8 *start = buf; + uint8_t *buf = (uint8_t*)Buffer; + uint8_t *start = buf; if (Used > 2 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF) { Type = Utf8; start += 3; } else if (Used > 1 && buf[0] == 0xFE && buf[1] == 0xFF) { Type = Utf16BE; start += 2; } else if (Used > 1 && buf[0] == 0xFF && buf[1] == 0xFE) { Type = Utf16LE; start += 2; } else if (Used > 3 && buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0xFE && buf[3] == 0xFF) { Type = Utf32BE; start += 4; } else if (Used > 3 && buf[0] == 0xFF && buf[1] == 0xFE && buf[2] == 0x00 && buf[3] == 0x00) { Type = Utf32LE; start += 4; } else { // Try and detect the char type - uint8 *end = buf + Rd; + uint8_t *end = buf + Rd; if (CheckForNull(buf, end)) Type = Utf8; else if (CheckForNull((uint16*)buf, end)) Type = Utf16LE; else Type = Utf32LE; } if (start > buf) { ssize_t bytes = start - buf; if (bytes <= Rd) { // Remove byte order mark from the buffer memmove(buf, start, Rd - bytes); Rd -= bytes; } } } } return Rd; } bool FillBuffer() { if (!Buf.Length()) { Buf.Length(4 << 10); Pos.u8 = &Buf[0]; Used = 0; } if (Buf.Length()) { // Move any consumed data down to the start of the buffer size_t BytePos = Pos.u8 - &Buf[0]; size_t Remaining = Used - BytePos; if (BytePos > 0 && Remaining > 0) { memmove(&Buf[0], &Buf[BytePos], Remaining); Used = Remaining; } else { Used = 0; } Pos.u8 = &Buf[0]; // Now read some more data into the free space Remaining = Buf.Length() - Used; LgiAssert(Remaining > 0); ssize_t Rd = Read(&Buf[Used], Remaining); if (Rd <= 0) return 0; Used += Rd; } return true; } template int GetLine(GArray &Out) { uint8 *End = NULL; int OutPos = 0; if (Buf.Length()) End = &Buf[0] + Used; while (true) { if (!End || End - Pos.u8 < sizeof(T)) { if (!FillBuffer()) break; End = &Buf[0] + Used; } if (End - Pos.u8 < sizeof(T)) break; T ch = 0; switch (Type) { case Utf16BE: LgiAssert(sizeof(ch) == 2); ch = LgiSwap16(*Pos.u16); Pos.u16++; break; case Utf16LE: LgiAssert(sizeof(ch) == 2); ch = *Pos.u16++; break; case Utf32BE: LgiAssert(sizeof(ch) == 4); ch = LgiSwap32(*Pos.u32); Pos.u32++; break; case Utf32LE: LgiAssert(sizeof(ch) == 4); ch = *Pos.u32++; break; default: // Utf8 { ssize_t len = (int) (End - Pos.u8); ch = LgiUtf8To32(Pos.u8, len); break; } } if (ch == 0) break; if (ch == '\r' || ch == '\n') { if (InEndOfLine) { continue; } else { InEndOfLine = true; break; } } else { InEndOfLine = false; } Out[OutPos++] = ch; } Out[OutPos] = 0; // NULL terminate return OutPos; } }; #endif \ No newline at end of file diff --git a/include/common/GTextLog.h b/include/common/GTextLog.h --- a/include/common/GTextLog.h +++ b/include/common/GTextLog.h @@ -1,126 +1,126 @@ /// \file /// \author Matthew Allen #ifndef _GTEXTLOG_H_ #define _GTEXTLOG_H_ #include "GTextView3.h" #include "INet.h" class GTextLog : public GTextView3, public GStream { protected: bool RemoveReturns; LMutex Sem; GArray Txt; void ProcessTxt() { if (Sem.Lock(_FL)) { - for (uint32 i=0; i= 0 ? chars : StrlenW(w); if (RemoveReturns) { char16 *end = w + Len; for (char16 *s = w; *s; ) { char16 *e = s; while (e < end && *e != '\r') e++; if (e > s) Insert(Size, s, e - s); if (e >= end) break; s = e + 1; } } else { Insert(Size, w, Len); } Invalidate(); SetCaret(Size, false); } int64 SetSize(int64 s) { Name(0); return 0; } ssize_t Write(const void *Buffer, ssize_t Size, int Flags = 0) { GAutoWString w(Utf8ToWide((char*)Buffer, Size)); // printf("GTextLog::Write(%p)\n", w.Get()); if (w) { if (Sem.LockWithTimeout(200, _FL)) { Txt.Add(w.Release()); Sem.Unlock(); } if (Handle()) PostEvent(M_LOG_TEXT); } return Size; } GMessage::Result OnEvent(GMessage *m) { if (MsgCode(m) == M_LOG_TEXT) { ProcessTxt(); } return GTextView3::OnEvent(m); } /* GTextView3 now handles this well enough void OnUrl(char *Url) { GUri u(Url); if (u.Protocol && (!stricmp(u.Protocol, "http") || !stricmp(u.Protocol, "https"))) { LgiExecute(Url); } } */ }; #endif diff --git a/include/common/GTextView3.h b/include/common/GTextView3.h --- a/include/common/GTextView3.h +++ b/include/common/GTextView3.h @@ -1,444 +1,444 @@ /// \file /// \author Matthew Allen /// \brief A unicode text editor #ifndef __GTEXTVIEW3_H #define __GTEXTVIEW3_H #include "GDocView.h" #include "GUndo.h" #include "GDragAndDrop.h" #include "GCss.h" #include "LUnrolledList.h" // use CRLF as opposed to just LF // internally it uses LF only... this is just to remember what to // save out as. #define TEXTED_USES_CR 0x00000001 #define TAB_SIZE 4 #define DEBUG_TIMES_MSG 8000 // a=0 b=(char*)Str enum GTextView3Messages { M_TEXTVIEW_DEBUG_TEXT = M_USER + 0x3421, M_TEXTVIEW_FIND, M_TEXTVIEW_REPLACE, M_TEXT_POUR_CONTINUE, }; extern char Delimiters[]; class GTextView3; enum GTextViewStyleOwners { STYLE_NONE, STYLE_IDE, STYLE_SPELLING, STYLE_FIND_MATCHES, STYLE_ADDRESS, STYLE_URL, }; /// Unicode text editor control. class LgiClass GTextView3 : public GDocView, public ResObject, public GDragDropTarget { friend struct GTextView3Undo; friend bool Text3_FindCallback(GFindReplaceCommon *Dlg, bool Replace, void *User); public: class GStyle { protected: void RefreshLayout(size_t Start, ssize_t Len); public: /// The view the style is for GTextView3 *View; /// When you write several bits of code to do styling assign them /// different owner id's so that they can manage the lifespan of their /// own styles. GTextView3::PourStyle is owner '0', anything else it /// will leave alone. GTextViewStyleOwners Owner; /// The start index into the text buffer of the region to style. ssize_t Start; /// The length of the styled region ssize_t Len; /// The font to draw the styled text in GFont *Font; /// The colour to draw with. If transparent, then the default /// line colour is used. GColour Fore, Back; /// Cursor LgiCursor Cursor; /// Optional extra decor not supported by the fonts GCss::TextDecorType Decor; /// Colour for the optional decor. GColour DecorColour; /// Application base data GVariant Data; GStyle(GTextViewStyleOwners owner = STYLE_NONE) { Owner = owner; View = NULL; Font = NULL; Empty(); Cursor = LCUR_Normal; Decor = GCss::TextDecorNone; } GStyle(const GStyle &s) { Owner = s.Owner; View = s.View; Font = s.Font; Start = s.Start; Len = s.Len; Decor = s.Decor; DecorColour = s.DecorColour; Fore = s.Fore; Back = s.Back; Data = s.Data; } GStyle &Construct(GTextView3 *view, GTextViewStyleOwners owner) { View = view; Owner = owner; Font = NULL; Empty(); Cursor = LCUR_Normal; Decor = GCss::TextDecorNone; return *this; } void Empty() { Start = -1; Len = 0; } bool Valid() { return Start >= 0 && Len > 0; } /* virtual ~GStyle() {} virtual bool OnMouseClick(GMouse *m) { return false; } virtual bool OnMenu(GSubMenu *m) { return false; } virtual void OnMenuClick(int i) {} virtual CURSOR_CHAR GetCursor() { return 0; } */ size_t End() const { return Start + Len; } /// \returns true if style is the same bool operator ==(const GStyle &s) { return Owner == s.Owner && Start == s.Start && Len == s.Len && Fore == s.Fore && Back == s.Back && Decor == s.Decor; } /// Returns true if this style overlaps the position of 's' bool Overlap(GStyle &s) { return Overlap(s.Start, s.Len); } /// Returns true if this style overlaps the position of 's' bool Overlap(ssize_t sStart, ssize_t sLen) { if (sStart + sLen - 1 < Start || sStart >= Start + Len) return false; return true; } void Union(const GStyle &s) { if (Start < 0) { Start = s.Start; Len = s.Len; } else { Start = MIN(Start, s.Start); Len = MAX(End(), s.End()) - Start; } } }; friend class GTextView3::GStyle; protected: // Internal classes enum GTextViewSeek { PrevLine, NextLine, StartLine, EndLine }; class GTextLine : public GRange { public: /* ssize_t Start; // Start offset ssize_t Len; // length of text */ GRect r; // Screen location GColour c; // Colour of line... transparent = default colour GColour Back; // Background colour or transparent GTextLine() { Start = -1; Len = 0; r.ZOff(-1, -1); } virtual ~GTextLine() {} bool Overlap(ssize_t i) { return i>=Start && i<=Start+Len; } size_t CalcLen(char16 *Text) { char16 *c = Text + Start, *e = c; while (*e && *e != '\n') e++; return Len = e - c; } }; class GTextView3Private *d; friend class GTextView3Private; // Options bool Dirty; bool CanScrollX; // Display GFont *Font; GFont *Bold; // Bold variant of 'Font' GFont *Underline; // Underline variant of 'Font' GFont *FixedFont; int LineY; ssize_t SelStart, SelEnd; int DocOffset; int MaxX; bool Blink; uint64 BlinkTs; int ScrollX; GRect CursorPos; /// true if the text pour process is still ongoing bool PourEnabled; // True if pouring the text happens on edit. Turn off if doing lots // of related edits at the same time. And then manually pour once // finished. bool PartialPour; // True if the pour is happening in the background. It's not threaded // but taking place in the GUI thread via timer. bool AdjustStylePos; // Insert/Delete moved styles automatically to match (default: true) List Line; LUnrolledList Style; // sorted in 'Start' order typedef LUnrolledList::Iter StyleIter; // For ::Name(...) char *TextCache; // Data char16 *Text; ssize_t Cursor; ssize_t Size; ssize_t Alloc; // Undo stuff bool UndoOn; GUndo UndoQue; struct GTextView3Undo *UndoCur; // private methods GTextLine *GetTextLine(ssize_t Offset, ssize_t *Index = 0); ssize_t SeekLine(ssize_t Offset, GTextViewSeek Where); int TextWidth(GFont *f, char16 *s, int Len, int x, int Origin); bool ScrollToOffset(size_t Off); int ScrollYLine(); int ScrollYPixel(); GRect DocToScreen(GRect r); ptrdiff_t MatchText(const char16 *Text, bool MatchWord, bool MatchCase, bool SelectionOnly, bool SearchUpwards); // styles bool InsertStyle(GAutoPtr s); GStyle *GetNextStyle(StyleIter &it, ssize_t Where = -1); GStyle *HitStyle(ssize_t i); int GetColumn(); int SpaceDepth(char16 *Start, char16 *End); int AdjustStyles(ssize_t Start, ssize_t Diff, bool ExtendStyle = false); // Overridables virtual void PourText(size_t Start, ssize_t Length); virtual void PourStyle(size_t Start, ssize_t Length); virtual void OnFontChange(); virtual void OnPaintLeftMargin(GSurface *pDC, GRect &r, GColour &colour); virtual char16 *MapText(char16 *Str, ssize_t Len, bool RtlTrailingSpace = false); #ifdef _DEBUG // debug uint64 _PourTime; uint64 _StyleTime; uint64 _PaintTime; #endif void LogLines(); bool ValidateLines(bool CheckBox = false); public: // Construction GTextView3( int Id, int x = 0, int y = 0, int cx = 100, int cy = 100, GFontType *FontInfo = NULL); ~GTextView3(); const char *GetClass() { return "GTextView3"; } // Data char *Name(); bool Name(const char *s); char16 *NameW(); bool NameW(const char16 *s); int64 Value(); void Value(int64 i); const char *GetMimeType() { return "text/plain"; } size_t GetSize() { return Size; } GString operator[](ssize_t LineIdx); ssize_t HitText(int x, int y, bool Nearest); void DeleteSelection(char16 **Cut = 0); // Font GFont *GetFont(); GFont *GetBold(); void SetFont(GFont *f, bool OwnIt = false); void SetFixedWidthFont(bool i); // Options - void SetTabSize(uint8 i); + void SetTabSize(uint8_t i); void SetBorder(int b); void SetReadOnly(bool i); void SetCrLf(bool crlf); /// Sets the wrapping on the control, use #TEXTED_WRAP_NONE or #TEXTED_WRAP_REFLOW void SetWrapType(LDocWrapType i); // State / Selection ssize_t GetCaret(bool Cursor = true); virtual void SetCaret(size_t i, bool Select, bool ForceFullUpdate = false); ssize_t IndexAt(int x, int y); bool IsDirty() { return Dirty; } void IsDirty(bool d) { Dirty = d; } bool HasSelection(); void UnSelectAll(); void SelectWord(size_t From); void SelectAll(); bool GetLineColumnAtIndex(GdcPt2 &Pt, ssize_t Index = -1); size_t GetLines(); void GetTextExtent(int &x, int &y); char *GetSelection(); GRange GetSelectionRange(); // File IO bool Open(const char *Name, const char *Cs = 0); bool Save(const char *Name, const char *Cs = 0); const char *GetLastError(); // Clipboard IO bool Cut(); bool Copy(); bool Paste(); // Undo/Redo void Undo(); void Redo(); bool GetUndoOn() { return UndoOn; } void SetUndoOn(bool b) { UndoOn = b; } // Action UI virtual bool DoGoto(); virtual bool DoCase(bool Upper); virtual bool DoFind(); virtual bool DoFindNext(); virtual bool DoReplace(); // Action Processing bool ClearDirty(bool Ask, char *FileName = 0); void UpdateScrollBars(bool Reset = false); ssize_t GetLine(); void SetLine(int Line); GDocFindReplaceParams *CreateFindReplaceParams(); void SetFindReplaceParams(GDocFindReplaceParams *Params); // Object Events virtual bool OnFind( const char16 *Find, bool MatchWord, bool MatchCase, bool SelectionOnly, bool SearchUpwards); virtual bool OnReplace( const char16 *Find, const char16 *Replace, bool All, bool MatchWord, bool MatchCase, bool SelectionOnly, bool SearchUpwards); bool OnMultiLineTab(bool In); void OnSetHidden(int Hidden); void OnPosChange(); void OnCreate(); void OnEscape(GKey &K); bool OnMouseWheel(double Lines); // Window Events void OnFocus(bool f); void OnMouseClick(GMouse &m); void OnMouseMove(GMouse &m); bool OnKey(GKey &k); void OnPaint(GSurface *pDC); GMessage::Result OnEvent(GMessage *Msg); int OnNotify(GViewI *Ctrl, int Flags); void OnPulse(); int OnHitTest(int x, int y); bool OnLayout(GViewLayoutInfo &Inf); int WillAccept(List &Formats, GdcPt2 Pt, int KeyState); int OnDrop(GArray &Data, GdcPt2 Pt, int KeyState); LgiCursor GetCursor(int x, int y); // Virtuals virtual bool Insert(size_t At, const char16 *Data, ssize_t Len); virtual bool Delete(size_t At, ssize_t Len); virtual void OnEnter(GKey &k); virtual void OnUrl(char *Url); virtual void DoContextMenu(GMouse &m); virtual bool OnStyleClick(GStyle *style, GMouse *m); virtual bool OnStyleMenu(GStyle *style, GSubMenu *m); virtual void OnStyleMenuClick(GStyle *style, int i); }; #endif diff --git a/include/common/GUnicode.h b/include/common/GUnicode.h --- a/include/common/GUnicode.h +++ b/include/common/GUnicode.h @@ -1,870 +1,870 @@ // // GUnicode.h // // Created by Matthew Allen on 1/08/15. // #ifndef _GUnicode_h #define _GUnicode_h #include "LgiInc.h" #ifdef MAC #define REG #else #define REG register #endif -typedef unsigned char uint8; +typedef unsigned char uint8_t; typedef signed char int8; typedef signed short int16; typedef unsigned short uint16; typedef signed int int32; -typedef unsigned int uint32; +typedef unsigned int uint32_t; #ifdef _MSC_VER typedef signed __int64 int64; typedef unsigned __int64 uint64; #ifdef _WIN64 typedef signed __int64 ssize_t; #else typedef signed int ssize_t; #endif #else typedef signed long long int64; typedef unsigned long long uint64; #endif // Defines for decoding UTF8 -#define IsUtf8_1Byte(c) ( ((uint8)(c) & 0x80) == 0x00 ) -#define IsUtf8_2Byte(c) ( ((uint8)(c) & 0xe0) == 0xc0 ) -#define IsUtf8_3Byte(c) ( ((uint8)(c) & 0xf0) == 0xe0 ) -#define IsUtf8_4Byte(c) ( ((uint8)(c) & 0xf8) == 0xf0 ) +#define IsUtf8_1Byte(c) ( ((uint8_t)(c) & 0x80) == 0x00 ) +#define IsUtf8_2Byte(c) ( ((uint8_t)(c) & 0xe0) == 0xc0 ) +#define IsUtf8_3Byte(c) ( ((uint8_t)(c) & 0xf0) == 0xe0 ) +#define IsUtf8_4Byte(c) ( ((uint8_t)(c) & 0xf8) == 0xf0 ) -#define IsUtf8_Lead(c) ( ((uint8)(c) & 0xc0) == 0xc0 ) -#define IsUtf8_Trail(c) ( ((uint8)(c) & 0xc0) == 0x80 ) +#define IsUtf8_Lead(c) ( ((uint8_t)(c) & 0xc0) == 0xc0 ) +#define IsUtf8_Trail(c) ( ((uint8_t)(c) & 0xc0) == 0x80 ) // Stand-alone functions /// Convert a single utf-8 char to utf-32 or returns -1 on error. -inline int32 LgiUtf8To32(uint8 *&i, ssize_t &Len) +inline int32 LgiUtf8To32(uint8_t *&i, ssize_t &Len) { int32 Out = 0; #define InvalidUtf() { Len--; i++; return -1; } if (Len > 0) { if (!*i) { Len = 0; return 0; } if (IsUtf8_1Byte(*i)) { // 1 byte UTF-8 Len--; return *i++; } else if (IsUtf8_2Byte(*i)) { // 2 byte UTF-8 if (Len > 1) { Out = ((int)(*i++ & 0x1f)) << 6; Len--; if (IsUtf8_Trail(*i)) { Out |= *i++ & 0x3f; Len--; } else InvalidUtf() } } else if (IsUtf8_3Byte(*i)) { // 3 byte UTF-8 if (Len > 2) { Out = ((int)(*i++ & 0x0f)) << 12; Len--; if (IsUtf8_Trail(*i)) { Out |= ((int)(*i++ & 0x3f)) << 6; Len--; if (IsUtf8_Trail(*i)) { Out |= *i++ & 0x3f; Len--; } else InvalidUtf() } else InvalidUtf() } } else if (IsUtf8_4Byte(*i)) { // 4 byte UTF-8 if (Len > 3) { Out = ((int)(*i++ & 0x07)) << 18; Len--; if (IsUtf8_Trail(*i)) { Out |= ((int)(*i++ & 0x3f)) << 12; Len--; if (IsUtf8_Trail(*i)) { Out |= ((int)(*i++ & 0x3f)) << 6; Len--; if (IsUtf8_Trail(*i)) { Out |= *i++ & 0x3f; Len--; } else InvalidUtf() } else InvalidUtf() } else InvalidUtf() } } else InvalidUtf() } return Out; } /// Convert a single utf-32 char to utf-8 -inline bool LgiUtf32To8(uint32 c, uint8 *&i, ssize_t &Len) +inline bool LgiUtf32To8(uint32_t c, uint8_t *&i, ssize_t &Len) { if ((c & ~0x7f) == 0) { if (Len > 0) { *i++ = c; Len--; return true; } } else if ((c & ~0x7ff) == 0) { if (Len > 1) { *i++ = 0xc0 | (c >> 6); *i++ = 0x80 | (c & 0x3f); Len -= 2; return true; } } else if ((c & 0xffff0000) == 0) { if (Len > 2) { *i++ = 0xe0 | (c >> 12); *i++ = 0x80 | ((c & 0x0fc0) >> 6); *i++ = 0x80 | (c & 0x3f); Len -= 3; return true; } } else { if (Len > 3) { *i++ = 0xf0 | (c >> 18); *i++ = 0x80 | ((c&0x3f000) >> 12); *i++ = 0x80 | ((c&0xfc0) >> 6); *i++ = 0x80 | (c&0x3f); Len -= 4; return true; } } return false; } // Defined for decoding UTF16 #define IsUtf16_Lead(c) ( ((uint16)(c) & 0xfc00) == 0xD800 ) #define IsUtf16_Trail(c) ( ((uint16)(c) & 0xfc00) == 0xDc00 ) /// Convert a single utf-16 char to utf-32 -inline uint32 LgiUtf16To32(const uint16 *&i, ssize_t &Len) +inline uint32_t LgiUtf16To32(const uint16_t *&i, ssize_t &Len) { if (Len > 1) { if (!*i) { Len = 0; return 0; } int n = *i & 0xfc00; if (n == 0xd800 || n == 0xdc00) { // 2 word UTF if (Len > 3) { Len -= sizeof(uint16)<<1; int w = (*i & 0x3c0) >> 6; int zy = *i++ & 0x3f; return ((w + 1) << 16) | (zy << 10) | (*i++ & 0x3ff); } } // 1 word UTF Len -= sizeof(uint16); return *i++; } return 0; } /// Convert a single utf-32 char to utf-16 -inline bool LgiUtf32To16(uint32 c, uint16 *&i, ssize_t &Len) +inline bool LgiUtf32To16(uint32_t c, uint16_t *&i, ssize_t &Len) { if (c >= 0x10000) { // 2 word UTF if (Len < 4) return false; int w = c - 0x10000; *i++ = 0xd800 + (w >> 10); *i++ = 0xdc00 + (c & 0x3ff); Len -= sizeof(*i) << 1; } else { if (Len < 2) return false; if (c > 0xD7FF && c < 0xE000) return false; // 1 word UTF *i++ = c; Len -= sizeof(*i); return true; } return false; } /// Seeks the pointer 'Ptr' to the next utf-8 character inline bool LgiNextUtf8(char *&p) { char *old = p; if (IsUtf8_Lead(*p)) { p++; while (IsUtf8_Trail(*p)) p++; } else p++; return p > old; } /// Seeks the pointer 'Ptr' to the previous utf-8 character inline void LgiPrevUtf8(char *&p) { p--; while (IsUtf8_Trail(*p)) p--; } /// Pointer to utf-8 string class LgiClass GUtf8Ptr { protected: - uint8 *Ptr; + uint8_t *Ptr; public: GUtf8Ptr(const void *p = 0); /// Assign a new pointer to the string - GUtf8Ptr &operator =(char *s) { Ptr = (uint8*)s; return *this; } + GUtf8Ptr &operator =(char *s) { Ptr = (uint8_t*)s; return *this; } /// Assign a new pointer to the string - GUtf8Ptr &operator =(uint8 *s) { Ptr = s; return *this; } + GUtf8Ptr &operator =(uint8_t *s) { Ptr = s; return *this; } /// \returns the current character in the string or -1 on error. operator int32(); /// Seeks forward GUtf8Ptr &operator ++(); GUtf8Ptr &operator ++(const int i); GUtf8Ptr &operator +=(const ssize_t n); /// Seeks 1 character backward GUtf8Ptr &operator --(); GUtf8Ptr &operator --(const int i); GUtf8Ptr &operator -=(const ssize_t n); // Comparison bool operator <(const GUtf8Ptr &p) { return Ptr < p.Ptr; } bool operator <=(const GUtf8Ptr &p) { return Ptr <= p.Ptr; } bool operator >(const GUtf8Ptr &p) { return Ptr > p.Ptr; } bool operator >=(const GUtf8Ptr &p) { return Ptr >= p.Ptr; } bool operator ==(const GUtf8Ptr &p) { return Ptr == p.Ptr; } bool operator !=(const GUtf8Ptr &p) { return Ptr != p.Ptr; } ptrdiff_t operator -(const GUtf8Ptr &p) { return Ptr - p.Ptr; } /// Gets the bytes between the cur pointer and the end of the buffer or string. int GetBytes(); /// Gets the characters between the cur pointer and the end of the buffer or string. int GetChars(); /// Encodes a utf-8 char at the current location and moves the pointer along void Add(wchar_t c); /// Returns the current pointer. - uint8 *GetPtr() { return Ptr; } + uint8_t *GetPtr() { return Ptr; } }; /// Unicode string class. Allows traversing a utf-8 strings. class LgiClass GUtf8Str : public GUtf8Ptr { // Complete buffer - uint8 *Start; - uint8 *End; + uint8_t *Start; + uint8_t *End; GUtf8Ptr Cur; bool Own; void Empty(); public: /// Constructor GUtf8Str ( /// The string pointer to start with char *utf, /// The number of bytes containing characters, or -1 if NULL terminated. int bytes = -1, /// Copy the string first bool Copy = false ); /// Constructor GUtf8Str ( /// The string pointer to start with. A utf-8 copy of the string will be created. wchar_t *wide, /// The number of wide chars, or -1 if NULL terminated. int chars = -1 ); ~GUtf8Str(); /// Assign a new pointer to the string GUtf8Str &operator =(char *s); /// Allocates a block of memory containing the wide representation of the string. wchar_t *ToWide(); /// \returns true if the class seems to be valid. bool Valid(); /// \returns true if at the start bool IsStart(); /// \returns true if at the end bool IsEnd(); }; // Converts character to lower case template T Tolower(T ch) { if (ch >= 'A' && ch <= 'Z') return ch - 'A' + 'a'; return ch; } template T *Strlwr(T *Str) { if (!Str) return NULL; for (T *s = Str; *s; s++) { if (*s >= 'A' && *s <= 'Z') *s = *s - 'A' + 'a'; } return Str; } // Converts character to upper case template T Toupper(T ch) { if (ch >= 'a' && ch <= 'z') return ch - 'a' + 'A'; return ch; } template T *Strupr(T *Str) { if (!Str) return NULL; for (T *s = Str; *s; s++) { if (*s >= 'a' && *s <= 'z') *s = *s - 'a' + 'A'; } return Str; } // Finds the length of the string in characters template ssize_t Strlen(const T *str) { if (!str) return 0; REG const T *s = str; while (*s) s++; return s - str; } // Templated version of NewStr/NewStrW // Duplicates a string in heap memory. template T *Strdup(const T *s, ssize_t len = -1) { if (!s) return NULL; if (len < 0) len = Strlen(s); T *n = new T[len+1]; if (!n) return NULL; memcpy(n, s, sizeof(T) * len); n[len] = 0; return n; } // Compares two strings, case sensitive template int Strcmp(const T *str_a, const T *str_b) { if (!str_a || !str_b) return -1; REG const T *a = str_a; REG const T *b = str_b; while (true) { if (!*a || !*b || *a != *b) return *a - *b; a++; b++; } return 0; } // Compares the first 'len' chars of two strings, case sensitive template int Strncmp(const T *str_a, const T *str_b, ssize_t len) { if (!str_a || !str_b) return -1; REG const T *a = str_a; REG const T *b = str_b; REG const T *end = a + len; while (a < end) { if (!*a || !*b || *a != *b) return *a - *b; a++; b++; } return 0; } // Compares two strings, case insensitive template int Stricmp(const T *str_a, const T *str_b) { if (!str_a || !str_b) return -1; REG const T *a = str_a; REG const T *b = str_b; REG T ach, bch; while (true) { ach = Tolower(*a); bch = Tolower(*b); if (!ach || !bch || ach != bch) return ach - bch; a++; b++; } return 0; } // Compares the first 'len' chars of two strings, case insensitive template int Strnicmp(const T *str_a, const T *str_b, ssize_t len) { if (!str_a || !str_b || len == 0) return -1; REG const T *a = str_a; REG const T *b = str_b; REG const T *end = a + len; REG T ach, bch; while (a < end) { ach = Tolower(*a); bch = Tolower(*b); if (!ach || !bch || ach != bch) return ach - bch; a++; b++; } return 0; } /// Copies a string template T *Strcpy(T *dst, ssize_t dst_len, const I *src) { if (!dst || !src || dst_len == 0) return NULL; REG T *d = dst; REG T *end = d + dst_len - 1; // leave 1 char for NULL terminator REG const I *s = src; while (d < end && *s) { *d++ = *s++; } *d = 0; // NULL terminate return dst; } /// Finds the first instance of a character in the string template T *Strchr(T *str, int ch) { if (!str) return NULL; for (REG T *s = str; *s; s++) { if (*s == ch) return s; } return NULL; } /// Finds the first instance of a character in the string template T *Strnchr(T *str, int ch, size_t len) { if (!str || len == 0) return NULL; REG T *e = str + len; for (REG T *s = str; s < e; s++) { if (*s == ch) return s; } return NULL; } /// Finds the last instance of a character in the string template T *Strrchr(T *str, int ch) { if (!str) return NULL; T *last = NULL; for (REG T *s = str; *s; s++) { if (*s == ch) last = s; } return last; } /// Appends a string to another template T *Strcat(T *dst, int dst_len, const T *postfix) { if (!dst || !postfix || dst_len < 1) return NULL; // Find the end of the string to append to while (*dst) { dst++; dst_len--; } // Reuse string copy at this point Strcpy(dst, dst_len, postfix); // Return the start of the complete string return dst; } /// Searches the string 'Data' for the 'Value' in a case insensitive manner template T *Stristr(const T *Data, const T *Value) { if (!Data || !Value) return NULL; const T v = Tolower(*Value); while (*Data) { if (Tolower(*Data) == v) { int i; for (i=1; Data[i] && Tolower(Data[i]) == Tolower(Value[i]); i++) ; if (Value[i] == 0) return (T*)Data; } Data++; } return NULL; } /// Searches the string 'Data' for the 'Value' in a case insensitive manner template T *Strstr(const T *Data, const T *Value) { if (!Data || !Value) return NULL; const T v = *Value; while (*Data) { if (*Data == v) { int i; for (i=1; Data[i] && Data[i] == Value[i]; i++) ; if (Value[i] == 0) return (T*)Data; } Data++; } return NULL; } /// Searches the string 'Data' for the 'Value' in a case insensitive manner template T *Strnstr(const T *Data, const T *Value, ssize_t DataLen) { if (!Data || !Value) return NULL; const T v = *Value; ptrdiff_t ValLen = Strlen(Value); if (ValLen > DataLen) return NULL; while (*Data && DataLen >= ValLen) { if (*Data == v) { int i; for (i=1; Data[i] && Data[i] == Value[i]; i++) ; if (Value[i] == 0) return (T*)Data; } Data++; DataLen--; } return NULL; } /// Searches the string 'Data' for the 'Value' in a case insensitive manner template T *Strnistr(const T *Data, const T *Value, ptrdiff_t DataLen) { if (!Data || !Value) return NULL; const T v = Tolower(*Value); ptrdiff_t ValLen = Strlen(Value); if (ValLen > DataLen) return NULL; while (*Data && DataLen >= ValLen) { if (Tolower(*Data) == v) { int i; for (i=1; Data[i] && Tolower(Data[i]) == Tolower(Value[i]); i++) ; if (Value[i] == 0) return (T*)Data; } Data++; DataLen--; } return NULL; } /// Converts a string to int64 (base 10) template int64 Atoi(const T *s, int Base = 10, int64 DefaultValue = -1) { if (!s) return DefaultValue; bool Minus = false; if (*s == '-') { Minus = true; s++; } else if (*s == '+') s++; int64 v = 0; const T *Start = s; if (Base <= 10) { while (*s >= '0' && *s <= '9') { int d = *s - '0'; v *= Base; v += d; s++; } } else { if (*s == '0' && Tolower(s[1]) == 'x') s += 2; int ValidChars = Base > 10 ? Base - 10 : 0; while (*s) { int d; if (*s >= '0' && *s <= '9') d = *s - '0'; else if (*s >= 'a' && *s <= 'a' + ValidChars) d = *s - 'a' + 10; else if (*s >= 'A' && *s <= 'A' + ValidChars) d = *s - 'A' + 10; else break; v *= Base; v += d; s++; } } if (s == Start) return DefaultValue; return Minus ? -v : v; } /// Works out the UTF8 length of a wide char string inline size_t WideToUtf8Len(const wchar_t *s, ssize_t wchars = -1) { if (!s) return 0; size_t Out = 0; - uint8 Buf[6]; + uint8_t Buf[6]; #ifdef _MSC_VER const uint16 *i = (const uint16*) s; ssize_t Len = wchars >= 0 ? wchars << 1 : 0x7fffffff; - for (uint32 ch; ch = LgiUtf16To32(i, Len); ) + for (uint32_t ch; ch = LgiUtf16To32(i, Len); ) { - uint8 *b = Buf; + uint8_t *b = Buf; ssize_t len = sizeof(Buf); if (!LgiUtf32To8(ch, b, len)) break; Out += sizeof(Buf) - len; } #else const wchar_t *end = wchars < 0 ? NULL : s + wchars; for (uint32 ch = 0; ( wchars < 0 || s < end ) && (ch = *s); s++) { uint8 *b = Buf; ssize_t len = sizeof(Buf); if (!LgiUtf32To8(ch, b, len)) break; Out += sizeof(Buf) - len; } #endif return Out; } /// Converts a utf-8 string into a wide character string /// \ingroup Text LgiFunc wchar_t *Utf8ToWide ( /// Input string const char *In, /// [Optional] Size of 'In' in 'chars' or -1 for NULL terminated ssize_t InLen = -1 ); /// Converts a wide character string into a utf-8 string /// \ingroup Text LgiFunc char *WideToUtf8 ( /// Input string const wchar_t *In, /// [Optional] Number of wchar_t's in the input or -1 for NULL terminated ptrdiff_t InLen = -1 ); #endif diff --git a/include/common/GVariant.h b/include/common/GVariant.h --- a/include/common/GVariant.h +++ b/include/common/GVariant.h @@ -1,420 +1,420 @@ /** \file \author Matthew Allen \brief Variant class.\n Copyright (C), Matthew Allen */ #ifndef __GVARIANT_H__ #define __GVARIANT_H__ #include "GDom.h" #undef Bool #include "LDateTime.h" #include "GContainers.h" #include "LHashTable.h" #include "GString.h" class GCompiledCode; #if !defined(_MSC_VER) && (defined(LGI_64BIT) || defined(MAC)) #define GVARIANT_SIZET 1 #define GVARIANT_SSIZET 1 #endif /// The different types the varient can be enum GVariantType { // 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 GVariant GV_LIST, /// Pointer to GDom 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 GVariants GV_HASHTABLE, // Scripting language operator GV_OPERATOR, // Custom scripting lang type GV_CUSTOM, // Wide string GV_WSTRING, // GSurface ptr GV_GSURFACE, /// Pointer to GView GV_GVIEW, /// Pointer to GMouse GV_GMOUSE, /// Pointer to GKey GV_GKEY, /// Pointer to GStream GV_STREAM, /// The maximum value for the variant type. /// (This is used by the scripting engine to refer to a GVariant itself) GV_MAX, }; /// Language operators enum GOperator { 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 GCustomType : public GDom { protected: struct CustomField : public GDom { ssize_t Offset; ssize_t Bytes; ssize_t ArrayLen; GVariantType Type; GString Name; GCustomType *Nested; ssize_t Sizeof(); bool GetVariant(const char *Name, GVariant &Value, char *Array = NULL); }; public: struct Method : public GDom { GString Name; GArray Params; size_t Address; int FrameSize; Method() { Address = -1; FrameSize = -1; } }; protected: // Global vars int Pack; size_t Size; GString Name; // Fields GArray Flds; LHashTbl, int> FldMap; // Methods GArray Methods; LHashTbl, Method*> MethodMap; // Private methods ssize_t PadSize(); public: GCustomType(const char *name, int pack = 1); GCustomType(const char16 *name, int pack = 1); ~GCustomType(); 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, GVariantType Type, int Bytes, int ArrayLen = 1); bool DefineField(const char *Name, GCustomType *Type, int ArrayLen = 1); Method *DefineMethod(const char *Name, GArray &Params, size_t Address); Method *GetMethod(const char *Name); // Field access. You can't use the GDom interface to get/set member variables because // there is no provision for the 'This' pointer. - bool Get(int Index, GVariant &Out, uint8 *This, int ArrayIndex = 0); - bool Set(int Index, GVariant &In, uint8 *This, int ArrayIndex = 0); + bool Get(int Index, GVariant &Out, uint8_t *This, int ArrayIndex = 0); + bool Set(int Index, GVariant &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, GVariant &Value, char *Array = NULL); bool SetVariant(const char *Name, GVariant &Value, char *Array = NULL); bool CallMethod(const char *MethodName, GVariant *ReturnValue, GArray &Args); }; /// A class that can be different types class LgiClass GVariant { public: typedef LHashTbl,GVariant*> LHash; /// The type of the variant GVariantType 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 GDom *Dom; /// Valid when Type is #GV_VOID_PTR, #GV_GVIEW, #GV_GMOUSE or #GV_GKEY 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 { GCustomType *Dom; - uint8 *Data; + 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 GDom *Dom; /// The name of the variable to set/get in the dom object char *Name; } DomRef; /// Valid when Type == #GV_OPERATOR GOperator Op; /// Valid when Type == #GV_GSURFACE struct { class GSurface *Ptr; bool Own; } Surface; /// Valid when Type == #GV_STREAM struct { class GStream *Ptr; bool Own; } Stream; /// Valid when Type == #GV_GVIEW class GView *View; /// Valid when Type == #GV_GMOUSE class GMouse *Mouse; /// Valid when Type == #GV_GKEY class GKey *Key; } Value; /// Constructor to null GVariant(); /// Constructor for integers GVariant(int32 i); - GVariant(uint32 i); + GVariant(uint32_t i); GVariant(int64 i); GVariant(uint64 i); #if GVARIANT_SIZET GVariant(size_t i); #endif #if GVARIANT_SSIZET GVariant(ssize_t i); #endif /// Constructor for double GVariant(double i); /// Constructor for string GVariant(const char *s); /// Constructor for wide string GVariant(const char16 *s); /// Constructor for ptr GVariant(void *p); /// Constructor for DOM ptr GVariant(GDom *p); /// Constructor for DOM variable reference GVariant(GDom *p, char *name); /// Constructor for date GVariant(LDateTime *d); /// Constructor for variant GVariant(GVariant const &v); /// Construtor for operator GVariant(GOperator Op); /// Destructor ~GVariant(); /// Assign bool value GVariant &operator =(bool i); /// Assign an integer value GVariant &operator =(int32 i); - GVariant &operator =(uint32 i); + GVariant &operator =(uint32_t i); GVariant &operator =(int64 i); GVariant &operator =(uint64 i); #if GVARIANT_SIZET GVariant &operator =(size_t i); #endif #if GVARIANT_SSIZET GVariant &operator =(ssize_t i); #endif /// Assign double value GVariant &operator =(double i); /// Assign string value (makes a copy) GVariant &operator =(const char *s); /// Assign a wide string value (makes a copy) GVariant &operator =(const char16 *s); /// Assign another variant value GVariant &operator =(GVariant const &i); /// Assign value to a void ptr GVariant &operator =(void *p); /// Assign value to DOM ptr GVariant &operator =(GDom *p); /// Assign value to be a date/time GVariant &operator =(LDateTime *d); GVariant &operator =(class GView *p); GVariant &operator =(class GMouse *p); GVariant &operator =(class GKey *k); GVariant &operator =(class GStream *s); bool operator ==(GVariant &v); bool operator !=(GVariant &v) { return !(*this == v); } /// Sets the value to a DOM variable reference bool SetDomRef(GDom *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 bool SetList(List *Lst = 0); /// Sets the value to a hashtable bool SetHashTable(LHash *Table = 0, bool Copy = true); /// Set the value to a surface bool SetSurface(class GSurface *Ptr, bool Own); /// Set the value to a stream bool SetStream(class GStream *Ptr, bool Own); /// Returns the string if valid (will convert a GV_WSTRING to utf) char *Str(); /// 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 GVariant to GV_NULL. char *ReleaseStr(); /// Returns the wide string, releasing ownership of the memory to caller and /// changing this GVariant 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. GVariant &Cast(GVariantType NewType); /// Casts the value to int, from whatever source type. The /// GVariant type does not change after calling this. int32 CastInt32(); /// Casts the value to a 64 bit int, from whatever source type. The /// GVariant type does not change after calling this. int64 CastInt64(); /// Casts the value to double, from whatever source type. The /// GVariant type does not change after calling this. double CastDouble(); /// Cast to a string from whatever source type, the GVariant 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 GDom *CastDom(); /// 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(); /// Returns the pointer if available. void *CastVoidPtr(); /// Returns a GView GView *CastView() { return Type == GV_GVIEW ? Value.View : NULL; } /// List insert bool Add(GVariant *v, int Where = -1); /// Converts the varient type to a string static const char *TypeToString(GVariantType t); /// Converts an operator to a string static const char *OperatorToString(GOperator op); /// Converts the varient value to a string GString ToString(); }; #endif diff --git a/include/common/Gdc2.h b/include/common/Gdc2.h --- a/include/common/Gdc2.h +++ b/include/common/Gdc2.h @@ -1,1399 +1,1399 @@ /** \file \author Matthew Allen \date 20/2/1997 \brief GDC v2.xx header */ #ifndef __GDC2_H_ #define __GDC2_H_ #include // sub-system headers #include "LgiOsDefs.h" // Platform specific #include "LgiInc.h" #include "LgiClass.h" #include "Progress.h" // Xp #include "GFile.h" // Platform specific #include "GMem.h" // Platform specific #include "Core.h" // Platform specific #include "GContainers.h" #include "GCapabilities.h" #include "GRefCount.h" #include "GPalette.h" // Alpha Blting #ifdef WIN32 #include "wingdi.h" #endif #ifndef AC_SRC_OVER #define AC_SRC_OVER 0 #endif #ifndef AC_SRC_ALPHA #define AC_SRC_ALPHA 1 #endif #include "GLibrary.h" // Defines /// The default gamma curve (none) used by the gamma LUT GdcDevice::GetGamma #define LGI_DEFAULT_GAMMA 1.0 #ifndef LGI_PI /// The value of PI #define LGI_PI 3.141592654 #endif /// Converts degrees to radians #define LGI_DegToRad(i) ((i)*LGI_PI/180) /// Converts radians to degrees #define LGI_RadToDeg(i) ((i)*180/LGI_PI) #if defined(WIN32) && !defined(_WIN64) /// Use intel assembly instructions, comment out for porting #define GDC_USE_ASM #endif /// Blending mode: overwrite #define GDC_SET 0 /// Blending mode: bitwise AND with background #define GDC_AND 1 /// Blending mode: bitwise OR with background #define GDC_OR 2 /// Blending mode: bitwise XOR with background #define GDC_XOR 3 /// Blending mode: alpha blend with background #define GDC_ALPHA 4 #define GDC_REMAP 5 #define GDC_MAXOP 6 #define GDC_CACHE_SIZE 4 // Channel types #define GDCC_MONO 0 #define GDCC_GREY 1 #define GDCC_INDEX 2 #define GDCC_R 3 #define GDCC_G 4 #define GDCC_B 5 #define GDCC_ALPHA 6 // Native data formats #define GDC_8BIT 0 #define GDC_16BIT 1 #define GDC_24BIT 2 #define GDC_32BIT 3 #define GDC_MAXFMT 4 // Colour spaces #define GDC_8I 0 // 8 bit paletted #define GDC_5R6G5B 1 // 16 bit #define GDC_8B8G8B 2 // 24 bit #define GDC_8A8R8G8B 3 // 32 bit #define GDC_MAXSPACE 4 // Update types #define GDC_PAL_CHANGE 0x1 #define GDC_BITS_CHANGE 0x2 // Flood fill types /// GSurface::FloodFill to a different colour #define GDC_FILL_TO_DIFFERENT 0 /// GSurface::FloodFill to a certain colour #define GDC_FILL_TO_BORDER 1 /// GSurface::FloodFill while colour is near to the seed colour #define GDC_FILL_NEAR 2 // Gdc options /// Used in GdcApp8Set::Blt when doing colour depth reduction to 8 bit. #define GDC_REDUCE_TYPE 0 /// No conversion #define REDUCE_NONE 0 /// Nearest colour pixels #define REDUCE_NEAREST 1 /// Halftone the pixels #define REDUCE_HALFTONE 2 /// Error diffuse the pixels #define REDUCE_ERROR_DIFFUSION 3 /// Not used. #define REDUCE_DL1 4 /// Not used. #define GDC_HALFTONE_BASE_INDEX 1 /// When in 8-bit defined the behaviour of GdcDevice::GetColour #define GDC_PALETTE_TYPE 2 /// Allocate colours from the palette #define PALTYPE_ALLOC 0 /// Use an RGB cube #define PALTYPE_RGB_CUBE 1 /// Use a HSL palette #define PALTYPE_HSL 2 /// Converts images to the specified bit-depth on load, does nothing if 0 #define GDC_PROMOTE_ON_LOAD 3 #define GDC_MAX_OPTION 4 // GSurface Flags #define GDC_ON_SCREEN 0x0002 #define GDC_ALPHA_CHANNEL 0x0004 #define GDC_UPDATED_PALETTE 0x0008 #define GDC_CAPTURE_CURSOR 0x0010 #define GDC_OWN_APPLICATOR 0x0020 #define GDC_CACHED_APPLICATOR 0x0040 #define GDC_OWN_PALETTE 0x0080 #define GDC_DRAW_ON_ALPHA 0x0100 // Region types #define GDC_RGN_NONE 0 // No clipping #define GDC_RGN_SIMPLE 1 // Single rectangle #define GDC_RGN_COMPLEX 2 // Many rectangles // Error codes #define GDCERR_NONE 0 #define GDCERR_ERROR 1 #define GDCERR_CANT_SET_SCAN_WIDTH 2 #define GDC_INVALIDMODE -1 // Font display flags #define GDCFNT_TRANSPARENT 0x0001 // not set - SOLID #define GDCFNT_UNDERLINE 0x0002 // Palette file types #define GDCPAL_JASC 1 #define GDCPAL_MICROSOFT 2 // Misc #define BMPWIDTH(bits) ((((bits)+31)/32)<<2) #include "GColourSpace.h" // Look up tables #define Div255Lut (GdcDevice::GetInst()->GetDiv255()) // Classes class GFilter; class GSurface; #include "GRect.h" // #include "GFont.h" #include "GPoint.h" #include "GColour.h" class LgiClass GBmpMem { public: enum GdcMemFlags { BmpOwnMemory = 0x1, BmpPreMulAlpha = 0x2, }; uchar *Base; int x, y; ssize_t Line; GColourSpace Cs; int Flags; GBmpMem(); ~GBmpMem(); bool PreMul() { return (Flags & BmpPreMulAlpha) != 0; } bool PreMul(bool set) { if (set) Flags |= BmpPreMulAlpha; else Flags &= ~BmpPreMulAlpha; return PreMul(); } bool OwnMem() { return (Flags & BmpOwnMemory) != 0; } bool OwnMem(bool set) { if (set) Flags |= BmpOwnMemory; else Flags &= ~BmpOwnMemory; return OwnMem(); } int GetBits() { return GColourSpaceToBits(Cs); } void GetMemoryExtents(uchar *&Start, uchar *&End) { if (Line < 0) { Start = Base + (y * Line); End = Base + Line; } else { Start = Base; End = Base + (y * Line); } } bool Overlap(GBmpMem *Mem) { uchar *ThisStart, *ThisEnd; GetMemoryExtents(ThisStart, ThisEnd); uchar *MemStart, *MemEnd; GetMemoryExtents(MemStart, MemEnd); if (ThisEnd < MemStart) return false; if (ThisStart > MemEnd) return false; return true; } }; #define GAPP_ALPHA_A 1 #define GAPP_ALPHA_PAL 2 #define GAPP_BACKGROUND 3 #define GAPP_ANGLE 4 #define GAPP_BOUNDS 5 /// \brief Class to draw onto a memory bitmap /// /// This class assumes that all clipping is done by the layer above. /// It can then implement very simple loops to do the work of filling /// pixels class LgiClass GApplicator { protected: GBmpMem *Dest; GBmpMem *Alpha; GPalette *Pal; int Op; public: union { COLOUR c; // main colour System24BitPixel p24; System32BitPixel p32; }; GApplicator() { c = 0; Dest = NULL; Alpha = NULL; Pal = NULL; } GApplicator(COLOUR Colour) { c = Colour; } virtual ~GApplicator() { } virtual const char *GetClass() { return "GApplicator"; } /// Get a parameter virtual int GetVar(int Var) { return 0; } /// Set a parameter virtual int SetVar(int Var, NativeInt Value) { return 0; } GColourSpace GetColourSpace() { return Dest ? Dest->Cs : CsNone; } /// Sets the operator void SetOp(int o, int Param = -1) { Op = o; } /// Gets the operator int GetOp() { return Op; } /// Gets the bit depth int GetBits() { return (Dest) ? GColourSpaceToBits(Dest->Cs) : 0; } /// Gets the flags in operation int GetFlags() { return (Dest) ? Dest->Flags : 0; } /// Gets the palette GPalette *GetPal() { return Pal; } /// Sets the bitmap to write onto virtual bool SetSurface(GBmpMem *d, GPalette *p = 0, GBmpMem *a = 0) = 0; // sets Dest, returns FALSE on error /// Sets the current position to an x,y virtual void SetPtr(int x, int y) = 0; // calculates Ptr from x, y /// Moves the current position one pixel left virtual void IncX() = 0; /// Moves the current position one scanline down virtual void IncY() = 0; /// Offset the current position virtual void IncPtr(int X, int Y) = 0; /// Sets the pixel at the current location with the current colour virtual void Set() = 0; /// Gets the colour of the pixel at the current location virtual COLOUR Get() = 0; /// Draws a vertical line from the current position down 'height' scanlines virtual void VLine(int height) = 0; /// Draws a rectangle starting from the current position, 'x' pixels across and 'y' pixels down virtual void Rectangle(int x, int y) = 0; /// Copies bitmap data to the current position virtual bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha = 0) = 0; }; /// Creates applications from parameters. class LgiClass GApplicatorFactory { public: GApplicatorFactory(); virtual ~GApplicatorFactory(); /// Find the application factory and create the appropriate object. static GApplicator *NewApp(GColourSpace Cs, int Op); virtual GApplicator *Create(GColourSpace Cs, int Op) = 0; }; class LgiClass GApp15 : public GApplicatorFactory { public: GApplicator *Create(GColourSpace Cs, int Op); }; class LgiClass GApp16 : public GApplicatorFactory { public: GApplicator *Create(GColourSpace Cs, int Op); }; class LgiClass GApp24 : public GApplicatorFactory { public: GApplicator *Create(GColourSpace Cs, int Op); }; class LgiClass GApp32 : public GApplicatorFactory { public: GApplicator *Create(GColourSpace Cs, int Op); }; class LgiClass GApp8 : public GApplicatorFactory { public: GApplicator *Create(GColourSpace Cs, int Op); }; class GAlphaFactory : public GApplicatorFactory { public: GApplicator *Create(GColourSpace Cs, int Op); }; #define OrgX(x) x -= OriginX #define OrgY(y) y -= OriginY #define OrgXy(x, y) x -= OriginX; y -= OriginY #define OrgPt(p) p.x -= OriginX; p.y -= OriginY #define OrgRgn(r) r.Offset(-OriginX, -OriginY) /// Base class API for graphics operations class LgiClass GSurface : public GRefCount, public GDom { friend class GFilter; friend class GView; friend class GWindow; friend class GVariant; friend class GRegionClipDC; void Init(); protected: int Flags; int PrevOp; GRect Clip; GColourSpace ColourSpace; GBmpMem *pMem; GSurface *pAlphaDC; GPalette *pPalette; GApplicator *pApp; GApplicator *pAppCache[GDC_CACHE_SIZE]; int OriginX, OriginY; // Protected functions GApplicator *CreateApplicator(int Op, GColourSpace Cs = CsNone); - uint32 LineBits; - uint32 LineMask; - uint32 LineReset; + uint32_t LineBits; + uint32_t LineMask; + uint32_t LineReset; #if WINNATIVE OsPainter hDC; OsBitmap hBmp; #elif defined __GTK_H__ OsPainter Cairo; #endif public: GSurface(); GSurface(GSurface *pDC); virtual ~GSurface(); // Win32 #if defined(__GTK_H__) /// Gets the drawable size, regardless of clipping or client rect virtual GdcPt2 GetSize() { GdcPt2 p; return p; } virtual Gtk::GtkPrintContext *GetPrintContext() { return NULL; } #elif defined(WINNATIVE) virtual HDC StartDC() { return hDC; } virtual void EndDC() {} #elif defined MAC #ifdef COCOA #else virtual CGColorSpaceRef GetColourSpaceRef() { return 0; } #endif #endif virtual OsBitmap GetBitmap(); virtual OsPainter Handle(); virtual void SetClient(GRect *c) {} virtual bool GetClient(GRect *c) { return false; } // Creation enum SurfaceCreateFlags { SurfaceCreateNone, SurfaceRequireNative, SurfaceRequireExactCs, }; virtual bool Create(int x, int y, GColourSpace Cs, int Flags = SurfaceCreateNone) { return false; } virtual void Update(int Flags) {} // Alpha channel /// Returns true if this Surface has an alpha channel virtual bool HasAlpha() { return pAlphaDC != 0; } /// Creates or destroys the alpha channel for this surface virtual bool HasAlpha(bool b); /// Returns true if we are drawing on the alpha channel bool DrawOnAlpha() { return ((Flags & GDC_DRAW_ON_ALPHA) != 0); } /// True if you want to edit the alpha channel rather than the colour bits bool DrawOnAlpha(bool Draw); /// Returns the surface of the alpha channel. GSurface *AlphaDC() { return pAlphaDC; } /// Lowers the alpha of the whole image to Alpha/255.0. /// Only works on bitmaps with an alpha channel (i.e. CsRgba32 or it's variants) - bool SetConstantAlpha(uint8 Alpha); + bool SetConstantAlpha(uint8_t Alpha); // Create sub-images (that reference the memory of this object) GSurface *SubImage(GRect r); GSurface *SubImage(int x1, int y1, int x2, int y2) { GRect r(x1, y1, x2, y2); return SubImage(r); } // Applicator virtual bool Applicator(GApplicator *pApp); virtual GApplicator *Applicator(); // Palette virtual GPalette *Palette(); virtual void Palette(GPalette *pPal, bool bOwnIt = true); // Clip region virtual GRect ClipRgn(GRect *Rgn); virtual GRect ClipRgn(); /// Gets the current colour virtual COLOUR Colour() { return pApp->c; } /// Sets the current colour virtual COLOUR Colour ( /// The new colour COLOUR c, /// The bit depth of the new colour or 0 to indicate the depth is the same as the current Surface int Bits = 0 ); /// Sets the current colour virtual GColour Colour ( /// The new colour GColour c ); /// Gets the current blending mode in operation virtual int Op() { return (pApp) ? pApp->GetOp() : GDC_SET; } /// Sets the current blending mode in operation /// \sa GDC_SET, GDC_AND, GDC_OR, GDC_XOR and GDC_ALPHA virtual int Op(int Op, NativeInt Param = -1); /// Gets the width in pixels virtual int X() { return (pMem) ? pMem->x : 0; } /// Gets the height in pixels virtual int Y() { return (pMem) ? pMem->y : 0; } /// Gets the bounds of the image as a GRect GRect Bounds() { return GRect(0, 0, X()-1, Y()-1); } /// Gets the length of a scanline in bytes virtual ssize_t GetRowStep() { return (pMem) ? pMem->Line : 0; } /// Returns the horizontal resolution of the device virtual int DpiX() { return 100; } /// Returns the vertical resolution of the device virtual int DpiY() { return 100; } /// Gets the bits per pixel virtual int GetBits() { return (pMem) ? GColourSpaceToBits(pMem->Cs) : 0; } /// Gets the colour space of the pixels virtual GColourSpace GetColourSpace() { return ColourSpace; } /// Gets any flags associated with the surface virtual int GetFlags() { return Flags; } /// Returns true if the surface is on the screen virtual class GScreenDC *IsScreen() { return 0; } /// Returns true if the surface is for printing virtual bool IsPrint() { return false; } /// Returns a pointer to the start of a scanline, or NULL if not available virtual uchar *operator[](int y); /// Returns true if this surface supports alpha compositing when using Blt virtual bool SupportsAlphaCompositing() { return false; } /// \returns whether if pixel data is pre-multiplied alpha virtual bool IsPreMultipliedAlpha(); /// Converts the pixel data between pre-mul alpha or non-pre-mul alpha virtual bool ConvertPreMulAlpha(bool ToPreMul); /// Makes the alpha channel opaque virtual bool MakeOpaque(); /// Gets the surface origin virtual void GetOrigin(int &x, int &y) { x = OriginX; y = OriginY; } /// Sets the surface origin virtual void SetOrigin(int x, int y) { OriginX = x; OriginY = y; } /// Sets a pixel with the current colour virtual void Set(int x, int y); /// Gets a pixel (doesn't work on some types of image, i.e. GScreenDC) virtual COLOUR Get(int x, int y); // Line /// Draw a horizontal line in the current colour virtual void HLine(int x1, int x2, int y); /// Draw a vertical line in the current colour virtual void VLine(int x, int y1, int y2); /// Draw a line in the current colour virtual void Line(int x1, int y1, int x2, int y2); /// Some surfaces only support specific line styles (e.g. GDI/Win) enum LineStyles { LineNone = 0x0, LineSolid = 0xffffffff, LineAlternate = 0xaaaaaaaa, LineDash = 0xf0f0f0f0, LineDot = 0xcccccccc, LineDashDot = 0xF33CCF30, LineDashDotDot = 0xf0ccf0cc, }; - virtual uint LineStyle(uint32 Bits, uint32 Reset = 0x80000000) + virtual uint LineStyle(uint32_t Bits, uint32_t Reset = 0x80000000) { - uint32 B = LineBits; + uint32_t B = LineBits; LineBits = Bits; LineMask = LineReset = Reset; return B; } virtual uint LineStyle() { return LineBits; } // Curve /// Stroke a circle in the current colour virtual void Circle(double cx, double cy, double radius); /// Fill a circle in the current colour virtual void FilledCircle(double cx, double cy, double radius); /// Stroke an arc in the current colour virtual void Arc(double cx, double cy, double radius, double start, double end); /// Fill an arc in the current colour virtual void FilledArc(double cx, double cy, double radius, double start, double end); /// Stroke an ellipse in the current colour virtual void Ellipse(double cx, double cy, double x, double y); /// Fill an ellipse in the current colour virtual void FilledEllipse(double cx, double cy, double x, double y); // Rectangular /// Stroke a rectangle in the current colour virtual void Box(int x1, int y1, int x2, int y2); /// Stroke a rectangle in the current colour virtual void Box ( /// The rectangle, or NULL to stroke the edge of the entire surface GRect *a = NULL ); /// Fill a rectangle in the current colour virtual void Rectangle(int x1, int y1, int x2, int y2); /// Fill a rectangle in the current colour virtual void Rectangle ( /// The rectangle, or NULL to fill the entire surface GRect *a = NULL ); /// Copy an image onto the surface virtual void Blt ( /// The destination x coord int x, /// The destination y coord int y, /// The source surface GSurface *Src, /// The optional area of the source to use, if not specified the whole source is used GRect *a = NULL ); void Blt(int x, int y, GSurface *Src, GRect a) { Blt(x, y, Src, &a); } /// Not implemented virtual void StretchBlt(GRect *d, GSurface *Src, GRect *s); // Other /// Fill a polygon in the current colour virtual void Polygon(int Points, GdcPt2 *Data); /// Stroke a bezier in the current colour virtual void Bezier(int Threshold, GdcPt2 *Pt); /// Flood fill in the current colour (doesn't work on a GScreenDC) virtual void FloodFill ( /// Start x coordinate int x, /// Start y coordinate int y, /// Use #GDC_FILL_TO_DIFFERENT, #GDC_FILL_TO_BORDER or #GDC_FILL_NEAR int Mode, /// Fill colour COLOUR Border = 0, /// The bounds of the filled area or NULL if you don't care GRect *Bounds = NULL ); // GDom interface bool GetVariant(const char *Name, GVariant &Value, char *Array = NULL); bool SetVariant(const char *Name, GVariant &Value, char *Array = NULL); bool CallMethod(const char *Name, GVariant *ReturnValue, GArray &Args); }; #ifdef MAC struct GPrintDcParams { #ifdef COCOA #else PMRect Page; CGContextRef Ctx; PMResolution Dpi; #endif }; #endif /// \brief An implemenation of GSurface to draw onto the screen. /// /// This is the class given to GView::OnPaint() most of the time. Which most of /// the time doesn't matter unless your doing something unusual. class LgiClass GScreenDC : public GSurface { class GScreenPrivate *d; public: GScreenDC(); virtual ~GScreenDC(); // OS Sepcific #if WINNATIVE GScreenDC(GViewI *view); GScreenDC(HWND hwnd); GScreenDC(HDC hdc, HWND hwnd, bool Release = false); GScreenDC(HBITMAP hBmp, int Sx, int Sy); bool CreateFromHandle(HDC hdc); void SetSize(int x, int y); #else /// Construct a wrapper to draw on a window GScreenDC(GView *view, void *Param = 0); #if defined(LGI_SDL) #elif defined(MAC) GScreenDC(GWindow *wnd, void *Param = 0); GScreenDC(GPrintDcParams *Params); // Used by GPrintDC GRect GetPos(); void PushState(); void PopState(); #elif defined(__GTK_H__) /// Constructs a server size pixmap GScreenDC(int x, int y, int bits); /// Constructs a wrapper around a drawable GScreenDC(Gtk::GdkDrawable *Drawable); /// Constructs a DC for drawing on a window ///GScreenDC(OsView View); // Gtk::cairo_surface_t *GetSurface(bool Render); GdcPt2 GetSize(); #elif defined(BEOS) GScreenDC(BView *view); #endif OsPainter Handle(); GView *GetView(); int GetFlags(); GRect *GetClient(); #endif // Properties bool GetClient(GRect *c); void SetClient(GRect *c); int X(); int Y(); GPalette *Palette(); void Palette(GPalette *pPal, bool bOwnIt = true); uint LineStyle(); - uint LineStyle(uint Bits, uint32 Reset = 0x80000000); + uint LineStyle(uint Bits, uint32_t Reset = 0x80000000); int GetBits(); GScreenDC *IsScreen() { return this; } bool SupportsAlphaCompositing(); #ifndef LGI_SDL uchar *operator[](int y) { return NULL; } void GetOrigin(int &x, int &y); void SetOrigin(int x, int y); GRect ClipRgn(); GRect ClipRgn(GRect *Rgn); COLOUR Colour(); COLOUR Colour(COLOUR c, int Bits = 0); GColour Colour(GColour c); int Op(); int Op(int Op, NativeInt Param = -1); // Primitives void Set(int x, int y); COLOUR Get(int x, int y); void HLine(int x1, int x2, int y); void VLine(int x, int y1, int y2); void Line(int x1, int y1, int x2, int y2); void Circle(double cx, double cy, double radius); void FilledCircle(double cx, double cy, double radius); void Arc(double cx, double cy, double radius, double start, double end); void FilledArc(double cx, double cy, double radius, double start, double end); void Ellipse(double cx, double cy, double x, double y); void FilledEllipse(double cx, double cy, double x, double y); void Box(int x1, int y1, int x2, int y2); void Box(GRect *a); void Rectangle(int x1, int y1, int x2, int y2); void Rectangle(GRect *a = NULL); void Blt(int x, int y, GSurface *Src, GRect *a = NULL); void StretchBlt(GRect *d, GSurface *Src, GRect *s = NULL); void Polygon(int Points, GdcPt2 *Data); void Bezier(int Threshold, GdcPt2 *Pt); void FloodFill(int x, int y, int Mode, COLOUR Border = 0, GRect *Bounds = NULL); #endif }; /// \brief Blitting region helper class, can calculate the right source and dest rectangles /// for a blt operation including propagating clipping back to the source rect. class GBlitRegions { // Raw image bounds GRect SrcBounds; GRect DstBounds; // Unclipped blit regions GRect SrcBlt; GRect DstBlt; public: /// Clipped blit region in destination co-ords GRect SrcClip; /// Clipped blit region in source co-ords GRect DstClip; /// Calculate the rectangles. GBlitRegions ( /// Destination surface GSurface *Dst, /// Destination blt x offset int x1, /// Destination blt y offset int y1, /// Source surface GSurface *Src, /// [Optional] Crop the source surface first, else whole surface is blt GRect *SrcRc = 0 ) { // Calc full image bounds if (Src) SrcBounds.Set(0, 0, Src->X()-1, Src->Y()-1); else SrcBounds.ZOff(-1, -1); if (Dst) DstBounds.Set(0, 0, Dst->X()-1, Dst->Y()-1); else DstBounds.ZOff(-1, -1); // Calc full sized blt regions if (SrcRc) { SrcBlt = *SrcRc; SrcBlt.Bound(&SrcBounds); } else SrcBlt = SrcBounds; DstBlt = SrcBlt; DstBlt.Offset(x1-DstBlt.x1, y1-DstBlt.y1); // Dest clipped to dest bounds DstClip = DstBlt; DstClip.Bound(&DstBounds); // Now map the dest clipping back to the source SrcClip = SrcBlt; SrcClip.x1 += DstClip.x1 - DstBlt.x1; SrcClip.y1 += DstClip.y1 - DstBlt.y1; SrcClip.x2 -= DstBlt.x2 - DstClip.x2; SrcClip.y2 -= DstBlt.y2 - DstClip.y2; } /// Returns non-zero if both clipped rectangles are valid. bool Valid() { return DstClip.Valid() && SrcClip.Valid(); } void Dump() { printf("SrcBounds: %s\n", SrcBounds.GetStr()); printf("DstBounds: %s\n", DstBounds.GetStr()); printf("SrcBlt: %s\n", SrcBlt.GetStr()); printf("DstBlt: %s\n", DstBlt.GetStr()); printf("SrcClip: %s\n", SrcClip.GetStr()); printf("DstClip: %s\n", DstClip.GetStr()); } }; #if defined(MAC) class CGImg { class CGImgPriv *d; void Create(int x, int y, int Bits, ssize_t Line, uchar *data, uchar *palette, GRect *r); public: CGImg(int x, int y, int Bits, ssize_t Line, uchar *data, uchar *palette, GRect *r); CGImg(GSurface *pDC); ~CGImg(); operator CGImageRef(); }; #endif /// \brief An implemenation of GSurface to draw into a memory bitmap. /// /// This class uses a block of memory to represent an image. You have direct /// pixel access as well as higher level functions to manipulate the bits. class LgiClass GMemDC : public GSurface { protected: class GMemDCPrivate *d; #if defined WINNATIVE PBITMAPINFO GetInfo(); #endif // This is called between capturing the screen and overlaying the // cursor in GMemDC::Blt(x, y, ScreenDC, Src). It can be used to // overlay effects between the screen and cursor layers. virtual void OnCaptureScreen() {} public: /// Creates a memory bitmap GMemDC ( /// The width int x = 0, /// The height int y = 0, /// The colour space to use. CsNone will default to the /// current screen colour space. GColourSpace cs = CsNone, /// Optional creation flags int Flags = SurfaceCreateNone ); GMemDC(GSurface *pDC); virtual ~GMemDC(); #if WINNATIVE HDC StartDC(); void EndDC(); void Update(int Flags); void UpsideDown(bool upsidedown); #else GRect ClipRgn() { return Clip; } #if defined MAC OsBitmap GetBitmap(); #if !defined(LGI_SDL) CGColorSpaceRef GetColourSpaceRef(); CGImg *GetImg(GRect *Sub = 0); #endif #elif defined(__GTK_H__) Gtk::GdkImage *GetImage(); GdcPt2 GetSize(); Gtk::cairo_surface_t *GetSurface(GRect &r); GColourSpace GetCreateCs(); #elif defined(BEOS) || defined(LGI_SDL) OsBitmap GetBitmap(); #endif OsPainter Handle(); #endif // Set new clipping region GRect ClipRgn(GRect *Rgn); void SetClient(GRect *c); /// Locks the bits for access. GMemDC's start in the locked state. bool Lock(); /// Unlocks the bits to optimize for display. While the bitmap is unlocked you /// can't access the data for read or write. On linux this converts the XImage /// to pixmap. On other systems it doesn't do much. As a general rule if you /// don't need access to a bitmap after creating / loading it then unlock it. bool Unlock(); void SetOrigin(int x, int y); void Empty(); bool SupportsAlphaCompositing(); bool Create(int x, int y, GColourSpace Cs, int Flags = SurfaceCreateNone); void Blt(int x, int y, GSurface *Src, GRect *a = NULL); void StretchBlt(GRect *d, GSurface *Src, GRect *s = NULL); void HorzLine(int x1, int x2, int y, COLOUR a, COLOUR b); void VertLine(int x, int y1, int y2, COLOUR a, COLOUR b); }; /// \brief An implemenation of GSurface to print to a printer. /// /// This class redirects standard graphics calls to print a page. /// /// \sa GPrinter class LgiClass GPrintDC #if defined(WIN32) || defined(MAC) : public GScreenDC #else : public GSurface #endif { class GPrintDCPrivate *d; public: GPrintDC(void *Handle, const char *PrintJobName, const char *PrinterName = NULL); ~GPrintDC(); bool IsPrint() { return true; } const char *GetOutputFileName(); int X(); int Y(); int GetBits(); /// Returns the horizontal DPI of the printer or 0 on error int DpiX(); /// Returns the vertical DPI of the printer or 0 on error int DpiY(); #if defined __GTK_H__ Gtk::GtkPrintContext *GetPrintContext(); int Op() { return GDC_SET; } int Op(int Op, NativeInt Param = -1) { return GDC_SET; } GRect ClipRgn(GRect *Rgn); GRect ClipRgn(); COLOUR Colour(); COLOUR Colour(COLOUR c, int Bits = 0); GColour Colour(GColour c); void Set(int x, int y); void HLine(int x1, int x2, int y); void VLine(int x, int y1, int y2); void Line(int x1, int y1, int x2, int y2); void Circle(double cx, double cy, double radius); void FilledCircle(double cx, double cy, double radius); void Arc(double cx, double cy, double radius, double start, double end); void FilledArc(double cx, double cy, double radius, double start, double end); void Ellipse(double cx, double cy, double x, double y); void FilledEllipse(double cx, double cy, double x, double y); void Box(int x1, int y1, int x2, int y2); void Box(GRect *a = NULL); void Rectangle(int x1, int y1, int x2, int y2); void Rectangle(GRect *a = NULL); void Blt(int x, int y, GSurface *Src, GRect *a = NULL); void StretchBlt(GRect *d, GSurface *Src, GRect *s); void Polygon(int Points, GdcPt2 *Data); void Bezier(int Threshold, GdcPt2 *Pt); #endif }; ////////////////////////////////////////////////////////////////////////////// class LgiClass GGlobalColour { class GGlobalColourPrivate *d; public: GGlobalColour(); ~GGlobalColour(); // Add all the colours first COLOUR AddColour(COLOUR c24); bool AddBitmap(GSurface *pDC); bool AddBitmap(GImageList *il); // Then call this bool MakeGlobalPalette(); // Which will give you a palette that // includes everything GPalette *GetPalette(); // Convert a bitmap to the global palette COLOUR GetColour(COLOUR c24); bool RemapBitmap(GSurface *pDC); }; /// This class is useful for double buffering in an OnPaint handler... class GDoubleBuffer { GSurface **In; GSurface *Screen; GMemDC Mem; GRect Rgn; bool Valid; public: GDoubleBuffer(GSurface *&pDC, GRect *Sub = NULL) : In(&pDC) { Rgn = Sub ? *Sub : pDC->Bounds(); Screen = pDC; Valid = pDC && Mem.Create(Rgn.X(), Rgn.Y(), pDC->GetColourSpace()); if (Valid) { *In = &Mem; if (Sub) pDC->SetOrigin(Sub->x1, Sub->y1); } } ~GDoubleBuffer() { if (Valid) { Mem.SetOrigin(0, 0); Screen->Blt(Rgn.x1, Rgn.y1, &Mem); } // Restore state *In = Screen; } }; #ifdef WIN32 typedef int (__stdcall *MsImg32_AlphaBlend)(HDC,int,int,int,int,HDC,int,int,int,int,BLENDFUNCTION); #endif /// Main singleton graphics device class. Holds all global data for graphics rendering. class LgiClass GdcDevice : public GCapabilityClient { friend class GScreenDC; friend class GMemDC; friend class GImageList; static GdcDevice *pInstance; class GdcDevicePrivate *d; #ifdef WIN32 MsImg32_AlphaBlend AlphaBlend; #endif public: GdcDevice(); ~GdcDevice(); static GdcDevice *GetInst() { return pInstance; } /// Returns the colour space of the screen GColourSpace GetColourSpace(); /// Returns the current screen bit depth int GetBits(); /// Returns the current screen width int X(); /// Returns the current screen height int Y(); /// Returns the size of the screen as a rectangle. GRect Bounds() { return GRect(0, 0, X()-1, Y()-1); } GGlobalColour *GetGlobalColour(); /// Set a global graphics option int GetOption(int Opt); /// Get a global graphics option int SetOption(int Opt, int Value); /// 256 lut for squares ulong *GetCharSquares(); /// Divide by 255 lut, 64k entries long. uchar *GetDiv255(); // Palette/Colour void SetGamma(double Gamma); double GetGamma(); // Palette void SetSystemPalette(int Start, int Size, GPalette *Pal); GPalette *GetSystemPalette(); void SetColourPaletteType(int Type); // Type = PALTYPE_xxx define COLOUR GetColour(COLOUR Rgb24, GSurface *pDC = NULL); // File I/O /// \brief Loads a image from a file /// /// This function uses the compiled in codecs, some of which require external /// shared libraries / DLL's to function. Other just need the right source to /// be compiled in. /// /// Lgi comes with the following image codecs: ///
      ///
    • Windows or OS/2 Bitmap: GdcBmp (GFilter.cpp) ///
    • PCX: GdcPcx (Pcx.cpp) ///
    • GIF: GdcGif (Gif.cpp and Lzw.cpp) ///
    • JPEG: GdcJpeg (Jpeg.cpp + libjpeg library) ///
    • PNG: GdcPng (Png.cpp + libpng library) ///
    /// GSurface *Load ( /// The full path of the file const char *FileName, /// [Optional] Enable OS based loaders bool UseOSLoader = true ); /// The stream version of the file loader... GSurface *Load ( /// The full path of the file GStream *In, /// [Optional] File name hint for selecting a filter const char *Name = NULL, /// [Optional] Enable OS based loaders bool UseOSLoader = true ); /// Save an image to a file. bool Save ( /// The file to write to const char *Name, /// The pixels to store GSurface *pDC ); #if LGI_SDL SDL_Surface *Handle(); #endif }; /// \brief Defines a bitmap inline in C++ code. /// /// The easiest way I know of create the raw data for an GInlineBmp /// is to use i.Mage to /// load a file or create a image and then use the Edit->Copy As Code /// menu. Then paste into your C++ and put a uint32 array declaration /// around it. Then point the Data member to the uint32 array. Just be /// sure to get the dimensions right. /// /// I use this for embeding resource images directly into the code so /// that a) they load instantly and b) they can't get lost as a separate file. class LgiClass GInlineBmp { public: /// The width of the image. int X; /// The height of the image. int Y; /// The bitdepth of the image (8, 15, 16, 24, 32). int Bits; /// Pointer to the raw data. - uint32 *Data; + uint32_t *Data; /// Creates a memory DC of the image. - GSurface *Create(uint32 TransparentPx = 0xffffffff); + GSurface *Create(uint32_t TransparentPx = 0xffffffff); }; // file filter support #include "GFilter.h" // globals #define GdcD GdcDevice::GetInst() /// Converts a context to a different bit depth LgiFunc GSurface *ConvertDC ( /// The source image GSurface *pDC, /// The destination bit depth int Bits ); /// Wrapper around GdcDevice::Load /// \deprecated Use GdcDevice::Load directly in new code. LgiFunc GSurface *LoadDC(const char *Name, bool UseOSLoader = true) DEPRECATED_POST; /// Wrapper around GdcDevice::Save /// \deprecated Use GdcDevice::Save directly in new code. LgiFunc bool WriteDC(const char *Name, GSurface *pDC) DEPRECATED_POST; /// Converts a colour to a different bit depth LgiFunc COLOUR CBit(int DstBits, COLOUR c, int SrcBits = 24, GPalette *Pal = 0); #ifdef __cplusplus /// blends 2 colours by the amount specified LgiClass GColour GdcMixColour(GColour a, GColour b, float HowMuchA = 0.5); #endif /// blends 2 24bit colours by the amount specified LgiFunc COLOUR GdcMixColour(COLOUR a, COLOUR b, float HowMuchA = 0.5); /// Turns a colour into an 8 bit grey scale representation LgiFunc COLOUR GdcGreyScale(COLOUR c, int Bits = 24); /// Colour reduction option to define what palette to go to enum GColourReducePalette { CR_PAL_NONE = -1, CR_PAL_CUBE = 0, CR_PAL_OPT, CR_PAL_FILE }; /// Colour reduction option to define how to deal with reduction error enum GColourReduceMatch { CR_MATCH_NONE = -1, CR_MATCH_NEAR = 0, CR_MATCH_HALFTONE, CR_MATCH_ERROR }; /// Colour reduction options class GReduceOptions { public: /// Type of palette GColourReducePalette PalType; /// Reduction error handling GColourReduceMatch MatchType; /// 1-256 int Colours; /// Specific palette to reduce to GPalette *Palette; GReduceOptions() { Palette = 0; Colours = 256; PalType = CR_PAL_NONE; MatchType = CR_MATCH_NONE; } }; /// Reduces a images colour depth LgiFunc bool GReduceBitDepth(GSurface *pDC, int Bits, GPalette *Pal = 0, GReduceOptions *Reduce = 0); struct GColourStop { COLOUR Colour; float Pos; }; /// Draws a horizontal or vertical gradient LgiFunc void LgiFillGradient(GSurface *pDC, GRect &r, bool Vert, GArray &Stops); #ifdef WIN32 /// Draws a windows HICON onto a surface at Dx, Dy LgiFunc void LgiDrawIcon(GSurface *pDC, int Dx, int Dy, HICON ico); #endif /// Row copy operator for full RGB (8 bit components) LgiFunc bool LgiRopRgb ( // Pointer to destination pixel buffer - uint8 *Dst, + uint8_t *Dst, // Destination colour space (must be 8bit components) GColourSpace DstCs, // Pointer to source pixel buffer (if this overlaps 'Dst', set 'Overlap' to true) - uint8 *Src, + uint8_t *Src, // Source colour space (must be 8bit components) GColourSpace SrcCs, // Number of pixels to convert int Px, // Whether to composite using alpha or copy blt bool Composite ); /// Universal bit blt method LgiFunc bool LgiRopUniversal(GBmpMem *Dst, GBmpMem *Src, bool Composite); /// Gets the screens DPI LgiFunc int LgiScreenDpi(); /// Find the bounds of an image. /// \return true if there is some non-transparent image in 'rc' LgiFunc bool LgiFindBounds ( /// [in] The image GSurface *pDC, /// [in/out] Starts off as the initial bounds to search. /// Returns the non-background area. GRect *rc ); #if defined(LGI_SDL) LgiFunc GColourSpace PixelFormat2ColourSpace(SDL_PixelFormat *pf); #elif defined(BEOS) LgiFunc GColourSpace BeosColourSpaceToLgi(color_space cs); #endif #endif diff --git a/include/common/IFtp.h b/include/common/IFtp.h --- a/include/common/IFtp.h +++ b/include/common/IFtp.h @@ -1,269 +1,269 @@ /// \file /// \author Matthew Allen #ifndef __IFTP_H #define __IFTP_H #include "INet.h" #include "LDateTime.h" #include "GStringClass.h" #include "LPermissions.h" // Start ftp list parser stuff struct ftpparse { char *name; /* not necessarily 0-terminated */ int namelen; int flagtrycwd; /* 0 if cwd is definitely pointless, 1 otherwise */ int flagtryretr; /* 0 if retr is definitely pointless, 1 otherwise */ int sizetype; long size; /* number of octets */ int mtimetype; time_t mtime; /* modification time */ int idtype; char *id; /* not necessarily 0-terminated */ int idlen; char perms[12]; } ; #define FTPPARSE_SIZE_UNKNOWN 0 #define FTPPARSE_SIZE_BINARY 1 /* size is the number of octets in TYPE I */ #define FTPPARSE_SIZE_ASCII 2 /* size is the number of octets in TYPE A */ #define FTPPARSE_MTIME_UNKNOWN 0 #define FTPPARSE_MTIME_LOCAL 1 /* time is correct */ #define FTPPARSE_MTIME_REMOTEMINUTE 2 /* time zone and secs are unknown */ #define FTPPARSE_MTIME_REMOTEDAY 3 /* time zone and time of day are unknown */ /* When a time zone is unknown, it is assumed to be GMT. You may want to use localtime() for LOCAL times, along with an indication that the time is correct in the local time zone, and gmtime() for REMOTE* times. */ #define FTPPARSE_ID_UNKNOWN 0 #define FTPPARSE_ID_FULL 1 /* unique identifier for files on this FTP server */ // End ftp list parser stuff #define DefaultFtpCharset "iso-8859-1" #define IFTP_DIR 0x01 #define IFTP_SYM_LINK 0x02 // Options #define OPT_LogOpen "LogOpen" // Messages #define M_CREATE_FOLDER (M_USER+300) #define M_RENAME (M_USER+302) #define M_PERMISSIONS (M_USER+303) #define M_RUN (M_USER+304) #define M_OPEN_AS_TEXT (M_USER+305) #define M_TRANSFER (M_USER+306) #define M_REFRESH (M_USER+307) #define M_UP (M_USER+308) // Classes enum FtpOpenStatus { FO_ConnectFailed, FO_LoginFailed, FO_Error, FO_Connected }; /// Represents a file or folder in the remote systemß class IFtpEntry { public: int Attributes; LPermissions Perms; int64 Size; GString Name; GString Path; GString User; GString Group; LDateTime Date; // App specific fields void *UserData; - uint8 Updated : 1; - uint8 Added : 1; - uint8 Deleted: 1; - uint8 Selected : 1; - uint8 Unchanged : 1; + uint8_t Updated : 1; + uint8_t Added : 1; + uint8_t Deleted: 1; + uint8_t Selected : 1; + uint8_t Unchanged : 1; IFtpEntry(); IFtpEntry(ftpparse *Fp, const char *Cs); IFtpEntry(char *Entry, const char *Cs); IFtpEntry(IFtpEntry *Entry); virtual ~IFtpEntry(); IFtpEntry &operator =(const IFtpEntry &e); bool IsDir() { if (Perms.IsWindows) return Perms.Win.Folder; return (Attributes & IFTP_DIR) != 0; } bool IsHidden() { if (Perms.IsWindows) return Perms.Win.Hidden; if (Name) return Name(0) == '.'; return false; } bool PermissionsFromStr(const char *s); }; /// The remote folder system interfaceß class IFileProtocol { public: virtual ~IFileProtocol() {} // Properties virtual const char *GetCharset() = 0; virtual void SetCharset(const char *cs) = 0; virtual bool IsForceActive() = 0; virtual void IsForceActive(bool i) = 0; virtual bool IsLongList() = 0; virtual void IsLongList(bool i) = 0; virtual bool IsShowHidden() = 0; virtual void IsShowHidden(bool i) = 0; virtual Progress *GetMeter() = 0; virtual void SetMeter(Progress *m) = 0; virtual bool GetAuthed() = 0; // Data virtual GSocketI *Handle() = 0; // Connection virtual FtpOpenStatus Open(GSocketI *S, char *RemoteHost, int Port, char *User, char *Password) = 0; virtual bool Close() = 0; virtual bool IsOpen() = 0; virtual void Noop() = 0; virtual void GetHost(GAutoString *Host, int *Port) = 0; // Directory virtual GString GetDir() = 0; virtual bool SetDir(const char *Dir) = 0; virtual bool CreateDir(const char *Dir) = 0; virtual bool DeleteDir(const char *Dir) = 0; virtual bool ListDir(GArray &Dir) = 0; virtual bool UpDir() = 0; // File virtual bool DeleteFile(const char *Remote) = 0; virtual bool DownloadFile(const char *Local, IFtpEntry *Remote, bool Binary = true) = 0; virtual bool UploadFile(const char *Local, const char *Remote, bool Binary = true) = 0; virtual bool RenameFile(const char *From, const char *To) = 0; virtual bool SetPerms(const char *File, LPermissions Perms) = 0; virtual bool ResumeAt(int64 Pos) = 0; virtual void Abort() = 0; }; /// An implementation of the remote file system interface for FTP connections class IFtp : public IFileProtocol { protected: class IFtpPrivate *d; /// The command connection GAutoPtr Socket; // commands ssize_t WriteLine(char *Msg = 0); ssize_t ReadLine(char *Msg = 0, ssize_t MsgSize = 0); bool TransferFile(const char *Local, const char *Remote, int64 RemoteSize, bool Upload, bool Binary); // Data connections char Ip[64]; int Port; bool PassiveMode; bool ForceActive; bool LongList; bool ShowHidden; Progress *Meter; bool Authenticated; // Socket factory details. // FtpSocketFactory SockFactory; // void *FactoryParam; // State int64 RestorePos; bool AbortTransfer; bool SetupData(bool Binary); bool ConnectData(); char *ToFtpCs(const char *s); char *FromFtpCs(const char *s); public: /// Construct an FTP protocol handler. IFtp(); virtual ~IFtp(); /// \returns the current charset const char *GetCharset(); /// Set the charset used for converting ftp listings to local utf void SetCharset(const char *cs); /// \returns the active connections only setting. bool IsForceActive() { return ForceActive; } /// Set the active connections only option void IsForceActive(bool i) { ForceActive = i; } bool IsLongList() { return LongList; } void IsLongList(bool i) { LongList = i; } bool IsShowHidden() { return ShowHidden; } void IsShowHidden(bool i) { ShowHidden = i; } Progress *GetMeter() { return Meter; } void SetMeter(Progress *m) { Meter = m; } bool GetAuthed() { return Authenticated; } const char *GetError(); /// Returns the socket used for the command connection. GSocketI *Handle() { return Socket; } /// Opens a new command connection to a remote server FtpOpenStatus Open(GSocketI *S, char *RemoteHost, int Port, char *User, char *Password); /// Returns the host and port void GetHost(GAutoString *Host, int *Port); /// Closes the currently active connection bool Close(); /// \returns true if the connection is open and active bool IsOpen(); void Noop(); /// \returns the current remote folder. GString GetDir(); /// Sets the current remote folder. bool SetDir(const char *Dir); /// Create a new sub-folder under the current remote folder. bool CreateDir(const char *Dir); /// Delete a sub-folder under the current folder. bool DeleteDir(const char *Dir); /// List the current remote folder contents. bool ListDir(GArray &Dir); /// Move up to the parent remote folder. bool UpDir(); /// Delete a file in the current remote folder bool DeleteFile(const char *Remote); /// Download a file from the current remote folder bool DownloadFile(const char *Local, IFtpEntry *Remote, bool Binary = true); /// Upload a local file to the current remote folder bool UploadFile(const char *Local, const char *Remote, bool Binary = true); /// Rename a file or folder in the current remote folder bool RenameFile(const char *From, const char *To); /// Set the permissions on a file in the current remote folder bool SetPerms(const char *File, LPermissions Perms); /// Set the resume point before downloading a file bool ResumeAt(int64 Pos); /// Abort the current transfer void Abort() { AbortTransfer = true; } }; #endif diff --git a/include/common/INet.h b/include/common/INet.h --- a/include/common/INet.h +++ b/include/common/INet.h @@ -1,539 +1,539 @@ /** \file \author Matthew Allen \date 28/5/1998 \brief Network sockets classes Copyright (C) 1998, Matthew Allen */ #ifndef __INET_H #define __INET_H #include "LgiNetInc.h" #include "LgiInterfaces.h" #include "GMem.h" #include "GContainers.h" #include "GStream.h" #include "GString.h" #include "LCancel.h" #include "LHashTable.h" #if defined WIN32 #include "ws2ipdef.h" #elif defined POSIX #include #elif defined BEOS #include #include #else typedef int SOCKET; #endif #define DEFAULT_TIMEOUT 30 // Standard ports #define FTP_PORT 21 #define SMTP_PORT 25 #define HTTP_PORT 80 #define HTTPS_PORT 443 #define SMTP_SSL_PORT 465 #define POP3_PORT 110 #define POP3_SSL_PORT 995 #define IMAP_PORT 143 #define IMAP_SSL_PORT 993 // Parameters for passing to GSocket::SetVariant /// Turn on/off logging. Used with GSocket::SetParameter. #define GSocket_Log "Log" /// Set the progress object. Used with GSocket::SetParameter. Value = (GProgress*)Prog #define GSocket_Progress "Progress" /// Set the size of the transfer. Used with GSocket::SetParameter. Value = (int)Size #define GSocket_TransferSize "TransferSize" /// Set the type of protocol. Used with GSocket::SetParameter. Value = (char*)Protocol #define GSocket_Protocol "Protocol" // Functions LgiNetFunc bool HaveNetConnection(); LgiNetFunc bool WhatsMyIp(GAutoString &Ip); -LgiExtern GString LIpStr(uint32 ip); -LgiExtern uint32 LIpHostInt(GString str); +LgiExtern GString LIpStr(uint32_t ip); +LgiExtern uint32_t LIpHostInt(GString str); /// Make md5 hash LgiNetFunc void MDStringToDigest ( /// Buffer to receive md5 hash unsigned char digest[16], /// Input string char *Str, /// Length of input string or -1 for null terminated int Len = -1 ); /// Implementation of a network socket class LgiNetClass GSocket : public GSocketI, public GStream { protected: class GSocketImplPrivate *d; // Methods void Log(const char *Msg, ssize_t Ret, const char *Buf, ssize_t Len); bool CreateUdpSocket(); public: ssize_t BytesRead, BytesWritten; /// Creates the class GSocket(GStreamI *logger = 0, void *unused_param = 0); /// Destroys the class ~GSocket(); /// Gets the active cancellation object LCancel *GetCancel(); /// Sets the active cancellation object void SetCancel(LCancel *c); /// Returns the operating system handle to the socket. OsSocket Handle(OsSocket Set = INVALID_SOCKET); OsSocket ReleaseHandle(); /// Returns true if the internal state of the class is ok bool IsOK(); /// Returns the IP address at this end of the socket. bool GetLocalIp(char *IpAddr); /// Returns the port at this end of the socket. int GetLocalPort(); /// Gets the IP address at the remote end of the socket. - bool GetRemoteIp(uint32 *IpAddr); + bool GetRemoteIp(uint32_t *IpAddr); bool GetRemoteIp(char *IpAddr); /// Gets the IP address at the remote end of the socket. int GetRemotePort(); /// Gets the current timeout for operations in ms int GetTimeout(); /// Sets the current timeout for operations in ms void SetTimeout(int ms); /// Returns whether there is data available for reading. bool IsReadable(int TimeoutMs = 0); /// Returns whether there is data available for reading. bool IsWritable(int TimeoutMs = 0); /// Returns if the socket is ready to accept a connection bool CanAccept(int TimeoutMs = 0); /// Returns if the socket is set to block bool IsBlocking(); /// Set the socket to block void IsBlocking(bool block); /// Get the send delay setting bool IsDelayed(); /// Set the send delay setting void IsDelayed(bool Delay); /// Opens a connection. int Open ( /// The name of the remote host. const char *HostAddr, /// The port on the remote host. int Port ); /// Returns true if the socket is connected. bool IsOpen(); /// Closes the connection to the remote host. int Close(); /// Listens on a given port for an incomming connection. bool Listen(int Port = 0); /// Accepts an incomming connection and connects the socket you pass in to the remote host. bool Accept ( /// The socket to handle the connection. GSocketI *c ); /// \brief Sends data to the remote host. /// \return the number of bytes written or <= 0 on error. ssize_t Write ( /// Pointer to the data to write const void *Data, /// Numbers of bytes to write ssize_t Len, /// Flags to pass to send int Flags = 0 ); /// \brief Reads data from the remote host. /// \return the number of bytes read or <= 0 on error. /// /// Generally the number of bytes returned is less than the buffer size. Depending on how much data /// you are expecting you will need to keep reading until you get and end of field marker or the number /// of bytes your looking for. ssize_t Read ( /// Pointer to the buffer to write output to void *Data, /// The length of the receive buffer. ssize_t Len, /// The flags to pass to recv int Flags = 0 ); /// Returns the last error or 0. int Error(void *Param = 0); const char *GetErrorString(); /// Not supported int64 GetSize() { return -1; } /// Not supported int64 SetSize(int64 Size) { return -1; } /// Not supported int64 GetPos() { return -1; } /// Not supported int64 SetPos(int64 Pos) { return -1; } /// Gets called when the connection is disconnected void OnDisconnect(); /// Gets called when data is received. void OnRead(char *Data, ssize_t Len) {} /// Gets called when data is sent. void OnWrite(const char *Data, ssize_t Len) {} /// Gets called when an error occurs. void OnError(int ErrorCode, const char *ErrorDescription); /// Gets called when some information is available. void OnInformation(const char *Str) {} /// Parameter change handler. int SetParameter ( /// e.g. #GSocket_Log int Param, int Value ) { return false; } /// Get UPD mode bool GetUdp(); /// Set UPD mode void SetUdp(bool b); /// Makes the socket able to broadcast void SetBroadcast(); /// Read UPD packet - int ReadUdp(void *Buffer, int Size, int Flags, uint32 *Ip = 0, uint16 *Port = 0); + int ReadUdp(void *Buffer, int Size, int Flags, uint32_t *Ip = 0, uint16_t *Port = 0); /// Write UPD packet - int WriteUdp(void *Buffer, int Size, int Flags, uint32 Ip, uint16 Port); + int WriteUdp(void *Buffer, int Size, int Flags, uint32_t Ip, uint16_t Port); - bool AddMulticastMember(uint32 MulticastIp, uint32 LocalInterface); - bool SetMulticastInterface(uint32 Interface); + bool AddMulticastMember(uint32_t MulticastIp, uint32_t LocalInterface); + bool SetMulticastInterface(uint32_t Interface); // Impl GStreamI *Clone() { GSocket *s = new GSocket; if (s) s->SetCancel(GetCancel()); return s; } // Statics /// Enumerates the current interfaces struct Interface { GString Name; - uint32 Ip4; // Host order... - uint32 Netmask4; + uint32_t Ip4; // Host order... + uint32_t Netmask4; bool IsLoopBack() { return Ip4 == 0x7f000001; } bool IsPrivate() { - uint8 h1 = (Ip4 >> 24) & 0xff; + uint8_t h1 = (Ip4 >> 24) & 0xff; if (h1 == 192) { - uint8 h2 = ((Ip4 >> 16) & 0xff); + uint8_t h2 = ((Ip4 >> 16) & 0xff); return h2 == 168; } else if (h1 == 10) { return true; } else if (h1 == 172) { - uint8 h2 = ((Ip4 >> 16) & 0xff); + uint8_t h2 = ((Ip4 >> 16) & 0xff); return h2 >= 16 && h2 <= 31; } return false; } bool IsLinkLocal() { - uint8 h1 = (Ip4 >> 24) & 0xff; + uint8_t h1 = (Ip4 >> 24) & 0xff; if (h1 == 169) { - uint8 h2 = ((Ip4 >> 16) & 0xff); + uint8_t h2 = ((Ip4 >> 16) & 0xff); return h2 == 254; } return false; } }; static bool EnumInterfaces(GArray &Out); }; class LgiNetClass GSocks5Socket : public GSocket { GAutoString Proxy; int Port; GAutoString UserName; GAutoString Password; protected: bool Socks5Connected; public: GSocks5Socket(); GSocks5Socket &operator=(const GSocks5Socket &s) { Proxy.Reset(NewStr(s.Proxy)); UserName.Reset(NewStr(s.UserName)); Password.Reset(NewStr(s.Password)); Port = s.Port; return *this; } // Connection void SetProxy(char *proxy, int port, char *username, char *password); void SetProxy(const GSocks5Socket *s); int Open(const char *HostAddr, int port); // Server bool Listen(int Port) { return false; } }; /// Uri parser class LgiNetClass GUri { public: char *Protocol; char *User; char *Pass; char *Host; int Port; char *Path; char *Anchor; /// Parser for URI's. GUri ( /// Optional URI to start parsing const char *uri = 0 ); ~GUri(); /// Parse a URI into it's sub fields... bool Set(const char *uri); /// Re-constructs the URI GAutoString GetUri(); /// Empty this object... void Empty(); /// URL encode GAutoString Encode ( /// The string to encode const char *s, /// [Optional] Any extra characters you want encoded const char *ExtraCharsToEncode = 0 ); /// URL decode GAutoString Decode(char *s); /// Separate args into map typedef LHashTbl,GString> StrMap; StrMap Params(); GUri &operator =(const GUri &u); GUri &operator =(char *s) { Set(s); return *this; } }; /// Proxy settings lookup class LgiNetClass GProxyUri : public GUri { public: GProxyUri(); }; #define MAX_UDP_SIZE 512 class LUdpListener : public GSocket { GStream *Log; GString Context; public: - LUdpListener(GArray interface_ips, uint32 mc_ip, uint16 port, GStream *log = NULL) : Log(log) + LUdpListener(GArray interface_ips, uint32_t mc_ip, uint16_t port, GStream *log = NULL) : Log(log) { //SetBroadcast(); SetUdp(true); struct sockaddr_in addr; ZeroObj(addr); addr.sin_family = AF_INET; addr.sin_port = htons(port); #ifdef WINDOWS addr.sin_addr.S_un.S_addr = INADDR_ANY; #elif defined(MAC) addr.sin_addr.s_addr = htonl(mc_ip); #else addr.sin_addr.s_addr = INADDR_ANY; #endif if (mc_ip) { for (auto ip : interface_ips) { printf("AddMulticastMember(%s, %s)\n", LIpStr(mc_ip).Get(), LIpStr(ip).Get()); AddMulticastMember(mc_ip, ip); } } int r = bind(Handle(), (struct sockaddr*)&addr, sizeof(addr)); if (r) { #ifdef WIN32 int err = WSAGetLastError(); OnError(err, NULL); #endif printf("Error: Bind on %s:%i\n", LIpStr(ntohl(addr.sin_addr.s_addr)).Get(), port); } else { printf("Ok: Bind on %s:%i\n", LIpStr(ntohl(addr.sin_addr.s_addr)).Get(), port); } } - bool ReadPacket(GString &d, uint32 &Ip, uint16 &Port) + bool ReadPacket(GString &d, uint32_t &Ip, uint16_t &Port) { if (!IsReadable(10)) return false; char Data[MAX_UDP_SIZE]; int Rd = ReadUdp(Data, sizeof(Data), 0, &Ip, &Port); if (Rd <= 0) return false; d.Set(Data, Rd); return true; } void OnError(int ErrorCode, const char *ErrorDescription) { if (Log) Log->Print("Error: %s - %i, %s\n", Context.Get(), ErrorCode, ErrorDescription); } }; class LUdpBroadcast : public GSocket { GArray Intf; - uint32 SelectIf; + uint32_t SelectIf; public: - LUdpBroadcast(uint32 selectIf) : SelectIf(selectIf) + LUdpBroadcast(uint32_t selectIf) : SelectIf(selectIf) { SetBroadcast(); SetUdp(true); EnumInterfaces(Intf); } - bool BroadcastPacket(GString Data, uint32 Ip, uint16 Port) + bool BroadcastPacket(GString Data, uint32_t Ip, uint16_t Port) { return BroadcastPacket(Data.Get(), Data.Length(), Ip, Port); } - bool BroadcastPacket(void *Ptr, size_t Size, uint32 Ip, uint16 Port) + bool BroadcastPacket(void *Ptr, size_t Size, uint32_t Ip, uint16_t Port) { if (Size > MAX_UDP_SIZE) return false; if (SelectIf) { struct in_addr addr; addr.s_addr = htonl(SelectIf); auto r = setsockopt(Handle(), IPPROTO_IP, IP_MULTICAST_IF, (char*)&addr, sizeof(addr)); if (r) printf("%s:%i - set IP_MULTICAST_IF failed.\n", _FL); SelectIf = 0; } /* uint32 Netmask = 0xffffff00; Interface *Cur = NULL; for (auto &i : Intf) { if (i.Ip4 == Ip) { Netmask = i.Netmask4; Cur = &i; break; } } */ - uint32 BroadcastIp = Ip; + uint32_t BroadcastIp = Ip; #if 0 printf("Broadcast %i.%i.%i.%i\n", (BroadcastIp >> 24) & 0xff, (BroadcastIp >> 16) & 0xff, (BroadcastIp >> 8) & 0xff, (BroadcastIp) & 0xff); #endif int wr = WriteUdp(Ptr, (int)Size, 0, BroadcastIp, Port); return wr == Size; } }; #endif diff --git a/include/common/LHashTable.h b/include/common/LHashTable.h --- a/include/common/LHashTable.h +++ b/include/common/LHashTable.h @@ -1,784 +1,784 @@ /* More modern take on the GHashTbl I had been using for a while. Moved the key management into a parameter class. All the key pooling is also now managed by the param class rather than the hash table itself. */ #ifndef _LHashTbl_H_ #define _LHashTbl_H_ #include #include "GMem.h" #include "GArray.h" #include "GString.h" #include "LgiClass.h" #ifndef LHASHTBL_MAX_SIZE #define LHASHTBL_MAX_SIZE (64 << 10) #endif #define HASH_TABLE_SHRINK_THRESHOLD 15 #define HASH_TABLE_GROW_THRESHOLD 50 template class IntKey { public: typedef T Type; T NullKey; IntKey() { NullKey = DefaultNull; } void EmptyKeys() {} - uint32 Hash(T k) { return (uint32)k; } + uint32_t Hash(T k) { return (uint32_t)k; } T CopyKey(T a) { return a; } size_t SizeKey(T a) { return sizeof(a); } void FreeKey(T &a) { a = NullKey; } bool CmpKey(T a, T b) { return a == b; } size_t TotalSize() { return 0; } }; template class PtrKey { public: typedef T Type; T NullKey; PtrKey() { NullKey = DefaultNull; } void EmptyKeys() {} - uint32 Hash(T k) { return (uint32)(((size_t)k)/31); } + uint32_t Hash(T k) { return (uint32_t)(((size_t)k)/31); } T CopyKey(T a) { return a; } size_t SizeKey(T a) { return sizeof(a); } void FreeKey(T &a) { a = NullKey; } bool CmpKey(T a, T b) { return a == b; } size_t TotalSize() { return 0; } }; template class StrKey { public: typedef T *Type; T *NullKey; StrKey() { NullKey = DefaultNull; } void EmptyKeys() {} - uint32 Hash(T *k) { return LHash(k, Strlen(k), CaseSen); } + uint32_t Hash(T *k) { return LHash(k, Strlen(k), CaseSen); } T *CopyKey(T *a) { return Strdup(a); } size_t SizeKey(T *a) { return (Strlen(a)+1)*sizeof(*a); } void FreeKey(T *&a) { if (a) delete [] a; a = NullKey; } bool CmpKey(T *a, T *b) { return !(CaseSen ? Strcmp(a, b) : Stricmp(a, b)); } size_t TotalSize() { return 0; } }; template class KeyPool { protected: struct Buf : public GArray { size_t Used; Buf(size_t Sz = 0) { this->Length(Sz); } size_t Free() { return this->Length() - Used; } }; GArray Mem; Buf *GetMem(size_t Sz) { if (!Mem.Length() || Mem.Last().Free() < Sz) Mem.New().Length(PoolSize); return Mem.Last().Free() >= Sz ? &Mem.Last() : NULL; } public: const int DefaultPoolSize = (64 << 10) / sizeof(T); int PoolSize; KeyPool() { PoolSize = BlockSize ? BlockSize : DefaultPoolSize; } void EmptyKeys() { Mem.Length(0); } size_t TotalSize() { size_t s = 0; for (auto &b : Mem) s += sizeof(Buf) + (b.Length() * sizeof(T)); return s; } }; template class ConstStrKey { public: typedef const T *Type; const T *NullKey; ConstStrKey() { NullKey = DefaultNull; } void EmptyKeys() {} - uint32 Hash(const T *k) { return LHash(k, Strlen(k), CaseSen); } + uint32_t Hash(const T *k) { return LHash(k, Strlen(k), CaseSen); } T *CopyKey(const T *a) { return Strdup(a); } size_t SizeKey(const T *a) { return (Strlen(a)+1)*sizeof(*a); } void FreeKey(const T *&a) { if (a) delete [] a; a = NullKey; } bool CmpKey(const T *a, const T *b) { return !(CaseSen ? Strcmp(a, b) : Stricmp(a, b)); } size_t TotalSize() { return 0; } }; template class StrKeyPool : public KeyPool { public: typedef T *Type; using Buf = typename KeyPool::Buf; T *NullKey; StrKeyPool() { NullKey = DefaultNull; } - uint32 Hash(T *k) { return LHash(k, Strlen(k), CaseSen); } + uint32_t Hash(T *k) { return LHash(k, Strlen(k), CaseSen); } size_t SizeKey(T *a) { return (Strlen(a)+1)*sizeof(*a); } bool CmpKey(T *a, T *b) { return !(CaseSen ? Strcmp(a, b) : Stricmp(a, b)); } T *CopyKey(T *a) { size_t Sz = Strlen(a) + 1; Buf *m = this->GetMem(Sz); if (!m) return NullKey; T *r = m->AddressOf(m->Used); memcpy(r, a, Sz*sizeof(*a)); m->Used += Sz; return r; } void FreeKey(T *&a) { // Do nothing... memory is own by KeyPool a = NullKey; } }; template class ConstStrKeyPool : public KeyPool { public: typedef const T *Type; using Buf = typename KeyPool::Buf; const T *NullKey; ConstStrKeyPool() { NullKey = DefaultNull; } - uint32 Hash(const T *k) { return LHash(k, Strlen(k), CaseSen); } + uint32_t Hash(const T *k) { return LHash(k, Strlen(k), CaseSen); } size_t SizeKey(const T *a) { return (Strlen(a)+1)*sizeof(*a); } bool CmpKey(const T *a, const T *b) { return !(CaseSen ? Strcmp(a, b) : Stricmp(a, b)); } const T *CopyKey(const T *a) { size_t Sz = Strlen(a) + 1; Buf *m = this->GetMem(Sz); if (!m) return NullKey; T *r = m->AddressOf(m->Used); memcpy(r, a, Sz*sizeof(*a)); m->Used += Sz; return r; } void FreeKey(const T *&a) { // Do nothing... memory is own by KeyPool a = NullKey; } }; /// General hash table container for O(1) access to table data. template class LHashTbl : public KeyTrait { public: typedef typename KeyTrait::Type Key; typedef LHashTbl HashTable; const int DefaultSize = 256; struct Pair { Key key; Value value; }; protected: Value NullValue; size_t Used; size_t Size; size_t MaxSize; int Version; // This changes every time 'Table' is resized. // It's used to invalidate iterators. Pair *Table; int Percent() { return (int) (Used * 100 / Size); } bool GetEntry(const Key k, ssize_t &Index, bool Debug = false) { if (k != this->NullKey && Table) { - uint32 h = this->Hash(k); + uint32_t h = this->Hash(k); for (size_t i=0; iNullKey) return false; if (this->CmpKey(Table[Index].key, k)) return true; } } return false; } bool Between(ssize_t Val, ssize_t Min, ssize_t Max) { if (Min <= Max) { // Not wrapped return Val >= Min && Val <= Max; } else { // Wrapped return Val <= Max || Val >= Min; } } void InitializeTable(Pair *e, ssize_t len) { if (!e || len < 1) return; while (len--) { e->key = this->NullKey; e->value = NullValue; e++; } } public: /// Constructs the hash table LHashTbl ( /// Sets the initial table size. Should be 2x your data set. size_t size = 0, /// The default empty value Value nullvalue = (Value)0 ) { Size = size; NullValue = nullvalue; Used = 0; Version = 0; MaxSize = LHASHTBL_MAX_SIZE; // LgiAssert(Size <= MaxSize); if ((Table = new Pair[Size])) { InitializeTable(Table, Size); } } LHashTbl(const HashTable &init) { Size = init.Size; NullValue = init.NullValue; Used = 0; Version = 0; MaxSize = LHASHTBL_MAX_SIZE; if ((Table = new Pair[Size])) { for (size_t i=0; iNullKey; } *this = init; } } /// Deletes the hash table removing all contents from memory virtual ~LHashTbl() { Empty(); DeleteArray(Table); } Key GetNullKey() { return this->NullKey; } /// Copy operator HashTable &operator =(const HashTable &c) { if (IsOk() && c.IsOk()) { Empty(); this->NullKey = c.NullKey; NullValue = c.NullValue; size_t Added = 0; for (size_t i=0; iNullKey) { if (!Add(OldTable[i].key, OldTable[i].value)) { LgiAssert(0); } this->FreeKey(OldTable[i].key); } } Version++; Status = true; } else { LgiAssert(Table != 0); Table = OldTable; Size = OldSize; return false; } DeleteArray(OldTable); } return Status; } /// Returns true if the object appears to be valid bool IsOk() const { bool Status = #ifndef __llvm__ this != 0 && #endif Table != 0; if (!Status) { #ifndef LGI_STATIC LgiTrace("%s:%i - this=%p Table=%p Used=%i Size=%i\n", _FL, this, Table, Used, Size); #endif LgiAssert(0); } return Status; } /// Gets the number of entries used size_t Length() { return IsOk() ? Used : 0; } /// Adds a value under a given key bool Add ( /// The key to insert the value under Key k, /// The value to insert Value v ) { if (!Size) SetSize(DefaultSize); if (IsOk() && k == this->NullKey && v == NullValue) { LgiAssert(!"Adding NULL key or value."); return false; } - uint32 h = this->Hash(k); + uint32_t h = this->Hash(k); ssize_t Index = -1; for (size_t i=0; iNullKey || this->CmpKey(Table[idx].key, k) ) { Index = idx; break; } } if (Index >= 0) { if (Table[Index].key == this->NullKey) { Table[Index].key = this->CopyKey(k); Used++; } Table[Index].value = v; if (Percent() > HASH_TABLE_GROW_THRESHOLD) { SetSize(Size << 1); } return true; } LgiAssert(!"Couldn't alloc space."); return false; } /// Deletes a value at 'key' bool Delete ( /// The key of the value to delete Key k, /// Turns off resizing, in case your iterating over the hash table, /// where resizing would invalidate the iterators. bool NoResize = false ) { ssize_t Index = -1; if (GetEntry(k, Index)) { // Delete the entry this->FreeKey(Table[Index].key); Table[Index].value = NullValue; Used--; // Bubble down any entries above the hole ssize_t Hole = Index; for (ssize_t i = (Index + 1) % Size; i != Index; i = (i + 1) % Size) { if (Table[i].key != this->NullKey) { - uint32 Hsh = this->Hash(Table[i].key); - uint32 HashIndex = Hsh % Size; + uint32_t Hsh = this->Hash(Table[i].key); + uint32_t HashIndex = Hsh % Size; if (HashIndex != i && Between(Hole, HashIndex, i)) { // Do bubble if (Table[Hole].key != this->NullKey) { LgiAssert(0); } memmove(Table + Hole, Table + i, sizeof(Table[i])); InitializeTable(Table + i, 1); Hole = i; } } else { // Reached the end of entries that could have bubbled break; } } // Check for auto-shrink limit if (!NoResize && Percent() < HASH_TABLE_SHRINK_THRESHOLD) { SetSize(Size >> 1); } return true; } else { GetEntry(k, Index, true); } return false; } /// Returns the value at 'key' Value Find(const Key k) { ssize_t Index = -1; if (IsOk() && GetEntry(k, Index)) { return Table[Index].value; } return NullValue; } /// Returns the Key at 'val' Key FindKey(const Value val) { if (IsOk()) { Pair *c = Table; Pair *e = Table + Size; while (c < e) { if (CmpKey(c->value, val)) { return c->key; } c++; } } return this->NullKey; } /// Removes all key/value pairs from memory void Empty() { if (!IsOk()) return; for (size_t i=0; iNullKey) { this->FreeKey(Table[i].key); LgiAssert(Table[i].key == this->NullKey); } Table[i].value = NullValue; } Used = 0; this->EmptyKeys(); } /// Returns the amount of memory in use by the hash table. int64 Sizeof() { int64 Sz = sizeof(*this); Sz += Sz * sizeof(Pair); int64 KeySize = 0; size_t Total = KeyTrait::TotalSize(); if (Total) { KeySize += Total; } else { int Keys = 0; for (size_t i=0; iNullKey) { Keys++; KeySize += this->SizeKey(Table[i].key); } } } return Sz + KeySize; } /// Deletes values as objects void DeleteObjects() { for (size_t i=0; iNullKey) this->FreeKey(Table[i].key); if (Table[i].value != NullValue) DeleteObj(Table[i].value); } Used = 0; } /// Deletes values as arrays void DeleteArrays() { for (size_t i=0; iNullKey) this->FreeKey(Table[i].key); if (Table[i].value != NullValue) DeleteArray(Table[i].value); } Used = 0; } /// Swaps the objects void Swap(LHashTbl &h) { LSwap(this->NullKey, h.NullKey); LSwap(NullValue, h.NullValue); LSwap(Used, h.Used); LSwap(Size, h.Size); LSwap(MaxSize, h.MaxSize); LSwap(Version, h.Version); LSwap(Table, h.Table); } struct PairIterator { LHashTbl *t; ssize_t Idx; int Version; public: PairIterator(LHashTbl *tbl, ssize_t i) { t = tbl; Version = t->Version; Idx = i; if (Idx < 0) Next(); } bool operator !=(const PairIterator &it) const { bool Eq = t == it.t && Idx == it.Idx; return !Eq; } PairIterator &Next() { if (t->IsOk()) { if (Version != t->Version) { #ifndef LGI_UNIT_TESTS LgiAssert(!"Iterator invalidated"); #endif *this = t->end(); } else { while (++Idx < (ssize_t)t->Size) { if (t->Table[Idx].key != t->NullKey) break; } } } return *this; } PairIterator &operator ++() { return Next(); } PairIterator &operator ++(int) { return Next(); } Pair &operator *() { LgiAssert( Idx >= 0 && Idx < (ssize_t)t->Size && t->Table[Idx].key != t->NullKey); return t->Table[Idx]; } }; PairIterator begin() { return PairIterator(this, -1); } PairIterator end() { return PairIterator(this, Size); } }; #endif diff --git a/include/common/LPermissions.h b/include/common/LPermissions.h --- a/include/common/LPermissions.h +++ b/include/common/LPermissions.h @@ -1,126 +1,126 @@ #ifndef _LPERM_H_ #define _LPERM_H_ #include "LHashTable.h" struct LPermissions { struct _Win { - uint32 ReadOnly : 1; // FILE_ATTRIBUTE_READONLY - uint32 Hidden : 1; - uint32 System : 1; - uint32 Reserved1 : 1; + uint32_t ReadOnly : 1; // FILE_ATTRIBUTE_READONLY + uint32_t Hidden : 1; + uint32_t System : 1; + uint32_t Reserved1 : 1; - uint32 Folder : 1; // FILE_ATTRIBUTE_DIRECTORY - uint32 Archive : 1; - uint32 Device : 1; - uint32 Normal : 1; + uint32_t Folder : 1; // FILE_ATTRIBUTE_DIRECTORY + uint32_t Archive : 1; + uint32_t Device : 1; + uint32_t Normal : 1; - uint32 Temporary : 1; // FILE_ATTRIBUTE_TEMPORARY - uint32 Sparse : 1; - uint32 ReparsePoint : 1; - uint32 Compressed : 1; + uint32_t Temporary : 1; // FILE_ATTRIBUTE_TEMPORARY + uint32_t Sparse : 1; + uint32_t ReparsePoint : 1; + uint32_t Compressed : 1; - uint32 Offline : 1; // FILE_ATTRIBUTE_OFFLINE - uint32 ContentNotIndexed : 1; - uint32 Encrypted : 1; - uint32 IntegrityStream : 1; + uint32_t Offline : 1; // FILE_ATTRIBUTE_OFFLINE + uint32_t ContentNotIndexed : 1; + uint32_t Encrypted : 1; + uint32_t IntegrityStream : 1; - uint32 Virtual : 1; // FILE_ATTRIBUTE_VIRTUAL - uint32 NoScrub : 1; - uint32 Ea : 1; + uint32_t Virtual : 1; // FILE_ATTRIBUTE_VIRTUAL + uint32_t NoScrub : 1; + uint32_t Ea : 1; }; struct _Unix { - uint32 GlobalExecute : 1; // S_IXOTH - uint32 GlobalWrite : 1; // S_IWOTH - uint32 GlobalRead : 1; // S_IROTH - uint32 GlobalReserved : 1; + uint32_t GlobalExecute : 1; // S_IXOTH + uint32_t GlobalWrite : 1; // S_IWOTH + uint32_t GlobalRead : 1; // S_IROTH + uint32_t GlobalReserved : 1; - uint32 GroupExecute : 1; // S_IXGRP - uint32 GroupWrite : 1; // S_IWGRP - uint32 GroupRead : 1; // S_IRGRP - uint32 GroupReserved : 1; + uint32_t GroupExecute : 1; // S_IXGRP + uint32_t GroupWrite : 1; // S_IWGRP + uint32_t GroupRead : 1; // S_IRGRP + uint32_t GroupReserved : 1; - uint32 UserExecute : 1; // S_IXUSR - uint32 UserWrite : 1; // S_IWUSR - uint32 UserRead : 1; // S_IRUSR - uint32 UserReserved : 1; + uint32_t UserExecute : 1; // S_IXUSR + uint32_t UserWrite : 1; // S_IWUSR + uint32_t UserRead : 1; // S_IRUSR + uint32_t UserReserved : 1; - uint32 Sticky : 1; - uint32 SetGid : 1; - uint32 SetUid : 1; + uint32_t Sticky : 1; + uint32_t SetGid : 1; + uint32_t SetUid : 1; }; bool IsWindows; // switch between Win and Unix members in the union union { - uint32 u32; + uint32_t u32; _Win Win; _Unix Unix; }; LPermissions() { IsWindows = false; u32 = 0; } - typedef LHashTbl,const char*> FlagMap; + typedef LHashTbl,const char*> FlagMap; FlagMap &EnumFlags() { static FlagMap fWin, fUnix; FlagMap &m = IsWindows ? fWin : fUnix; if (m.Length() == 0) { for ( auto s : GString ( IsWindows ? "0x1,ReadOnly\n0x2,Hidden\n0x4,System\n" "0x10,Folder\n0x20,Archive\n0x40,Device\n0x80,Normal\n" "0x100,Temporary\n0x200,Sparse\n0x400,ReparsePoint\n0x800,Compressed\n" "0x1000,Offline\n0x2000,ContentNotIndexed\n0x4000,Encrypted\n0x8000,IntegrityStream\n" "0x10000,Virtual\n0x20000,NoScrub\n0x40000,Ea\n" : "0x1,GlobalExecute\n0x2,GlobalWrite\n0x4,GlobalRead\n" "0x10,GroupExecute\n0x20,GroupWrite\n0x40,GroupRead\n" "0x100,UserExecute\n0x200,UserWrite\n0x400,UserRead\n" "0x1000,Sticky\n0x2000,SetGid\n0x4000,SetUid" ).Split("\n") ) { auto p = s.Split(","); fWin.Add(htoi(p[0].Get()),p[1]); } } return m; } bool UnitTest() { #ifdef WINDOWS u32 = FILE_ATTRIBUTE_READONLY; #else u32 = 1; #endif if (Win.System || !Win.ReadOnly || Win.Hidden) return false; #ifdef WINDOWS u32 = FILE_ATTRIBUTE_HIDDEN; if (Win.System || Win.ReadOnly || !Win.Hidden) return false; u32 = FILE_ATTRIBUTE_SYSTEM; if (!Win.System || Win.ReadOnly || Win.Hidden) return false; #endif u32 = 0x400; if (!Unix.UserRead || Unix.UserWrite || Unix.UserExecute) return false; return true; } }; #endif diff --git a/include/common/LStringLayout.h b/include/common/LStringLayout.h --- a/include/common/LStringLayout.h +++ b/include/common/LStringLayout.h @@ -1,117 +1,117 @@ #ifndef _GDISPLAY_STRING_LAYOUT_H_ #define _GDISPLAY_STRING_LAYOUT_H_ #include "GDisplayString.h" #include "GCss.h" #include "GFontCache.h" /// Section of text with the same style. struct LLayoutRun : public GCss { GString Text; LLayoutRun(GCss *Style) { if (Style) { GCss *This = this; *This = *Style; } } }; /// Display string of a certain style. struct LLayoutString : public GDisplayString { int Fx, y, Line; GColour Fore, Back; ssize_t Offset; LLayoutRun *Src; LLayoutString(GFont *f, const char *s, ssize_t l = -1, GSurface *pdc = 0) : GDisplayString(f, s, l, pdc) { Offset = 0; Fx = y = 0; Layout(); Src = NULL; } LLayoutString(LLayoutString *Ls, const char *s, ssize_t l = -1) : GDisplayString(Ls->GetFont(), s, l) { Fx = Ls->Fx; y = Ls->y; Fore = Ls->Fore; Back = Ls->Back; Line = Ls->Line; Offset = Ls->Offset; Src = Ls->Src; } void Set(int LineIdx, int FixX, int YPx, LLayoutRun *Lr, ssize_t Start); }; /// This class lays out a block of text according to the given styles. It /// builds upon the GDisplayString class to render sections of text. class LStringLayout { protected: GFontCache *FontCache; // Min and max bounds GdcPt2 Min, Max; int MinLines; // Setting bool Wrap; bool AmpersandToUnderline; // Array of display strings... GArray Text; GArray Strs; GRect Bounds; public: bool Debug; LStringLayout(GFontCache *fc); ~LStringLayout(); void Empty(); bool GetWrap() { return Wrap; } void SetWrap(bool b) { Wrap = b; } GdcPt2 GetMin() { return Min; } GdcPt2 GetMax() { return Max; } GArray *GetStrs() { return &Strs; } GRect GetBounds() { return Bounds; } /// Adds a run of text with the same style bool Add(const char *Str, GCss *Style); - uint32 NextChar(char *s); - uint32 PrevChar(char *s); + uint32_t NextChar(char *s); + uint32_t PrevChar(char *s); GFont *GetBaseFont(); void SetBaseFont(GFont *f); // Pre-layout min/max calculation void DoPreLayout(int32 &MinX, int32 &MaxX); // Creates the layout of from the runs in 'Text' bool DoLayout(int Width, int MinYSize = 0, bool Debug = false); /// Paints the laid out strings at 'pt'. void Paint( GSurface *pDC, GdcPt2 pt, GColour Back, GRect &rc, bool Enabled, bool Focused); }; #endif diff --git a/include/common/LThreadEvent.h b/include/common/LThreadEvent.h --- a/include/common/LThreadEvent.h +++ b/include/common/LThreadEvent.h @@ -1,56 +1,56 @@ #ifndef _GTHREADEVENT_H_ #define _GTHREADEVENT_H_ #if defined(CARBON) #define USE_MACH_SEM 1 #include #include #elif defined(LINUX) #define USE_POSIX_SEM 1 #include #endif class LgiClass LThreadEvent : public GBase { - uint32 LastError; + uint32_t LastError; #if USE_MACH_SEM task_t Task; semaphore_t Sem; #elif USE_POSIX_SEM sem_t Local; sem_t *Sem; #elif defined(POSIX) pthread_cond_t Cond; pthread_mutex_t Mutex; #elif defined(WIN32) HANDLE Event; #endif public: enum WaitStatus { WaitError, WaitTimeout, WaitSignaled, }; LThreadEvent(const char *name = NULL); ~LThreadEvent(); #if USE_MACH_SEM semaphore_t Handle() { return Sem; } #elif USE_POSIX_SEM sem_t *Handle() { return Sem; } #elif defined(POSIX) int Handle() { return -1; } // ? #elif defined(WIN32) HANDLE Handle() { return Event; } #endif bool IsOk(); bool Signal(); WaitStatus Wait(int32 Timeout = -1); - uint32 GetError(); + uint32_t GetError(); }; #endif diff --git a/include/common/LUnicodeString.h b/include/common/LUnicodeString.h --- a/include/common/LUnicodeString.h +++ b/include/common/LUnicodeString.h @@ -1,235 +1,235 @@ #ifndef _LUNICODE_STR_H_ #define _LUNICODE_STR_H_ #include "GUnicode.h" /// Yet another attempt to abstract away the unicode nightmare. template class LUnicodeString { T *start, *cur, *next; ssize_t words, chars, alloc/*words*/; bool own; bool Alloc(ssize_t words) { if (start && own) { ssize_t c = cur - start, n = next - start; start = (T*) realloc(start, sizeof(T) * words); if (!start) return false; cur = start + c; next = start + n; } else { start = (T*) calloc(sizeof(T), words); cur = start; next = NULL; } alloc = words; own = true; return true; } - uint32 Read(const uint8 *&s) + uint32_t Read(const uint8_t *&s) { if (IsUtf8_1Byte(*s)) return *s++; #define Trailing(sh) \ if (!IsUtf8_Trail(*s)) return 0; \ Out |= (*s++ & 0x3f) << sh; if (IsUtf8_2Byte(*s)) { - uint32 Out = ((int)(*s++ & 0x1f)) << 6; + uint32_t Out = ((int)(*s++ & 0x1f)) << 6; Trailing(0); return Out; } if (IsUtf8_3Byte(*s)) { - uint32 Out = ((int)(*s++ & 0x1f)) << 12; + uint32_t Out = ((int)(*s++ & 0x1f)) << 12; Trailing(6); Trailing(0); return Out; } if (IsUtf8_4Byte(*s)) { - uint32 Out = ((int)(*s++ & 0x1f)) << 18; + uint32_t Out = ((int)(*s++ & 0x1f)) << 18; Trailing(12); Trailing(6); Trailing(0); return Out; } #undef Trailing return 0; } - uint32 Read(uint8 *&s) { return Read((const uint8*&)s); } - uint32 Read(char *&s) { return Read((const uint8*&)s); } + uint32_t Read(uint8_t *&s) { return Read((const uint8_t*&)s); } + uint32_t Read(char *&s) { return Read((const uint8_t*&)s); } - uint32 Read(const uint16 *&s) + uint32_t Read(const uint16_t *&s) { int n = *s & 0xfc00; if (n == 0xd800 || n == 0xdc00) { int w = (*s & 0x3c0) >> 6; int zy = *s++ & 0x3f; return ((w + 1) << 16) | (zy << 10) | (*s++ & 0x3ff); } return *s++; } - uint32 Read(uint16 *&s) { return Read((const uint16*&)s); } + uint32_t Read(uint16_t *&s) { return Read((const uint16_t*&)s); } - void Write(uint16 *&s, uint32 ch) + void Write(uint16_t *&s, uint32_t ch) { if (ch < 0x10000) { if (!(ch > 0xD7FF && ch < 0xE000)) { // 1 word UTF *s++ = ch; return; } LgiAssert(!"Invalid char."); return; } // 2 word UTF int w = ch - 0x10000; *s++ = 0xd800 + (w >> 10); *s++ = 0xdc00 + (ch & 0x3ff); } #ifdef WINDOWS - uint32 Read(char16 *&s) { return Read((const uint16*&)s); } - void Write(char16 *&s, uint32 ch) { Write((uint16*&)s, ch); } + uint32_t Read(char16 *&s) { return Read((const uint16_t*&)s); } + void Write(char16 *&s, uint32_t ch) { Write((uint16_t*&)s, ch); } #else uint32 Read(char16 *&s) { return Read((const uint32*&)s); } void Write(char16 *&s, uint32 ch) { Write((uint32*&)s, ch); } #endif - uint32 Read(const uint32 *&s) + uint32_t Read(const uint32_t *&s) { return *s++; } - void Write(uint32 *&s, uint32 ch) + void Write(uint32_t *&s, uint32_t ch) { *s++ = ch; } void Scan() { if (chars >= 0) return; chars = 0; if (words >= 0) { // Length terminated T *End = start + words; T *s = start; while (s < End) { if (Read(s)) chars++; } } else { // NULL terminated T *s = start; while (Read(s)) chars++; words = s - start; } } void Construct(T *init, ssize_t sz) { own = false; start = init; cur = init; next = NULL; words = sz; chars = -1; alloc = 0; } public: LUnicodeString(T *init = NULL, ssize_t words = -1) { Construct(init, words); } ~LUnicodeString() { Empty(); } ssize_t Bytes() { Scan(); return words * sizeof(T); } ssize_t Words() { Scan(); return words; } ssize_t Chars() { Scan(); return chars; } T *Get() { return cur; } T *End() { Scan(); return start + words; } void Empty() { if (own) { free(start); start = NULL; own = false; } next = cur = NULL; words = alloc = 0; chars = -1; } class Iter { LUnicodeString *u; T *p; public: Iter(LUnicodeString *str, T *cur) : u(str), p(cur) {} - operator uint32() + operator uint32_t() { return u->Read(p); } - Iter &operator =(uint32 ch) + Iter &operator =(uint32_t ch) { LgiAssert(u->start != NULL); u->Write(p, ch); return *this; } Iter &operator *() { return *this; } }; Iter operator *() { return Iter(this, cur); } Iter operator ++(int) { if (!start) Alloc(256); Iter old(this, cur); Read(cur); return old; } }; LgiFunc bool LUnicodeString_UnitTests(); #endif diff --git a/include/common/LgiClass.h b/include/common/LgiClass.h --- a/include/common/LgiClass.h +++ b/include/common/LgiClass.h @@ -1,347 +1,347 @@ #ifndef _LGI_CLASS_H_ #define _LGI_CLASS_H_ #include "LgiInc.h" #include "LgiDefs.h" #if defined __OBJC__ #include #endif // Virtual input classes class GKey; class GMouse; // General GUI classes class GTarget; class GComponent; class GEvent; class GId; class GApp; class GWindow; class GWin32Class; class GView; class GLayout; class GFileSelect; class GFindReplace; class GSubMenu; class GMenuItem; class GMenu; class GToolBar; class GToolButton; class GSplitter; class GStatusPane; class GStatusBar; class GToolColour; class GScrollBar; class GImageList; class GDialog; // General objects class LgiClass GBase { char *_Name8; char16 *_Name16; public: GBase(); virtual ~GBase(); virtual char *Name(); virtual bool Name(const char *n); virtual char16 *NameW(); virtual bool NameW(const char16 *n); }; #define AssignFlag(f, bit, to) if (to) f |= bit; else f &= ~(bit) /// Sets the output stream for the LgiTrace statement. By default the stream output /// is to .txt in the executables folder or $LSP_APP_ROOT\.txt if /// that is not writable. If the stream is set to something then normal file output is /// directed to the specified stream instead. LgiFunc void LgiTraceSetStream(class GStreamI *stream); /// Gets the log file path LgiFunc bool LgiTraceGetFilePath(char *LogPath, int BufLen); /// Writes a debug statement to a output stream, or if not defined with LgiTraceSetStream /// then to a log file (see LgiTraceSetStream for details) /// /// Default path is ./.txt relative to the executable. /// Fallback path is LgiGetSystemPath(LSP_APP_ROOT). LgiFunc void LgiTrace(const char *Format, ...); #ifndef LGI_STATIC /// Same as LgiTrace but writes a stack trace as well. LgiFunc void LgiStackTrace(const char *Format, ...); #endif /// General user interface event class LgiClass GUiEvent { public: int Flags; GUiEvent() { Flags = 0; } virtual ~GUiEvent() {} virtual void Trace(const char *Msg) {} /// The key or mouse button was being pressed. false on the up-click. bool Down() { return TestFlag(Flags, LGI_EF_DOWN); } /// The mouse button was double clicked. bool Double() { return TestFlag(Flags, LGI_EF_DOUBLE); } /// A ctrl button was held down during the event bool Ctrl() { return TestFlag(Flags, LGI_EF_CTRL); } /// A alt button was held down during the event bool Alt() { return TestFlag(Flags, LGI_EF_ALT); } /// A shift button was held down during the event bool Shift() { return TestFlag(Flags, LGI_EF_SHIFT); } /// The system key was held down (windows key / apple key etc) bool System() { return TestFlag(Flags, LGI_EF_SYSTEM); } // Set void Down(bool i) { AssignFlag(Flags, LGI_EF_DOWN, i); } void Double(bool i) { AssignFlag(Flags, LGI_EF_DOUBLE, i); } void Ctrl(bool i) { AssignFlag(Flags, LGI_EF_CTRL, i); } void Alt(bool i) { AssignFlag(Flags, LGI_EF_ALT, i); } void Shift(bool i) { AssignFlag(Flags, LGI_EF_SHIFT, i); } void System(bool i) { AssignFlag(Flags, LGI_EF_SYSTEM, i); } bool Modifier() { #if defined(BEOS) return Alt(); #elif defined(MAC) return System(); // "Apple" key #else // win32 and linux return Ctrl(); #endif } #if defined COCOA void SetModifer(uint32 modifierKeys); #else - void SetModifer(uint32 modifierKeys) + void SetModifer(uint32_t modifierKeys) { #if defined(MAC) #if !defined COCOA System(modifierKeys & cmdKey); Shift(modifierKeys & shiftKey); Alt(modifierKeys & optionKey); Ctrl(modifierKeys & controlKey); #endif #elif defined(__GTK_H__) System(modifierKeys & Gtk::GDK_MOD4_MASK); Shift(modifierKeys & Gtk::GDK_SHIFT_MASK); Alt(modifierKeys & Gtk::GDK_MOD1_MASK); Ctrl(modifierKeys & Gtk::GDK_CONTROL_MASK); #endif } #endif }; /// All the information related to a keyboard event class LgiClass GKey : public GUiEvent { public: /// The virtual code for key char16 vkey; /// The unicode character for the key char16 c16; /// OS Specific - uint32 Data; + uint32_t Data; /// True if this is a standard character (ie not a control key) bool IsChar; GKey() { vkey = 0; c16 = 0; Data = 0; IsChar = 0; } GKey(int vkey, int flags); void Trace(const char *Msg) { LgiTrace("%s GKey vkey=%i(0x%x) c16=%i(%c) IsChar=%i down=%i ctrl=%i alt=%i sh=%i sys=%i\n", Msg ? Msg : (char*)"", vkey, vkey, c16, c16 >= ' ' && c16 < 127 ? c16 : '.', IsChar, Down(), Ctrl(), Alt(), Shift(), System()); } /// Returns the character in the right case... char16 GetChar() { if (Shift() ^ TestFlag(Flags, LGI_EF_CAPS_LOCK)) { return (c16 >= 'a' && c16 <= 'z') ? c16 - 'a' + 'A' : c16; } else { return (c16 >= 'A' && c16 <= 'Z') ? c16 - 'A' + 'a' : c16; } } /// \returns true if this event should show a context menu bool IsContextMenu(); }; /// \brief All the parameters of a mouse click event /// /// The parent class GUiEvent keeps information about whether it was a Down() /// or Double() click. You can also query whether the Alt(), Ctrl() or Shift() /// keys were pressed at the time the event occured. /// /// To get the position of the mouse in screen co-ordinates you can either use /// GView::GetMouse() and pass true in the 'ScreenCoords' parameter. Or you can /// construct a GdcPt2 out of the x,y fields of this class and use GView::PointToScreen() /// to map the point to screen co-ordinates. class LgiClass GMouse : public GUiEvent { public: /// Receiving view class GViewI *Target; /// True if specified in view coordinates, false if in screen coords bool ViewCoords; /// The x co-ordinate of the mouse relitive to the current view int x; /// The y co-ordinate of the mouse relitive to the current view int y; GMouse() { Target = 0; ViewCoords = true; x = y = 0; } void Trace(const char *Msg) { LgiTrace("%s GMouse pos=%i,%i view=%i btns=%i/%i/%i dwn=%i dbl=%i " "ctrl=%i alt=%i sh=%i sys=%i\n", Msg ? Msg : (char*)"", x, y, ViewCoords, Left(), Middle(), Right(), Down(), Double(), Ctrl(), Alt(), Shift(), System()); } /// True if the left mouse button was clicked bool Left() { return TestFlag(Flags, LGI_EF_LEFT); } /// True if the middle mouse button was clicked bool Middle() { return TestFlag(Flags, LGI_EF_MIDDLE); } /// True if the right mouse button was clicked bool Right() { return TestFlag(Flags, LGI_EF_RIGHT); } /// True if the mouse event is a move, false for a click event. bool IsMove() { return TestFlag(Flags, LGI_EF_MOVE); } /// Sets the left button flag void Left(bool i) { AssignFlag(Flags, LGI_EF_LEFT, i); } /// Sets the middle button flag void Middle(bool i) { AssignFlag(Flags, LGI_EF_MIDDLE, i); } /// Sets the right button flag void Right(bool i) { AssignFlag(Flags, LGI_EF_RIGHT, i); } /// Sets the move flag void IsMove(bool i) { AssignFlag(Flags, LGI_EF_MOVE, i); } /// Converts to screen coordinates bool ToScreen(); /// Converts to local coordinates bool ToView(); /// \returns true if this event should show a context menu bool IsContextMenu(); #if defined __OBJC__ void SetFromEvent(NSEvent *ev, NSView *view); #else - void SetButton(uint32 Btn) + void SetButton(uint32_t Btn) { #if defined(MAC) && defined(__CARBONEVENTS__) Left(Btn == kEventMouseButtonPrimary); Right(Btn == kEventMouseButtonSecondary); Middle(Btn == kEventMouseButtonTertiary); #endif } #endif }; #include "GAutoPtr.h" /// Holds information pertaining to an application class GAppInfo { public: /// The path to the executable for the app GAutoString Path; /// Plain text name for the app GAutoString Name; /// A path to an icon to display for the app GAutoString Icon; /// The params to call the app with GAutoString Params; }; template void LSwap(T &a, T &b) { T tmp = a; a = b; b = tmp; } template RESULT LHash(const CHAR *v, ssize_t l, bool Case) { RESULT h = 0; if (Case) { // case sensitive if (l > 0) { while (l--) h = (h << 5) - h + *v++; } else { for (; *v; v ++) h = (h << 5) - h + *v; } } else { // case insensitive CHAR c; if (l > 0) { while (l--) { c = tolower(*v); v++; h = (h << 5) - h + c; } } else { for (; *v; v++) { c = tolower(*v); h = (h << 5) - h + c; } } } return h; } #endif diff --git a/include/common/LgiClasses.h b/include/common/LgiClasses.h --- a/include/common/LgiClasses.h +++ b/include/common/LgiClasses.h @@ -1,1936 +1,1936 @@ /** \file \author Matthew Allen \date 19/12/1997 \brief Gui class definitions Copyright (C) 1997-2004, Matthew Allen */ ///////////////////////////////////////////////////////////////////////////////////// // Includes #ifndef __GUI_H #define __GUI_H #if defined BEOS #include #endif #include "LMutex.h" #include "LgiOsClasses.h" #include "GMem.h" #include "GArray.h" #include "LgiCommon.h" #include "GXmlTree.h" #include "GDragAndDrop.h" ///////////////////////////////////////////////////////////////////////////////////// // Externs extern long MouseWatcher(void *Ptr); extern bool LgiCheckFile(char *Path, int PathSize); LgiFunc bool LgiPostEvent(OsView Wnd, int Event, GMessage::Param a = 0, GMessage::Param b = 0); LgiFunc GViewI *GetNextTabStop(GViewI *v, bool Back); /// Converts an OS error code into a text string -LgiClass GAutoString LgiErrorCodeToString(uint32 ErrorCode); +LgiClass GAutoString LgiErrorCodeToString(uint32_t ErrorCode); #if defined(MAC) && !defined(COCOA) LgiFunc void DumpHnd(HIViewRef v, int depth = 0); #endif ///////////////////////////////////////////////////////////////////////////////// #if WINNATIVE typedef DWORD OsProcessId; #else typedef int OsProcessId; #endif /// Returns the current process ID #define LgiProcessId() (LgiApp->GetProcessId()) /// Returns a pointer to the GApp object. /// /// \warning Don't use this before you have created your GApp object. i.e. in a constructor /// of a global static class which is initialized before the main begins executing. #define LgiApp (GApp::ObjInstance()) /// Returns a system font pointer. /// /// \warning Don't use this before you have created your GApp object. i.e. in a constructor /// of a global static class which is initialized before the main begins executing. #define SysFont (LgiApp->SystemNormal) /// Returns a bold system font pointer. /// /// \warning Don't use this before you have created your GApp object. i.e. in a constructor /// of a global static class which is initialized before the main begins executing. #define SysBold (LgiApp->SystemBold) /// Exits the application right now! /// /// \warning This will cause data loss if you have any unsaved data. Equivilant to exit(0). LgiFunc void LgiExitApp(); /// Closes the application gracefully. /// /// This actually causes GApp::Run() to stop processing message and return. #define LgiCloseApp() LgiApp->Exit(false) #if defined(LINUX) && !defined(LGI_SDL) #define ThreadCheck() LgiAssert(InThread()) #else #define ThreadCheck() #endif /// Optional arguments to the GApp object struct GAppArguments { /// Don't initialize the skinning engine. bool NoSkin; }; /// \brief Singleton class for handling application wide settings and methods /// /// This should be the first class you create, passing in the arguments from the /// operating system. And once your initialization is complete the 'Run' method /// is called to enter the main application loop that processes messages for the /// life time of the application. class LgiClass GApp : virtual public GAppI, public GBase, public OsApplication { friend class GView; public: typedef LHashTbl, GWin32Class*> ClassContainer; protected: // private member vars class GAppPrivate *d; #if defined LGI_SDL void OnSDLEvent(GMessage *m); #elif defined WIN32 CRITICAL_SECTION StackTraceSync; friend LONG __stdcall _ExceptionFilter_Redir(LPEXCEPTION_POINTERS e); LONG __stdcall _ExceptionFilter(LPEXCEPTION_POINTERS e, char *ProductId); friend class GWin32Class; ClassContainer *GetClasses(); #elif defined ATHEOS char *_AppFile; #elif defined BEOS void RefsReceived(BMessage *Msg); #elif defined LINUX friend class GClipBoard; virtual void OnEvents(); void DeleteMeLater(GViewI *v); void SetClipBoardContent(OsView Hnd, GVariant &v); bool GetClipBoardContent(OsView Hnd, GVariant &v, GArray &Types); #endif friend class GMouseHook; static GMouseHook *MouseHook; public: // Static publics /// Use 'LgiApp' to return a pointer to the GApp object static GApp *ObjInstance(); static class GSkinEngine *SkinEngine; // public member vars /// The system font GFont *SystemNormal; /// The system font in bold GFont *SystemBold; /// Pointer to the applications main window GWindow *AppWnd; /// Returns true if the GApp object initialized correctly bool IsOk(); /// Returns this processes ID OsProcessId GetProcessId(); /// Returns the thread currently running the active message loop OsThread _GetGuiThread(); OsThreadId GetGuiThreadId(); bool InThread(); /// Returns the number of CPU cores the machine has int GetCpuCount(); /// Construct the object GApp ( /// The arguments passed in by the OS. OsAppArguments &AppArgs, /// The application's name. const char *AppName, /// Optional args GAppArguments *ObjArgs = 0 ); /// Destroys the object virtual ~GApp(); /// Returns the version of Lgi used. String returned is in the form '#.#.#' const char *GetLgiVersion() { return LGI_VER; } /// Resets the arguments void SetAppArgs(OsAppArguments &AppArgs); /// Returns the arguemnts OsAppArguments *GetAppArgs(); /// Returns the n'th argument as a heap string. Free with DeleteArray(...). const char *GetArgumentAt(int n); /// Enters the message loop. bool Run ( /// If true this function will return when the application exits (with LgiCloseApp()). /// Otherwise if false only pending events will be processed and then the function returns. bool Loop = true, /// Idle callback OnIdleProc IdleCallback = NULL, /// Param for IdleCallback void *IdleParam = NULL ); /// Event called to process the command line void OnCommandLine(); /// Event called to process files dropped on the application void OnReceiveFiles(GArray &Files); /// Event called to process URLs given to the application void OnUrl(const char *Url); /// Exits the event loop with the code specified void Exit ( /// The application exit code. int Code = 0 ); /// \brief Parses the command line for a switch /// \return true if the option exists. bool GetOption ( /// The option to look for. const char *Option, /// String to receive the value (if any) of the option GString &Value ); /// \brief Parses the command line for a switch /// \return true if the option exists. bool GetOption ( /// The option to look for. const char *Option, /// The buffer to receive the value of the command line parameter or NULL if you don't care. char *Dst = 0, /// The buffer size in bytes int DstSize = 0 ); /// Gets the application conf stored in lgi.conf GXmlTag *GetConfig(const char *Tag); /// Sets a single tag in the config. (Not written to disk) void SetConfig(GXmlTag *Tag); /// Gets the control with the keyboard focus GViewI *GetFocus(); /// Gets the MIME type of a file /// \returns the mime type or NULL if unknown. GAutoString GetFileMimeType ( /// The file to identify const char *File ); /// Gets the applications that can handle a file of a certain mime type bool GetAppsForMimeType(char *Mime, GArray &Apps); /// Get a system metric int32 GetMetric ( /// One of #LGI_MET_DECOR_X, #LGI_MET_DECOR_Y LgiSystemMetric Metric ); /// Get the mouse hook instance GMouseHook *GetMouseHook(); /// Gets the singleton symbol lookup class class GSymLookup *GetSymLookup(); /// \returns true if the process is running with elevated permissions bool IsElevated(); /// Gets the font cache class GFontCache *GetFontCache(); // OS Specific #if defined(LGI_SDL) /// This keeps track of the dirty rectangle and issues a M_INVALIDATE /// event when needed to keep the screen up to date. bool InvalidateRect(GRect &r); /// Push a new window to the top of the window stack bool PushWindow(GWindow *w); /// Remove the top most window GWindow *PopWindow(); /// Sets up mouse tracking beyond the current window... void CaptureMouse(bool capture); /// Returns the freetype version as a string. GString GetFreetypeVersion(); #elif defined(WIN32) HINSTANCE GetInstance(); int GetShow(); /// \returns true if the application is running under Wine on Linux. This is useful to know /// if you need to work around missing functionality in the Wine implementation. bool IsWine(); #elif defined(LINUX) class GLibrary *GetWindowManagerLib(); class DesktopInfo { friend class GApp; GString File; bool Dirty; struct KeyPair { GString Key, Value; }; struct Section { GString Name; GArray Values; KeyPair *Get(const char *Name, bool Create, bool &Dirty) { for (unsigned i=0; iKey.Equals(Name)) return kp; } if (Create) { KeyPair *kp = &Values.New(); kp->Key = Name; Dirty = true; return kp; } return NULL; } }; GArray
    Data; bool Serialize(bool Write); Section *GetSection(const char *Name, bool Create); public: DesktopInfo(const char *file); GString Get(const char *Field, const char *Section = NULL); bool Set(const char *Field, const char *Value, const char *Section = NULL); bool Update() { return Dirty ? Serialize(true) : true; } const char *GetFile() { return File; } }; DesktopInfo *GetDesktopInfo(); bool SetApplicationIcon(const char *FileName); bool PostEvent(GViewI *View, int Msg, GMessage::Param a = 0, GMessage::Param b = 0); void OnDetach(GViewI *View); #endif }; /// \brief The base class for all windows in the GUI. /// /// This is the core object that all on screen windows inherit from. It encapsulates /// a HWND on Win32, a BView on BeOS, a Window on X11 + Mac OS X. Used by itself it's not a /// top level window, for that see the GWindow class. /// /// To create a top level window see GWindow or GDialog. /// /// For a GView with scroll bars use GLayout. /// class LgiClass GView : virtual public GViewI, virtual public GBase { friend class GWindow; friend class GLayout; friend class GControl; friend class GMenu; friend class GSubMenu; friend class GWnd; friend class GScrollBar; friend class GFileTarget; friend class GDialog; friend class GDragDropTarget; friend class GPopup; friend class GWindowPrivate; friend bool SysOnKey(GView *w, GMessage *m); #if defined(__GTK_H__) friend Gtk::gboolean lgi_widget_expose(Gtk::GtkWidget *widget, Gtk::GdkEventExpose *e); friend Gtk::gboolean lgi_widget_click(Gtk::GtkWidget *widget, Gtk::GdkEventButton *ev); friend Gtk::gboolean lgi_widget_motion(Gtk::GtkWidget *widget, Gtk::GdkEventMotion *ev); friend Gtk::gboolean GViewCallback(Gtk::GtkWidget *widget, Gtk::GdkEvent *event, GView *view); friend Gtk::gboolean PopupEvent(Gtk::GtkWidget *widget, Gtk::GdkEvent *event, class GPopup *This); friend Gtk::gboolean GtkViewCallback(Gtk::GtkWidget *widget, Gtk::GdkEvent *event, GView *This); virtual Gtk::gboolean OnGtkEvent(Gtk::GtkWidget *widget, Gtk::GdkEvent *event); public: void OnGtkRealize(); virtual void OnGtkDelete(); private: #endif #if defined WIN32 friend class GWin32Class; friend class GCombo; friend LRESULT CALLBACK DlgRedir(OsView hWnd, UINT m, WPARAM a, LPARAM b); - static void CALLBACK TimerProc(OsView hwnd, UINT uMsg, UINT_PTR idEvent, uint32 dwTime); + static void CALLBACK TimerProc(OsView hwnd, UINT uMsg, UINT_PTR idEvent, uint32_t dwTime); #elif defined MAC #if defined(COCOA) #else friend OSStatus LgiWindowProc(EventHandlerCallRef, EventRef, void *); friend OSStatus LgiRootCtrlProc(EventHandlerCallRef, EventRef, void *); friend OSStatus CarbonControlProc(EventHandlerCallRef, EventRef, void *); friend OSStatus GViewProc(EventHandlerCallRef, EventRef, void *); friend OSStatus LgiViewDndHandler(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData); #endif #elif defined BEOS friend class GButtonRedir; friend class _OsEditFrame; friend class BViewRedir; friend long _lgi_pulse_thread(void *ptr); friend GView *_lgi_search_children(GView *v, int &x, int &y); #endif #if defined(LGI_SDL) friend Uint32 SDL_PulseCallback(Uint32 interval, GView *v); friend class GApp; #endif GRect Pos; int _InLock; protected: class GViewPrivate *d; OsView _View; // OS specific handle to view object GView *_Window; LMutex *_Lock; uint16 _BorderSize; uint16 _IsToolBar; int WndFlags; static GViewI *_Capturing; static GViewI *_Over; #if defined(__GTK_H__) || defined(LGI_SDL) public: enum LockOp { OpCreate, OpDelete, OpExists, }; static bool LockHandler(GViewI *v, LockOp Op); #endif protected: #if defined WINNATIVE - uint32 GetStyle(); - void SetStyle(uint32 i); - uint32 GetExStyle(); - void SetExStyle(uint32 i); - uint32 GetDlgCode(); - void SetDlgCode(uint32 i); + uint32_t GetStyle(); + void SetStyle(uint32_t i); + uint32_t GetExStyle(); + void SetExStyle(uint32_t i); + uint32_t GetDlgCode(); + void SetDlgCode(uint32_t i); /// \brief Gets the win32 class passed to CreateWindowEx() const char *GetClassW32(); /// \brief Sets the win32 class passed to CreateWindowEx() void SetClassW32(const char *s); /// \brief Creates a class to pass to CreateWindowEx(). If this methed is not /// explicitly called then the string from GetClass() is used to create a class, /// which is usually the name of the object. GWin32Class *CreateClassW32(const char *Class = 0, HICON Icon = 0, int AddStyles = 0); virtual int SysOnNotify(int Msg, int Code) { return 0; } #elif defined BEOS struct OsMouseInfo; friend long _lgi_mouse_thread(OsMouseInfo *Info); OsMouseInfo *_MouseInfo; OsThread _CaptureThread; OsThread _PulseThread; int _PulseRate; BWindow *_QuitMe; void _Key(const char *bytes, int32 numBytes, bool down); virtual bool QuitRequested() {} #elif defined MAC bool _Attach(GViewI *parent); #if defined(COCOA) public: GdcPt2 Flip(GdcPt2 p); GRect Flip(GRect p); void OnCocoaLayout(); protected: #else OsView _CreateCustomView(); virtual bool _OnGetInfo(HISize &size, HISize &line, HIRect &bounds, HIPoint &origin) { return false; } virtual void _OnScroll(HIPoint &origin) {} #endif #endif // Complex Region searches /// Finds the largest rectangle in the region GRect *FindLargest(GRegion &r); /// Finds the smallest rectangle that would fit a window 'Sx' by 'Sy' GRect *FindSmallestFit(GRegion &r, int Sx, int Sy); /// Finds the largest rectangle on the specified GRect *FindLargestEdge ( /// The region to search GRegion &r, /// The edge to look at: /// \sa GV_EDGE_TOP, GV_EDGE_RIGHT, GV_EDGE_BOTTOM or GV_EDGE_LEFT int Edge ); virtual void _Delete(); GViewI *FindReal(GdcPt2 *Offset = 0); bool HandleCapture(GView *Wnd, bool c); #if !WINNATIVE GView *&PopupChild(); virtual bool _Mouse(GMouse &m, bool Move); void _Focus(bool f); #endif virtual bool OnViewMouse(GView *v, GMouse &m) { return true; } virtual bool OnViewKey(GView *v, GKey &k) { return false; } virtual void OnNcPaint(GSurface *pDC, GRect &r); /// List of children views. friend class GViewIter; List Children; #ifdef LGI_SDL public: #endif virtual void _Paint(GSurface *pDC = NULL, GdcPt2 *Offset = NULL, GRegion *Update = NULL); public: /// \brief Creates a view/window. /// /// On non-Win32 platforms the default argument is the class that redirects the /// C++ virtual event handlers to the GView handlers. Which is usually the /// 'DefaultOsView' class. If you pass NULL in a DefaultOsView will be created to /// do the job. On BeOS you can subclass the native controls by passing in an /// instance of the BView based class. GView ( /// The handle that the OS knows the window by OsView wnd = NULL ); /// Destructor virtual ~GView(); /// Returns the OS handle of the view OsView Handle() { return _View; } /// Returns the ptr to a GView GView *GetGView() { return this; } /// Returns the OS handle of the top level window virtual OsWindow WindowHandle(); // Attaching windows / heirarchy bool AddView(GViewI *v, int Where = -1); bool DelView(GViewI *v); bool HasView(GViewI *v); GViewIterator *IterateViews(); /// \brief Attaches the view to a parent view. /// /// Each GView starts in an un-attached state. When you attach it to a Parent GView /// the view gains a OS-specific handle and becomes visible on the screen (if the /// Visible() property is TRUE). However if a view is inserted into the Children list /// of a GView and it's parent pointer is set correctly it will still paint on the /// screen without the OS knowing about it. This is known in Lgi as a "virtual window" /// and is primarily used to cut down on windowing resources. Mouse clicks are handled /// by the parent window and passed down to the virtual children. Virtual children /// are somewhat limited. They can't receive focus, or participate in drag and drop /// operations. If you want to see an example have a look at the GToolBar code. virtual bool Attach ( /// The parent view or NULL for a top level window GViewI *p ); /// Attachs all the views in the Children list if not already attached. virtual bool AttachChildren(); /// Detachs a window from it's parent. virtual bool Detach(); /// Returns true if the window is attached virtual bool IsAttached(); /// Destroys the window async virtual void Quit(bool DontDelete = false); // Properties /// Gets the top level window that this view belongs to GWindow *GetWindow(); /// Gets the parent view. GViewI *GetParent(); /// \brief Sets the parent view. /// /// This doesn't attach the window so that it will display. You should use GView::Attach for that. virtual void SetParent(GViewI *p); /// Sends a notification to the notify target or the parent chain void SendNotify(int Data = 0); /// Gets the window that receives event notifications GViewI *GetNotify(); /// \brief Sets the view to receive event notifications. /// /// The notify window will receive events when this view changes. By /// default the parent view receives the events. virtual void SetNotify(GViewI *n); /// \brief Each top level window (GWindow) has a lock. By calling this function /// you lock the whole GWindow and all it's children. bool Lock ( /// The file name of the caller const char *file, /// The line number of the caller int line, /// The timeout in milli-seconds or -1 to block until locked. int TimeOut = -1 ); /// Unlocks the GWindow and that this view belongs to. void Unlock(); /// Add this view to the event target sink dispatch hash table. /// This allows you to use PostThreadEvent with a handle. Which /// is safe even if the object is deleted (unlike the PostEvent /// member function). /// /// Calling this multiple times only adds the view once, but it /// returns the same handle each time. /// The view is automatically removed from the dispatch on /// deletion. /// /// \returns the handle for PostThreadEvent. int AddDispatch(); /// Called to process every message received by this window. GMessage::Result OnEvent(GMessage *Msg); /// true if the view is enabled bool Enabled(); /// Sets the enabled state void Enabled(bool e); /// true if the view is visible bool Visible(); /// Hides/Shows the view void Visible ( /// True if you want to show the view, False to hide the view/ bool v ); /// true if the view has keyboard focus bool Focus(); /// Sets the keyboard focus state on the view. void Focus(bool f); /// Get/Set the drop source GDragDropSource *DropSource(GDragDropSource *Set = NULL); /// Get/Set the drop target GDragDropTarget *DropTarget(GDragDropTarget *Set = NULL); /// Sets the drop target state of this view bool DropTarget(bool t); /// \brief Gives this view a 1 or 2 px sunken border. /// /// The size is set by the _BorderSize member variable. This border is /// not considered part of the client area. Mouse and drawing coordinates /// do not take it into account. bool Sunken(); /// Sets a sunken border around the control void Sunken(bool i); /// true if the view has a flat border bool Flat(); /// Sets the flat border state void Flat(bool i); /// \brief true if the view has a raised border /// /// The size is set by the _BorderSize member variable. This border is /// not considered part of the client area. Mouse and drawing coordinates /// do not take it into account. bool Raised(); /// Sets the raised border state void Raised(bool i); /// Draws an OS themed border void DrawThemeBorder(GSurface *pDC, GRect &r); /// \brief true if the control is currently executing in the GUI thread /// /// Some OS functions are not thread safe, and can only be called in the GUI /// thread. In the Linux implementation the GUI thread can change from time /// to time. On Win32 it stays the same. In any case if this function returns /// true it's safe to do just about anything. bool InThread(); /// \brief Asyncronously posts an event to be received by this window virtual bool PostEvent ( /// The command ID. /// \sa Should be M_USER or higher for custom events. int Cmd, /// The first 32-bits of data. Equivalent to wParam on Win32. GMessage::Param a = 0, /// The second 32-bits of data. Equivalent to lParam on Win32. GMessage::Param b = 0 ); template bool PostEvent(int Cmd, T *Ptr) { return PostEvent(Cmd, (GMessage::Param)Ptr, 0); } /// \brief Sets the utf-8 text associated with this view /// /// Name and NameW are interchangable. Using them in any order will convert the /// text between utf-8 and wide to satify any requirement. Generally once the opposing /// version of the string is required both the utf-8 and wide copies of the string /// remain cached in RAM until the Name is changed. bool Name(const char *n); /// Returns the utf-8 text associated with this view char *Name(); /// Sets the wide char text associated with this view virtual bool NameW(const char16 *n); /// \brief Returns the wide char text associated with this view /// /// On Win32 the wide characters are 16 bits, on unix systems they are 32-bit /// characters. virtual char16 *NameW(); /// \brief Gets the font this control should draw with. /// /// The default font is the system font, owned by the GApp object. virtual GFont *GetFont(); /// \brief Sets the font for this control /// /// The lifetime of the font passed in is the responsibility of the caller. /// The GView object assumes the pointer will be valid at all times. virtual void SetFont(GFont *Fnt, bool OwnIt = false); /// Returns the cursor that should be displayed for the given location /// \returns a cursor type. i.e. LCUR_Normal from LgiDefs.h LgiCursor GetCursor(int x, int y); /// \brief Get the position of the view relitive to it's parent. virtual GRect &GetPos() { return Pos; } /// Get the client region of the window relitive to itself (ie always 0,0-x,y) virtual GRect &GetClient(bool InClientSpace = true); /// Set the position of the view in terms of it's parent virtual bool SetPos(GRect &p, bool Repaint = false); /// Gets the width of the view in pixels int X() { return Pos.X(); } /// Gets the height of the view in pixels. int Y() { return Pos.Y(); } /// Gets the minimum size of the view GdcPt2 GetMinimumSize(); /// \brief Set the minimum size of the view. /// /// Only works for top level windows. void SetMinimumSize(GdcPt2 Size); /// Sets the style of the control bool SetCssStyle(const char *CssStyle); /// Gets the style of the control class GCss *GetCss(bool Create = false); /// Resolve a CSS colour, e.g.: /// auto Back = StyleColour(GCss::PropBackgroundColor, LC_MED); GColour StyleColour(int CssPropType, GColour Default, int Depth = 5); /// Sets the style of the control (will take ownership of 'css') void SetCss(GCss *css); /// Sets the CSS foreground or background colour bool SetColour(GColour &c, bool Fore); /// The class' name. Should be overriden in child classes to return the /// right class name. Mostly used for debugging, but in the win32 port it /// is also the default WIN32 class name passed to RegisterClass() in /// GView::CreateClass(). /// /// \returns the Class' name for debugging const char *GetClass(); /// The array of CSS class names. GString::Array *CssClasses(); /// \brief Captures all mouse events to this view /// /// Once you have mouse capture all mouse events will be passed to this /// view. i.e. during a mouse click. bool Capture(bool c); /// true if this view is capturing mouse events. bool IsCapturing(); /// \brief Gets the current mouse location /// \return true on success bool GetMouse ( /// The mouse location information returned GMouse &m, /// Get the location in screen coordinates bool ScreenCoords = false ); /// \brief Gets the ID associated with the view /// /// The ID of a view is designed to associate controls defined in resource /// files with a object at runtime via a C header file define. int GetId(); /// Sets the view's ID. void SetId(int i); /// true if this control is a tab stop. bool GetTabStop(); /// \brief Sets whether this control is a tab stop. /// /// A top stop is a control that receives focus if the user scrolls through the controls /// with the tab key. void SetTabStop(bool b); /// Gets the integer representation of the view's contents virtual int64 Value() { return 0; } /// Sets the integer representation of the view's contents virtual void Value(int64 i) {} /// Find a view by it's os handle virtual GViewI *FindControl(OsView hnd); /// Returns the view by it's ID virtual GViewI *FindControl ( // The ID to look for int Id ); /// Gets the value of the control identified by the ID int64 GetCtrlValue(int Id); /// Sets the value of the control identified by the ID void SetCtrlValue(int Id, int64 i); /// Gets the name (text) of the control identified by the ID char *GetCtrlName(int Id); /// Sets the name (text) of the control identified by the ID void SetCtrlName(int Id, const char *s); /// Gets the enabled state of the control identified by the ID bool GetCtrlEnabled(int Id); /// Sets the enabled state of the control identified by the ID void SetCtrlEnabled(int Id, bool Enabled); /// Gets the visible state of the control identified by the ID bool GetCtrlVisible(int Id); /// Sets the visible state of the control identified by the ID void SetCtrlVisible(int Id, bool Visible); /// Causes the given area of the view to be repainted to update the screen bool Invalidate ( /// The rectangle of the view to repaint, or NULL for the entire view GRect *r = NULL, /// true if you want to wait for the update to happen bool Repaint = false, /// false to update in client coordinates, true to update the non client region bool NonClient = false ); /// Causes the given area of the view to be repainted to update the screen bool Invalidate ( /// The region of the view to repaint GRegion *r, /// true if you want to wait for the update to happen bool Repaint = false, /// false to update in client coordinates, true to update the non client region bool NonClient = false ); /// true if the mouse event is over the view bool IsOver(GMouse &m); /// returns the sub window located at the point x,y GViewI *WindowFromPoint(int x, int y, bool Debug = false); /// Sets a timer to call the OnPulse() event void SetPulse ( /// The milliseconds between calls to OnPulse() or -1 to disable int Ms = -1 ); /// Convert a point form view coordinates to screen coordinates void PointToScreen(GdcPt2 &p); /// Convert a point form screen coordinates to view coordinates void PointToView(GdcPt2 &p); /// Get the x,y offset from the virtual window to the first real view in the parent chain bool WindowVirtualOffset(GdcPt2 *Offset); /// Get the size of the window borders GdcPt2 &GetWindowBorderSize(); /// Layout all the child views virtual bool Pour ( /// The available space to lay out the views into GRegion &r ) { return false; } /// The mouse was clicked over this view void OnMouseClick ( /// The event parameters GMouse &m ); /// Mouse moves into the area over the control void OnMouseEnter ( /// The event parameters GMouse &m ); /// Mouse leaves the area over the control void OnMouseExit ( /// The event parameters GMouse &m ); /// The mouse moves over the control void OnMouseMove ( /// The event parameters GMouse &m ); /// The mouse wheel was scrolled. bool OnMouseWheel ( /// The amount scrolled double Lines ); /// A key was pressed while this view has focus bool OnKey(GKey &k); /// The view is attached void OnCreate(); /// The view is detached void OnDestroy(); /// The view gains or loses the keyboard focus void OnFocus ( /// True if the control is receiving focus bool f ); /// \brief Called every so often by the timer system. /// \sa SetPulse() void OnPulse(); /// Called when the view position changes void OnPosChange(); /// Called on a top level window when something requests to close the window /// \returns true if it's ok to continue shutting down. bool OnRequestClose ( /// True if the operating system is shutting down. bool OsShuttingDown ); /// Return the type of cursor that should be visible when the mouse is at x,y /// e.g. #LCUR_Normal int OnHitTest ( /// The x coordinate in view coordinates int x, /// The y coordinate in view coordinates int y ); /// Called when the contents of the Children list have changed. void OnChildrenChanged(GViewI *Wnd, bool Attaching); /// Called to paint the onscreen representation of the view void OnPaint(GSurface *pDC); /// \brief Called when a child view or view with it's SetNotify() set to this window changes. /// /// The event by default will bubble up to the GWindow at the top of the window heirarchy visiting /// each GView on the way. If it reaches a GView that processes it then the event stops propergating /// up the heirarchy. int OnNotify(GViewI *Ctrl, int Flags); /// Called when a menu command is activated by the user. int OnCommand(int Cmd, int Event, OsView Wnd); /// Called after the view is attached to a new parent void OnAttach(); /// Called to get layout information for the control. It's called /// up to 3 times to collect various dimensions: /// 1) PreLayout: Get the maximum width, and optionally the minimum width. /// Called with both widths set to zero. /// Must fill out Inf.Width.Max. Use -1 to fill all available space. /// Optionally fill out the min width. /// 2) Layout: Called to work out row height. /// Called with: /// - Width.Min unchanged from previous call. /// - Width.Max is limited to Cell size. /// Must fill out Inf.Height.Max. /// Min height currently not used. /// 3) PostLayout: Called to position view in cell. /// Not called. bool OnLayout(GViewLayoutInfo &Inf) { return false; } #if defined(_DEBUG) bool _Debug; void Debug(); void _Dump(int Depth = 0); #endif }; /////////////////////////////////////////////////////////////////////////////////////////////// // Control or View window /// FindLargestEdge parameter /// \sa GView::FindLargest(GRegion &, int) #define GV_EDGE_TOP 0x0001 /// FindLargestEdge parameter /// \sa GView::FindLargest(GRegion &, int) #define GV_EDGE_RIGHT 0x0002 /// FindLargestEdge parameter /// \sa GView::FindLargest(GRegion &, int) #define GV_EDGE_BOTTOM 0x0004 /// FindLargestEdge parameter /// \sa GView::FindLargest(GRegion &, int) #define GV_EDGE_LEFT 0x0008 /// Id of the vertical scroll bar in a GLayout control #define IDC_VSCROLL 14000 /// Id of the horizontal scroll bar in a GLayout control #define IDC_HSCROLL 14001 #ifdef MAC #define XPLATFORM_GLAYOUT 1 #else #define XPLATFORM_GLAYOUT 0 #endif /// \brief A GView with scroll bars /// /// This class adds scroll bars to the standard GView base class. The scroll bars can be /// directly accessed using the VScroll and HScroll member variables. Although you should /// always do a NULL check on the pointer before using, if the scroll bar is not activated /// using GLayout::SetScrollBars then VScroll and/or HScroll will by NULL. When the scroll /// bar is used to scroll the GLayout control you will receive an event on GView::OnNotify /// with the control ID of the scrollbar, which is either #IDC_VSCROLL or #IDC_HSCROLL. class LgiClass GLayout : public GView { friend class GScroll; friend class GView; // Private variables bool _SettingScrollBars; bool _PourLargest; protected: /// The vertical scroll bar GScrollBar *VScroll; /// The horizontal scroll bar GScrollBar *HScroll; /// Sets which of the scroll bars is visible virtual bool SetScrollBars ( /// Make the horizontal scroll bar visible bool x, /// Make the vertical scroll bar visible bool y ); #if defined(XPLATFORM_GLAYOUT) void AttachScrollBars(); bool _SetScrollBars(bool x, bool y); #endif #if defined(MAC) && !XPLATFORM_GLAYOUT friend class GLayoutScrollBar; HISize Line; OsView RealWnd; bool _OnGetInfo(HISize &size, HISize &line, HIRect &bounds, HIPoint &origin); void _OnScroll(HIPoint &origin); void OnScrollConfigure(); #endif public: GLayout(); ~GLayout(); const char *GetClass() { return "GLayout"; } /// Gets the current scroll bar values. virtual void GetScrollPos(int &x, int &y); /// Sets the current scroll bar values virtual void SetScrollPos(int x, int y); /// Gets the "pour largest" setting bool GetPourLargest(); /// \brief Sets the "pour largest" setting /// /// When "pour largest" is switched on the pour function automatically /// lays the control into the largest rectangle available. This is useful /// for putting a single GView into a splitter pane or a tab view and having /// it just take up all the space. void SetPourLargest(bool i); /// Handles the incoming events. GMessage::Result OnEvent(GMessage *Msg); /// Lay out all the children views into the client area according to their /// own internal rules. Space is given in a first come first served basis. bool Pour(GRegion &r); // Impl #if defined(__GTK_H__) || !defined(WINNATIVE) bool Attach(GViewI *p); bool Detach(); GRect &GetClient(bool InClientSpace = true); void OnCreate(); #if defined(MAC) && !XPLATFORM_GLAYOUT bool Invalidate(GRect *r = NULL, bool Repaint = false, bool NonClient = false) override; bool Focus() override; void Focus(bool f) override; bool SetPos(GRect &p, bool Repaint = false) override; #else void OnPosChange(); int OnNotify(GViewI *c, int f); void OnNcPaint(GSurface *pDC, GRect &r); #endif #endif GViewI *FindControl(int Id); }; /////////////////////////////////////////////////////////////////////////////////////////////////// // Menus #include "GMenu.h" /////////////////////////////////////////////////////////////////////////////////////////////////// /// The available states for a top level window enum GWindowZoom { /// Minimized GZoomMin, /// Restored/Normal GZoomNormal, /// Maximized GZoomMax }; enum GWindowHookType { GNoEvents = 0, /// \sa GWindow::RegisterHook() GMouseEvents = 1, /// \sa GWindow::RegisterHook() GKeyEvents = 2, /// \sa GWindow::RegisterHook() GKeyAndMouseEvents = GMouseEvents | GKeyEvents, }; /// A top level window. class LgiClass GWindow : public GView, // This needs to be second otherwise is causes v-table problems. #ifndef LGI_SDL virtual #endif public GDragDropTarget { friend class BViewRedir; friend class GView; friend class GButton; friend class XWindow; friend class GDialog; friend class GApp; friend class GWindowPrivate; friend struct GDialogPriv; #if defined(CARBON) friend pascal OSStatus LgiWindowProc(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData); #endif bool _QuitOnClose; protected: class GWindowPrivate *d; #if WINNATIVE GRect OldPos; GWindow *_Dialog; #else OsWindow Wnd; void _OnViewDelete(); void _SetDynamic(bool i); #endif #if defined BEOS friend class GMenu; friend class GView; #elif defined __GTK_H__ friend class GMenu; Gtk::GtkWidget *_Root, *_VBox, *_MenuBar; void _Paint(GSurface *pDC = NULL, GdcPt2 *Offset = NULL, GRegion *Update = NULL); void OnGtkDelete(); Gtk::gboolean OnGtkEvent(Gtk::GtkWidget *widget, Gtk::GdkEvent *event); #endif #if defined(MAC) && !defined(LGI_SDL) void _Delete(); #endif /// The default button GViewI *_Default; /// The menu on the window GMenu *Menu; void SetChildDialog(GDialog *Dlg); void SetDragHandlers(bool On); public: #ifdef __GTK_H__ GWindow(Gtk::GtkWidget *w = 0); #else GWindow(); #endif #if CARBON GWindow(WindowRef wr); #endif ~GWindow(); const char *GetClass() { return "GWindow"; } /// Lays out the child views into the client area. virtual void PourAll(); /// Returns the current menu object GMenu *GetMenu() { return Menu; } /// Set the menu object. void SetMenu(GMenu *m) { Menu = m; } /// Set the window's icon bool SetIcon(const char *FileName); /// Gets the "quit on close" setting. bool GetQuitOnClose() { return _QuitOnClose; } /// \brief Sets the "quit on close" setting. /// /// When this is switched on the application will quit the main message /// loop when this GWindow is closed. This is really useful for your /// main application window. Otherwise the UI will disappear but the /// application is still running. void SetQuitOnClose(bool i) { _QuitOnClose = i; } bool GetSnapToEdge(); void SetSnapToEdge(bool b); bool GetAlwaysOnTop(); void SetAlwaysOnTop(bool b); /// Gets the current zoom setting GWindowZoom GetZoom(); /// Sets the current zoom void SetZoom(GWindowZoom i); /// Raises the window to the top of the stack. void Raise(); /// Moves a top level window on screen. void MoveOnScreen(); /// Moves a top level to the center of the screen void MoveToCenter(); /// Moves a top level window to where the mouse is void MoveToMouse(); /// Moves the window to somewhere on the same screen as 'wnd' bool MoveSameScreen(GViewI *wnd); // Focus setting GViewI *GetFocus(); enum FocusType { GainFocus, LoseFocus, ViewDelete }; void SetFocus(GViewI *ctrl, FocusType type); /// Registers a watcher to receive OnView... messages before they /// are passed through to the intended recipient. bool RegisterHook ( /// The target view. GView *Target, /// Combination of: /// #GMouseEvents - Where Target->OnViewMouse(...) is called for each click. /// and /// #GKeyEvents - Where Target->OnViewKey(...) is called for each key. /// OR'd together. GWindowHookType EventType, /// Not implemented int Priority = 0 ); /// Unregisters a hook target bool UnregisterHook(GView *Target); /// Gets the default view GViewI *GetDefault(); /// Sets the default view void SetDefault(GViewI *v); /// Saves/loads the window's state, e.g. position, minimized/maximized etc bool SerializeState ( /// The data store for reading/writing GDom *Store, /// The field name to use for storing settings under const char *FieldName, /// TRUE if loading the settings into the window, FALSE if saving to the store. bool Load ); /// Builds a map of keyboard short cuts. typedef LHashTbl,GViewI*> ShortcutMap; void BuildShortcuts(ShortcutMap &Map, GViewI *v = NULL); ////////////////////// Events /////////////////////////////// /// Called when the window zoom state changes. virtual void OnZoom(GWindowZoom Action) {} /// Called when the tray icon is clicked. (if present) virtual void OnTrayClick(GMouse &m); /// Called when the tray icon menu is about to be displayed. virtual void OnTrayMenu(GSubMenu &m) {} /// Called when the tray icon menu item has been selected. virtual void OnTrayMenuResult(int MenuId) {} /// Called when files are dropped on the window. virtual void OnReceiveFiles(GArray &Files) {} /// Called when a URL is sent to the window virtual void OnUrl(const char *Url) {}; ///////////////// Implementation //////////////////////////// void OnPosChange(); GMessage::Result OnEvent(GMessage *Msg); void OnPaint(GSurface *pDC); bool HandleViewMouse(GView *v, GMouse &m); bool HandleViewKey(GView *v, GKey &k); bool OnRequestClose(bool OsShuttingDown); bool Obscured(); bool Visible(); void Visible(bool i); bool IsActive(); GRect &GetPos(); // D'n'd int WillAccept(List &Formats, GdcPt2 Pt, int KeyState); int OnDrop(GArray &Data, GdcPt2 Pt, int KeyState); #if !WINNATIVE bool Attach(GViewI *p); // Props OsWindow WindowHandle() { return Wnd; } bool Name(const char *n); char *Name(); bool SetPos(GRect &p, bool Repaint = false); GRect &GetClient(bool InClientSpace = true); // Events void OnChildrenChanged(GViewI *Wnd, bool Attaching); void OnCreate(); virtual void OnFrontSwitch(bool b); #endif #if defined(LGI_SDL) virtual bool PushWindow(GWindow *v); virtual GWindow *PopWindow(); #elif defined(MAC) bool &CloseRequestDone(); bool PostEvent(int Cmd, GMessage::Param a = 0, GMessage::Param b = 0); void Quit(bool DontDelete = false); #ifndef COCOA OSErr HandlerCallback(DragTrackingMessage *tracking, DragRef theDrag); #endif int OnCommand(int Cmd, int Event, OsView Wnd); GViewI *WindowFromPoint(int x, int y, bool Debug = false); #elif defined __GTK_H__ void OnMap(bool m); void Quit(bool DontDelete = false); GRect *GetDecorSize(); #endif }; //////////////////////////////////////////////////////////////////////////// /// Puts a tool tip on screen when the mouse wanders over a region. class LgiClass GToolTip : public GView { class GToolTipPrivate *d; public: GToolTip(); ~GToolTip(); /// Create a tip int NewTip ( /// The text to display char *Name, /// The region the mouse has to be in to trigger the tip GRect &Pos ); /// Delete the tip. void DeleteTip(int Id); bool Attach(GViewI *p); }; //////////////////////////////////////////////////////////////////////////// // Dialog stuff #include "LgiWidgets.h" //////////////////////////////////////////////////////////////////////////// // Progress meters stuff #include "Progress.h" #include "GProgress.h" //////////////////////////////////////////////////////////////////////// #include "GFileSelect.h" #include "GFindReplaceDlg.h" #include "GToolBar.h" #include "LThread.h" //////////////////////////////////////////////////////////////////////////////////////////////// /// Displays 2 views side by side class LgiClass GSplitter : public GLayout { class GSplitterPrivate *d; void CalcRegions(bool Follow = false); bool OverSplit(int x, int y); public: GSplitter(); ~GSplitter(); const char *GetClass() { return "GSplitter"; } /// Get the position of the split in px int64 Value(); // Use to set/get the split position /// Sets the position of the split void Value(int64 i); /// True if the split is vertical bool IsVertical(); /// Sets the split to horizontal or vertical void IsVertical(bool v); /// True if the split follows the opposite bool DoesSplitFollow(); /// Sets the split to follow the opposite void DoesSplitFollow(bool i); /// Return the left/top view GView *GetViewA(); /// Detach the left/top view void DetachViewA(); /// Sets the left/top view void SetViewA(GView *a, bool Border = true); /// Return the right/bottom view GView *GetViewB(); /// Detach the right/bottom view void DetachViewB(); /// Sets the right/bottom view void SetViewB(GView *b, bool Border = true); /// Get the size of the bar that splits the views int BarSize(); /// Set the bar size void BarSize(int i); GViewI *FindControl(OsView hCtrl); bool Attach(GViewI *p); bool Pour(GRegion &r); void OnPaint(GSurface *pDC); void OnPosChange(); void OnMouseClick(GMouse &m); void OnMouseMove(GMouse &m); void OnMouseExit(GMouse &m); int OnHitTest(int x, int y); void OnChildrenChanged(GViewI *Wnd, bool Attaching); LgiCursor GetCursor(int x, int y); }; //////////////////////////////////////////////////////////////////////////////////////////////// #define STATUSBAR_SEPARATOR 4 #define GSP_SUNKEN 0x0001 class LgiClass GStatusBar : public GLayout { friend class GStatusPane; public: GStatusBar(); ~GStatusBar(); const char *GetClass() { return "GStatusBar"; } bool Pour(GRegion &r); void OnPaint(GSurface *pDC); void OnPosChange(); GStatusPane *AppendPane(const char *Text, int Width); bool AppendPane(GStatusPane *Pane); }; class LgiClass GStatusPane : public GView { friend class GStatusBar; protected: int Flags; int Width; GSurface *pDC; public: GStatusPane(); ~GStatusPane(); const char *GetClass() { return "GStatusPane"; } char *Name() { return GBase::Name(); } bool Name(const char *n); void OnPaint(GSurface *pDC); int GetWidth(); void SetWidth(int x); bool Sunken(); void Sunken(bool i); GSurface *Bitmap(); void Bitmap(GSurface *pdc); }; ///////////////////////////////////////////////////////////////////////////////////////////// class LgiClass GCommand : public GBase //, public GFlags { int Flags; bool PrevValue; public: int Id; GToolButton *ToolButton; GMenuItem *MenuItem; GKey *Accelerator; char *TipHelp; GCommand(); ~GCommand(); bool Enabled(); void Enabled(bool e); bool Value(); void Value(bool v); }; ///////////////////////////////////////////////////////////////////////// /// Put an icon in the system tray class LgiClass GTrayIcon : public GBase // public GFlags { friend class GTrayWnd; class GTrayIconPrivate *d; public: /// Constructor GTrayIcon ( /// The owner GWindow GWindow *p ); ~GTrayIcon(); /// Add an icon to the list bool Load(const TCHAR *Str); /// Is it visible? bool Visible(); /// Show / Hide the tray icon void Visible(bool v); /// The index of the icon visible int64 Value(); /// Set the index of the icon you want visible void Value(int64 v); /// Call this in your window's OnEvent handler virtual GMessage::Result OnEvent(GMessage *Msg); }; ////////////////////////////////////////////////////////////////// #include "GInput.h" ////////////////////////////////////////////////////////////////// #ifndef LGI_STATIC /// \brief A BeOS style alert window, kinda like a Win32 MessageBox /// /// The best thing about this class is you can name the buttons very specifically. /// It's always non-intuitive to word a question to the user in such a way so thats /// it's obvious to answer with "Ok" or "Cancel". But if the user gets a question /// with customised "actions" as buttons they'll love you. /// /// The button pressed is returned as a index from the DoModal() function. Starting /// at '1'. i.e. Btn2 -> returns 2. class LgiClass GAlert : public GDialog { public: /// Constructor GAlert ( /// The parent view GViewI *parent, /// The dialog title const char *Title, /// The body of the message const char *Text, /// The first button text const char *Btn1, /// The [optional] 2nd buttons text const char *Btn2 = 0, /// The [optional] 3rd buttons text const char *Btn3 = 0 ); void SetAppModal(); int OnNotify(GViewI *Ctrl, int Flags); }; #endif /// Timer class to help do something every so often class LgiClass DoEvery { int64 LastTime; int64 Period; public: /// Constructor DoEvery ( /// Timeout in ms int p = 1000 ); /// Reset the timer void Init ( /// Timeout in ms int p = -1 ); /// Returns true when the time has expired. Resets automatically. bool DoNow(); }; /// \brief Factory for creating view's by name. /// /// Inherit from this to add a new factory to create objects. Override /// NewView() to create your control. class LgiClass GViewFactory { /** \brief Create a view by name \code if (strcmp(Class, "MyControl") == 0) { return new MyControl; } \endcode */ virtual GView *NewView ( /// The name of the class to create const char *Class, /// The initial position of the view GRect *Pos, /// The initial text of the view const char *Text ) = 0; public: GViewFactory(); virtual ~GViewFactory(); /// Create a view by name. static GView *Create(const char *Class, GRect *Pos = 0, const char *Text = 0); }; ////////////////////////////////////////////////////////// // Colour LgiFunc void LgiInitColours(); LgiFunc COLOUR LgiColour(int Colour); // Graphics LgiFunc void LgiDrawBox(GSurface *pDC, GRect &r, bool Sunken, bool Fill); LgiFunc void LgiWideBorder(GSurface *pDC, GRect &r, LgiEdge Type); LgiFunc void LgiThinBorder(GSurface *pDC, GRect &r, LgiEdge Type); LgiFunc void LgiFlatBorder(GSurface *pDC, GRect &r, int Width = -1); // Helpers #ifdef __GTK_H__ extern Gtk::gboolean GtkViewCallback(Gtk::GtkWidget *widget, Gtk::GdkEvent *event, GView *This); #endif #ifdef LINUX /// Ends a x windows startup session LgiFunc void LgiFinishXWindowsStartup(class GViewI *Wnd); #endif /// \brief Displays a message box /// \returns The button clicked. The return value is one of #IDOK, #IDCANCEL, #IDYES or #IDNO. LgiFunc int LgiMsg ( /// The parent view or NULL if none available GViewI *Parent, /// The message's text. This is a printf format string that you can pass arguments to const char *Msg, /// The title of the message box window const char *Title = 0, /// The type of buttons below the message. Can be one of: /// #MB_OK, #MB_OKCANCEL, #MB_YESNO or #MB_YESNOCANCEL. int Type = MB_OK, ... ); /// Contains all the infomation about a display/monitor attached to the system. /// \sa LgiGetDisplays struct GDisplayInfo { /// The position and dimensions of the display. On windows the left/upper /// most display will be positioned at 0,0 and each furthur display will have /// co-ordinates that join to one edge of that initial rectangle. GRect r; /// The number of bits per pixel int BitDepth; /// The refreash rate int Refresh; /// The device's path, system specific char *Device; /// A descriptive name of the device, usually the video card char *Name; /// The name of any attached monitor char *Monitor; GDisplayInfo() { r.ZOff(-1, -1); BitDepth = 0; Refresh = 0; Device = 0; Name = 0; Monitor = 0; } ~GDisplayInfo() { DeleteArray(Device); DeleteArray(Name); DeleteArray(Monitor); } }; /// Returns infomation about the displays attached to the system. /// \returns non-zero on success. LgiFunc bool LgiGetDisplays ( /// [out] The array of display info structures. The caller should free these /// objects using Displays.DeleteObjects(). GArray &Displays, /// [out] Optional bounding rectangle of all displays. Can be NULL if your don't /// need that information. GRect *AllDisplays = 0 ); /// This class makes it easy to profile a function and write out timings at the end class LgiClass GProfile { struct Sample { uint64 Time; const char *Name; Sample(uint64 t = 0, const char *n = 0) { Time = t; Name = n; } }; GArray s; char *Buf; int Used; int MinMs; public: GProfile(const char *Name, int HideMs = -1); virtual ~GProfile(); void HideResultsIfBelow(int Ms); virtual void Add(const char *Name); virtual void Add(const char *File, int Line); }; // This code will assert if the cast fails. template A &AssertCast(A &a, B b) { a = (A) b; // If this breaks it'll assert. LgiAssert((B)a == b); return a; } #endif diff --git a/include/common/LgiCommon.h b/include/common/LgiCommon.h --- a/include/common/LgiCommon.h +++ b/include/common/LgiCommon.h @@ -1,422 +1,422 @@ /** \file \author Matthew Allen \date 27/3/2000 \brief Common LGI include Copyright (C) 2000-2004, Matthew Allen */ /** * \defgroup Base Foundation tools * \ingroup Lgi */ /** * \defgroup Text Text handling * \ingroup Lgi */ #ifndef _LGI_COMMON_H #define _LGI_COMMON_H #if defined LINUX #include #endif #include "GMem.h" #include "GArray.h" #include "LgiClass.h" #include "GString.h" #include "GStringClass.h" /// Returns the system path specified /// \ingroup Base LgiExtern GString LGetSystemPath( /// Which path to retreive LgiSystemPath Which, int WordSize = 0 ); /// Returns the mime type of the file /// \ingroup Mime LgiExtern GString LGetFileMimeType ( /// File to find mime type for const char *File ); /// Returns the application associated with the mime type /// \ingroup Mime LgiExtern GString LGetAppForMimeType ( /// Type of the file to find and app for const char *Mime ); /// URL encode a string LgiExtern GString LUrlEncode(const char *s, const char *delim); /// URL decode a string LgiExtern GString LUrlDecode(const char *s); /// Gets the current user LgiExtern GString LCurrentUserName(); /// Returns an environment variable. LgiExtern GString LGetEnv(const char *Var); /// Gets the system path.. LgiExtern GString::Array LGetPath(); /// Check for a valid email string LgiExtern bool LIsValidEmail(GString Email); /// Finds an application to handle a protocol request (e.g. 'mailto', 'http' etc) LgiExtern GString LGetAppForProtocol(const char *Protocol); #ifdef __cplusplus extern "C" { #endif ///////////////////////////////////////////////////////////// // Externs // Codepages /// Converts a buffer of text to a different charset /// \ingroup Text /// \returns the bytes written to the location pointed to by 'Out' LgiFunc ssize_t LgiBufConvertCp(void *Out, const char *OutCp, ssize_t OutLen, const void *&In, const char *InCp, ssize_t &InLen); /// \brief Converts a string to a new charset /// \return A dynamically allocate, null terminated string in the new charset /// \ingroup Text LgiFunc void *LgiNewConvertCp ( /// Output charset const char *OutCp, /// Input buffer const void *In, /// The input data's charset const char *InCp, /// Bytes of valid data in the input ssize_t InLen = -1 ); /// Return true if Lgi support the charset /// \ingroup Text LgiFunc bool LgiIsCpImplemented(const char *Cp); /// Converts the ANSI code page to a charset name /// \ingroup Text LgiFunc const char *LgiAnsiToLgiCp(int AnsiCodePage = -1); /// Calculate the number of characters in a string /// \ingroup Text LgiFunc int LgiCharLen(const void *Str, const char *Cp, int Bytes = -1); /// Move a pointer along a utf-8 string by characters /// \ingroup Text LgiFunc char *LgiSeekUtf8 ( /// Pointer to the current character const char *Ptr, /// The number of characters to move forward or back ssize_t D, /// The start of the memory buffer if you known char *Start = 0 ); /// Return true if the string is valid utf-8 /// \ingroup Text LgiFunc bool LgiIsUtf8(const char *s, ssize_t len = -1); /// Converts a string to the native 8bit charset of the OS from utf-8 /// \ingroup Text LgiFunc char *LgiToNativeCp(const char *In, ssize_t InLen = -1); /// Converts a string from the native 8bit charset of the OS to utf-8 /// \ingroup Text LgiFunc char *LgiFromNativeCp(const char *In, ssize_t InLen = -1); /// Map an OS virtual key to an Lgi keycode LgiFunc unsigned LOsKeyToLgi(unsigned k); /// Map a Lgi keycode to a OS virtual key LgiFunc unsigned LLgiToOsKey(unsigned k); /// Returns the next token in a string, leaving the argument pointing to the end of the token /// \ingroup Text LgiFunc char *LgiTokStr(const char *&s); /// Formats a data size into appropriate units /// \ingroup Base LgiFunc void LgiFormatSize ( /// Output string char *Str, /// Output string buffer length int SLen, /// Input size in bytes uint64 Size ); /// \returns true if the path is a volume root. LgiFunc bool LgiIsVolumeRoot(const char *Path); /// Converts a string from URI encoding (ala %20 -> ' ') /// \returns a dynamically allocated string or NULL on error /// \ingroup Text LgiFunc char *LgiDecodeUri ( /// The URI const char *uri, /// The length or -1 if NULL terminated int len = -1 ); /// Converts a string to URI encoding (ala %20 -> ' ') /// \returns a dynamically allocated string or NULL on error /// \ingroup Text LgiFunc char *LgiEncodeUri ( /// The URI const char *uri, /// The length or -1 if NULL terminated int len = -1 ); // Path #ifdef COCOA LgiExtern GString LgiArgsAppPath; #endif /// Gets the path and file name of the currently running executable /// \ingroup Base LgiFunc bool LgiGetExeFile(char *Dst, int DstSize); /// Gets the path of the currently running executable /// \ingroup Base LgiFunc bool LgiGetExePath(char *Dst, int DstSize); /// Gets the path of the temporary file directory /// \ingroup Base LgiFunc bool LgiGetTempPath(char *Dst, int DstSize); /// Returns the system path specified /// \ingroup Base LgiFunc bool LGetSystemPath ( /// Which path to retreive LgiSystemPath Which, /// The buffer to receive the path into char *Dst, /// The size of the receive buffer in bytes int DstSize ); /// Finds a file in the applications directory or nearby /// \ingroup Base LgiFunc char *LgiFindFile(const char *Name); /// Returns 0 to end search /// \ingroup Base typedef bool (*RecursiveFileSearch_Callback)(void *UserData, char *Path, class GDirectory *Dir); /// \brief Recursively search for files /// \return Non zero if something was found /// \ingroup Base LgiFunc bool LgiRecursiveFileSearch ( /// Start search in this dir const char *Root, /// Extensions to match GArray *Ext = NULL, /// [optional] Output filenames GArray *Files = NULL, /// [optional] Output total size uint64 *Size = NULL, /// [optional] File count uint64 *Count = NULL, /// [optional] Callback for match RecursiveFileSearch_Callback Callback = NULL, /// [options] Callback user data void *UserData = NULL ); // Resources /// Gets the currently selected language /// \ingroup Resources LgiFunc struct GLanguage *LgiGetLanguageId(); // Os version functions /// Gets the current operating system and optionally it's version. /// \returns One of the defines starting with #LGI_OS_UNKNOWN in LgiDefs.h /// \ingroup Base LgiFunc int LgiGetOs(GArray *Ver = 0); /// Gets the current operation systems name. /// \ingroup Base LgiFunc const char *LgiGetOsName(); // System /// \brief Opens a file or directory. /// /// If the input is an executable then it is run. If the input file /// is a document then an appropriate application is found to open the /// file and the file is passed to that application. If the input is /// a directory then the OS's file manager is openned to browse the /// directory. /// /// \ingroup Base LgiFunc bool LgiExecute ( /// The file to open const char *File, /// The arguments to pass to the program const char *Arguments="", /// The directory to run in const char *Dir = 0, /// An error message GAutoString *ErrorMsg = NULL ); /// Initializes the random number generator /// \ingroup Base LgiFunc void LgiRandomize(uint Seed); /// Returns a random number between 0 and Max-1 /// \ingroup Base LgiFunc uint LgiRand(uint Max = 0); -LgiFunc bool _lgi_read_colour_config(const char *Tag, uint32 *c); +LgiFunc bool _lgi_read_colour_config(const char *Tag, uint32_t *c); /// Plays a sound /// \ingroup Base LgiFunc bool LgiPlaySound ( /// File name of the sound to play const char *FileName, /// 0 or SND_ASYNC. If 0 the function blocks till the sound finishes. int Flags ); /** * \defgroup Mime Mime handling support. * \ingroup Lgi */ /// Returns the file extensions associated with the mimetype /// \ingroup Mime LgiExtern bool LgiGetMimeTypeExtensions ( /// The returned mime type const char *Mime, /// The extensions GArray &Ext ); inline bool LGetFileMimeType(const char *File, char *MimeType, int BufSize) { GString p = LGetFileMimeType(File); if (MimeType && p) strcpy_s(MimeType, BufSize, p); return p.Length() > 0; } inline bool LGetAppForMimeType(const char *Mime, char *AppPath, int BufSize) { GString p = LGetAppForMimeType(Mime); if (AppPath && p) strcpy_s(AppPath, BufSize, p); return p.Length() > 0; } /// Returns the all applications that can open a given mime type. /// \ingroup Mime LgiFunc bool LgiGetAppsForMimeType ( /// The type of files to match apps to. /// /// Two special cases exist: /// - application/email gets the default email client /// - application/browser get the default web browser const char *Mime, /// The applications that can handle the GArray &Apps, /// Limit the length of the results, i.e. stop looking after 'Limit' matches. /// -1 means return all matches. int Limit = -1 ); /// Gets the current clock in milli-seconds. (1,000th of a second) /// \ingroup Time LgiFunc uint64 LgiCurrentTime(); /// Get the current clock in micro-seconds (1,000,000th of a second) LgiFunc uint64 LgiMicroTime(); // Debug /// Returns true if the build is for release. /// \ingroup Base LgiFunc int LgiIsReleaseBuild(); #if defined WIN32 /// Registers an active x control LgiFunc bool RegisterActiveXControl(const char *Dll); enum HWBRK_TYPE { HWBRK_TYPE_CODE, HWBRK_TYPE_READWRITE, HWBRK_TYPE_WRITE, }; enum HWBRK_SIZE { HWBRK_SIZE_1, HWBRK_SIZE_2, HWBRK_SIZE_4, HWBRK_SIZE_8, }; /// Set a hardware breakpoint. LgiFunc HANDLE SetHardwareBreakpoint ( /// Use GetCurrentThread() HANDLE hThread, /// Type of breakpoint HWBRK_TYPE Type, /// Size of breakpoint HWBRK_SIZE Size, /// The pointer to the data to break on void *s ); /// Deletes a hardware breakpoint LgiFunc bool RemoveHardwareBreakpoint(HANDLE hBrk); #elif defined LINUX /// Window managers enum WindowManager { WM_Unknown, WM_Kde, WM_Gnome }; /// Returns the currently running window manager WindowManager LgiGetWindowManager(); #endif #ifdef __cplusplus } #endif #endif diff --git a/include/common/LgiDefs.h b/include/common/LgiDefs.h --- a/include/common/LgiDefs.h +++ b/include/common/LgiDefs.h @@ -1,642 +1,622 @@ /** \file \author Matthew Allen \date 24/9/1999 \brief Defines and types Copyright (C) 1999-2004, Matthew Allen */ #ifndef _LGIDEFS_H_ #define _LGIDEFS_H_ #include "LgiInc.h" #if defined(WIN32) && defined(__GNUC__) #define PLATFORM_MINGW #endif +#include + // Unsafe typedefs, for backward compatibility typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned int uint; typedef unsigned long ulong; // Length safe typedesf, use these in new code - -#ifndef BEOS - /// 8-bit signed int type (size safe, guaranteed to be 8 bits) - typedef signed char int8; - /// 8-bit unsigned int type (size safe, guaranteed to be 8 bits) - typedef unsigned char uint8; -#else - #include -#endif - -/// 16-bit signed int type (size safe, guaranteed to be 16 bits) -typedef short int16; -/// 16-bit unsigned int type (size safe, guaranteed to be 16 bits) -typedef unsigned short uint16; - -#ifndef BEOS - /// 32-bit signed int type (size safe, guaranteed to be 32 bits) - typedef int int32; - /// 32-bit unsigned int type (size safe, guaranteed to be 32 bits) - typedef unsigned int uint32; -#endif - #ifdef _MSC_VER /// 64-bit signed int type (size safe, guaranteed to be 64 bits) typedef signed __int64 int64; /// 64-bit unsigned int type (size safe, guaranteed to be 64 bits) typedef unsigned __int64 uint64; #ifndef _WCHAR_T_DEFINED #include #endif #pragma warning(error:4263) #ifdef _WIN64 typedef signed __int64 ssize_t; #else typedef signed int ssize_t; #endif #else /// 64-bit signed int type (size safe, guaranteed to be 64 bits) typedef signed long long int64; /// 64-bit unsigned int type (size safe, guaranteed to be 64 bits) typedef unsigned long long uint64; #endif #ifndef __cplusplus #include #if defined(_MSC_VER) && _MSC_VER<1800 typedef unsigned char bool; #define true 1 #define false 0 #else #include #endif #endif /// \brief Wide unicode char /// /// This is 16 bits on Win32 and Mac, but 32 bits on unix platforms. There are a number /// of wide character string function available for manipulating wide char strings. /// /// Firstly to convert to and from utf-8 there is: ///
      ///
    • Utf8ToWide() ///
    • WideToUtf8() ///
    /// /// Wide versions of standard library functions are available: ///
      ///
    • StrchrW() ///
    • StrrchrW() ///
    • StrnchrW() ///
    • StrstrW() ///
    • StristrW() ///
    • StrnstrW() ///
    • StrnistrW() ///
    • StrcmpW() ///
    • StricmpW() ///
    • StrncmpW() ///
    • StrnicmpW() ///
    • StrcpyW() ///
    • StrncpyW() ///
    • StrlenW() ///
    • StrcatW() ///
    • HtoiW() ///
    • NewStrW() ///
    • TrimStrW() ///
    • ValidStrW() ///
    • MatchStrW() ///
    #include typedef wchar_t char16; #if !WINNATIVE #ifdef UNICODE typedef char16 TCHAR; #ifndef _T #define _T(arg) L##arg #endif #else typedef char TCHAR; #ifndef _T #define _T(arg) arg #endif #endif #endif #if defined(_MSC_VER) #if _MSC_VER >= 1400 #ifdef _WIN64 typedef __int64 NativeInt; typedef unsigned __int64 UNativeInt; #else typedef _W64 int NativeInt; typedef _W64 unsigned int UNativeInt; #endif #else typedef int NativeInt; typedef unsigned int UNativeInt; #endif #else #if __LP64__ typedef int64 NativeInt; typedef uint64 UNativeInt; #else typedef int NativeInt; typedef unsigned int UNativeInt; #endif #endif /// Generic pointer to any base type. Used when addressing continuous data of /// different types. typedef union { - int8 *s8; - uint8 *u8; - int16 *s16; - uint16 *u16; - int32 *s32; - uint32 *u32; + int8_t *s8; + uint8_t *u8; + int16_t *s16; + uint16_t *u16; + int32_t *s32; + uint32_t *u32; int64 *s64; uint64 *u64; NativeInt *ni; UNativeInt *uni; char *c; char16 *w; float *f; double *d; #ifdef __cplusplus bool *b; #else unsigned char *b; #endif void **vp; int i; } GPointer; // Basic macros #define limit(i,l,u) (((i)<(l)) ? (l) : (((i)>(u)) ? (u) : (i))) #define makelong(a, b) ((a)<<16 | (b&0xFFFF)) #define loword(a) (a&0xFFFF) #define hiword(a) (a>>16) #define LgiSwap(a, b) { int n = a; a = b; b = n; } #undef ABS #ifdef __cplusplus template inline T ABS(T v) { if (v < 0) return -v; return v; } #else #define ABS(v) ((v) < 0 ? -(v) : (v)) #endif /// Returns true if 'c' is an ascii character #define IsAlpha(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) /// Returns true if 'c' is a digit (number) #define IsDigit(c) ((c) >= '0' && (c) <= '9') /// Returns true if 'c' is a hexadecimal digit #define IsHexDigit(c) ( \ ((c) >= '0' && (c) <= '9') || \ ((c) >= 'a' && (c) <= 'f') || \ ((c) >= 'A' && (c) <= 'F') \ ) // Byte swapping #define LgiSwap16(a) ( (((a) & 0xff00) >> 8) | \ (((a) & 0x00ff) << 8) ) #define LgiSwap32(a) ( (((a) & 0xff000000) >> 24) | \ (((a) & 0x00ff0000) >> 8) | \ (((a) & 0x0000ff00) << 8) | \ (((a) & 0x000000ff) << 24) ) #ifdef __GNUC__ #define LgiSwap64(a) ( (((a) & 0xff00000000000000LLU) >> 56) | \ (((a) & 0x00ff000000000000LLU) >> 40) | \ (((a) & 0x0000ff0000000000LLU) >> 24) | \ (((a) & 0x000000ff00000000LLU) >> 8) | \ (((a) & 0x00000000ff000000LLU) << 8) | \ (((a) & 0x0000000000ff0000LLU) << 24) | \ (((a) & 0x000000000000ff00LLU) << 40) | \ (((a) & 0x00000000000000ffLLU) << 56) ) #else #define LgiSwap64(a) ( (((a) & 0xff00000000000000) >> 56) | \ (((a) & 0x00ff000000000000) >> 40) | \ (((a) & 0x0000ff0000000000) >> 24) | \ (((a) & 0x000000ff00000000) >> 8) | \ (((a) & 0x00000000ff000000) << 8) | \ (((a) & 0x0000000000ff0000) << 24) | \ (((a) & 0x000000000000ff00) << 40) | \ (((a) & 0x00000000000000ff) << 56) ) #endif // Asserts LgiFunc void _lgi_assert(bool b, const char *test, const char *file, int line); #ifdef _DEBUG #define LgiAssert(b) _lgi_assert(b, #b, __FILE__, __LINE__) #else #define LgiAssert(b) while (0) #endif // Good ol NULLy #ifndef NULL #define NULL 0 #endif // Slashes and quotes #define IsSlash(c) (((c)=='/')||((c)=='\\')) #define IsQuote(c) (((c)=='\"')||((c)=='\'')) // Some objectish ones #define ZeroObj(obj) memset(&obj, 0, sizeof(obj)) #ifndef CountOf #define CountOf(array) (sizeof(array)/sizeof(array[0])) #endif #ifndef MEMORY_DEBUG #define DeleteObj(obj) if (obj) { delete obj; obj = 0; } #define DeleteArray(obj) if (obj) { delete [] obj; obj = 0; } #endif // Flags #define SetFlag(i, f) (i) |= (f) #define ClearFlag(i, f) (i) &= ~(f) #define TestFlag(i, f) (((i) & (f)) != 0) // Defines /// Enum of all the operating systems we might be running on. enum LgiOs { /// \brief Unknown OS /// \sa LgiGetOs LGI_OS_UNKNOWN = 0, /// \brief Windows 95, 98[se] or ME. (Not supported) /// \sa LgiGetOs LGI_OS_WIN9X, /// \brief 32bit NT, 2k, XP, Vista, 7, 8 or later. (XP and later supported) /// \sa LgiGetOs LGI_OS_WIN32, /// \brief 64bit NT, 2k, XP, Vista, 7, 8 or later. (XP and later supported) /// \sa LgiGetOs LGI_OS_WIN64, /// \brief BeOS/Haiku. (Somewhat supported) /// \sa LgiGetOs LGI_OS_HAIKU, /// \brief Linux. (Kernels v2.4 and up supported) /// \sa LgiGetOs LGI_OS_LINUX, /// \brief There was an Atheos port at one point. (Not supported) /// \sa LgiGetOs LGI_OS_MAC_OS_X, /// One higher than the maximum OS define LGI_OS_MAX, }; // System Colours /// Black #define LC_BLACK LgiColour(0) /// Dark grey #define LC_DKGREY LgiColour(1) /// Medium grey #define LC_MIDGREY LgiColour(2) /// Light grey #define LC_LTGREY LgiColour(3) /// White #define LC_WHITE LgiColour(4) /// 3d dark shadow #define LC_SHADOW LgiColour(5) /// 3d light shadow #define LC_LOW LgiColour(6) /// Flat colour for dialogs, windows and buttons #define LC_MED LgiColour(7) /// 3d dark hilight #define LC_HIGH LgiColour(8) /// 3d light hilight #define LC_LIGHT LgiColour(9) /// Dialog colour #define LC_DIALOG LgiColour(10) /// Workspace area #define LC_WORKSPACE LgiColour(11) /// Default text colour #define LC_TEXT LgiColour(12) /// Selection background colour when in focus #define LC_FOCUS_SEL_BACK LgiColour(13) /// Selection foreground colour when in focus #define LC_FOCUS_SEL_FORE LgiColour(14) #define LC_ACTIVE_TITLE LgiColour(15) #define LC_ACTIVE_TITLE_TEXT LgiColour(16) #define LC_INACTIVE_TITLE LgiColour(17) #define LC_INACTIVE_TITLE_TEXT LgiColour(18) #define LC_MENU_BACKGROUND LgiColour(19) #define LC_MENU_TEXT LgiColour(20) /// Selection background colour when not in focus #define LC_NON_FOCUS_SEL_BACK LgiColour(21) /// Selection forground colour when not in focus #define LC_NON_FOCUS_SEL_FORE LgiColour(22) #define LC_DEBUG_CURRENT_LINE LgiColour(23) #define LC_MAXIMUM 24 #define LC_TOOL_TIP Rgb24(255, 255, 231) // Edge types enum LgiEdge { EdgeNone, EdgeXpSunken, EdgeXpRaised, EdgeXpChisel, EdgeXpFlat, EdgeWin7FocusSunken, EdgeWin7Sunken, }; #define DefaultSunkenEdge EdgeWin7Sunken #define DefaultRaisedEdge EdgeXpRaised // Cursors enum LgiCursor { /// Blank/invisible cursor LCUR_Blank, /// Normal arrow LCUR_Normal, /// Upwards arrow LCUR_UpArrow, /// Downwards arrow LCUR_DownArrow, /// Left arrow LCUR_LeftArrow, /// Right arrow LCUR_RightArrow, /// Crosshair LCUR_Cross, /// Hourglass/watch LCUR_Wait, /// Ibeam/text entry LCUR_Ibeam, /// Vertical resize (|) LCUR_SizeVer, /// Horizontal resize (-) LCUR_SizeHor, /// Diagonal resize (/) LCUR_SizeBDiag, /// Diagonal resize (\) LCUR_SizeFDiag, /// All directions resize LCUR_SizeAll, /// Vertical splitting LCUR_SplitV, /// Horziontal splitting LCUR_SplitH, /// A pointing hand LCUR_PointingHand, /// A slashed circle LCUR_Forbidden, /// Copy Drop LCUR_DropCopy, /// Copy Move LCUR_DropMove, }; // General Event Flags #define LGI_EF_LCTRL 0x00000001 #define LGI_EF_RCTRL 0x00000002 #define LGI_EF_CTRL (LGI_EF_LCTRL | LGI_EF_RCTRL) #define LGI_EF_LALT 0x00000004 #define LGI_EF_RALT 0x00000008 #define LGI_EF_ALT (LGI_EF_LALT | LGI_EF_RALT) #define LGI_EF_LSHIFT 0x00000010 #define LGI_EF_RSHIFT 0x00000020 #define LGI_EF_SHIFT (LGI_EF_LSHIFT | LGI_EF_RSHIFT) #define LGI_EF_DOWN 0x00000040 #define LGI_EF_DOUBLE 0x00000080 #define LGI_EF_CAPS_LOCK 0x00000100 #define LGI_EF_IS_CHAR 0x00000200 #define LGI_EF_IS_NOT_CHAR 0x00000400 #define LGI_EF_SYSTEM 0x00000800 // Windows key/Apple key etc // Mouse Event Flags #define LGI_EF_LEFT 0x00001000 #define LGI_EF_MIDDLE 0x00002000 #define LGI_EF_RIGHT 0x00004000 #define LGI_EF_MOVE 0x00008000 // Emit compiler warnings #define __STR2__(x) #x #define __STR1__(x) __STR2__(x) #define __LOC__ __FILE__ "("__STR1__(__LINE__)") : Warning: " // To use just do #pragma message(__LOC__"My warning message") // Simple definition of breakable unicode characters #define LGI_BreakableChar(c) ( \ (c) == '\n' || \ (c) == ' ' || \ (c) == '\t' || \ ( (c) >= 0x3040 && (c) <= 0x30FF ) || \ ( (c) >= 0x3300 && (c) <= 0x9FAF ) \ ) // Os metrics enum LgiSystemMetric { /// Get the standard window horizontal border size /// \sa GApp::GetMetric() LGI_MET_DECOR_X = 1, /// Get the standard window vertical border size including caption bar. /// \sa GApp::GetMetric() LGI_MET_DECOR_Y, /// Get the standard window caption bar height only. /// \sa GApp::GetMetric() LGI_MET_DECOR_CAPTION, /// Get the height of a single line menu bar /// \sa GApp::GetMetric() LGI_MET_MENU, /// This is non-zero if the system is theme aware LGI_MET_THEME_AWARE }; /// \brief Types of system paths available for querying /// \sa LgiGetSystemPath enum LgiSystemPath { LSP_ROOT, /// The location of the operating system folder /// [Win32] = e.g. C:\Windows /// [Mac] = /System /// [Linux] /boot LSP_OS, /// The system library folder /// [Win32] = e.g. C:\Windows\System32 /// [Mac] = /Library /// [Linux] = /usr/lib LSP_OS_LIB, /// A folder for storing temporary files. These files are usually /// deleted automatically later by the system. /// [Win32] = ~\Local Settings\Temp /// [Mac] = ~/Library/Caches/TemporaryItems /// [Linux] = /tmp LSP_TEMP, /// System wide application data /// [Win32] = ~\..\All Users\Application Data /// [Mac] = /System/Library /// [Linux] = /usr LSP_COMMON_APP_DATA, /// User specific application data /// [Win32] = ~\Application Data /// [Mac] = ~/Library /// [Linux] = /usr LSP_USER_APP_DATA, /// Machine + user specific application data (probably should not use) /// [Win32] = ~\Local Settings\Application Data /// [Mac] = ~/Library /// [Linux] = /usr/local LSP_LOCAL_APP_DATA, /// Desktop dir /// i.e. ~/Desktop LSP_DESKTOP, /// Home dir /// i.e. ~ LSP_HOME, /// Application install folder: /// [Win] c:\Program Files /// [Mac] /Applications /// [Linux] /usr/bin LSP_USER_APPS, /// The running application's path. /// [Mac] This doesn't include the "Contents/MacOS" part of the path inside the bundle. LSP_EXE, /// The system trash folder. LSP_TRASH, /// The app's install folder. /// [Win32] = $ExePath (sans '\Release' or '\Debug') /// [Mac/Linux] = $ExePath /// Where $ExePath is the folder the application is running from LSP_APP_INSTALL, /// The app's root folder (Where config and data should be stored) /// GOptionsFile uses LSP_APP_ROOT as the default location. /// [Win32] = ~\Application Data\Roaming\$AppName /// [Mac] = ~/Library/$AppName /// [Linux] = ~/.$AppName /// Where $AppName = GApp::GetName. /// If the given folder doesn't exist it will be created. LSP_APP_ROOT, /// This is the user's documents folder /// [Win32] ~\My Documents /// [Mac] ~\Documents LSP_USER_DOCUMENTS, /// This is the user's music folder /// [Win32] ~\My Music /// [Mac] ~\Music LSP_USER_MUSIC, /// This is the user's video folder /// [Win32] ~\My Videos /// [Mac] ~\Movies LSP_USER_VIDEO, /// This is the user's download folder /// ~\Downloads LSP_USER_DOWNLOADS, /// This is the user's links folder /// [Win32] = ~\Links /// [Mac] = ??? /// [Linux] = ??? LSP_USER_LINKS, /// User's pictures/photos folder LSP_USER_PICTURES, /// [Win32] = C:\Users\%HOME%\Pictures /// [Mac] = ??? /// [Linux] = ~\Pictures }; // Deprecated method defines #if defined(__GNUC__) || defined(__clang__) #define DEPRECATED_PRE #define DEPRECATED_POST __attribute__((deprecated)) #elif defined(_MSC_VER) #define DEPRECATED_PRE __declspec(deprecated) #define DEPRECATED_POST #else #pragma message("WARNING: You need to implement DEPRECATED for this compiler") #define DEPRECATED_PRE #define DEPRECATED_POST #endif // #ifdef _DEBUG #define DeclDebugArgs , const char *_file, int _line #define PassDebugArgs , __FILE__, __LINE__ #else #define DeclDebugArgs #define PassDebugArgs #endif #define _FL __FILE__, __LINE__ #define CALL_MEMBER_FN(obj, memFn) ((obj).*(memFn)) #include "GAutoPtr.h" #endif diff --git a/include/common/LgiInterfaces.h b/include/common/LgiInterfaces.h --- a/include/common/LgiInterfaces.h +++ b/include/common/LgiInterfaces.h @@ -1,544 +1,544 @@ // \file /// \author Matthew Allen #ifndef _LGI_INTERFACES_H_ #define _LGI_INTERFACES_H_ // Includes #include "GMem.h" #include "GArray.h" #include "LgiOsDefs.h" #include "GColour.h" #include "LCancel.h" #include "GStringClass.h" // Fwd defs class GXmlTag; class GMouseHook; class GFont; class GRect; class GdcPt2; class GRegion; class GSurface; class GViewI; #ifndef BEOS class GMessage; #endif class GMouse; class GKey; class GWindow; class GViewFill; class GView; class GVariant; class GCss; // Classes class GDomI { public: virtual ~GDomI() {} virtual bool GetValue(const char *Var, GVariant &Value) { return false; } virtual bool SetValue(const char *Var, GVariant &Value) { return false; } virtual bool CallMethod(const char *MethodName, GVariant *ReturnValue, GArray &Args) { return false; } }; /// Stream interface class /// /// Defines the API /// for all the streaming data classes. Allows applications to plug /// different types of date streams into functions that take a GStream. /// Typically this means being able to swap files with sockets or data /// buffers etc. /// class LgiClass GStreamI : virtual public GDomI { public: /// Open a connection /// \returns > zero on success virtual int Open ( /// A string connection parameter const char *Str = 0, /// An integer connection parameter int Int = 0 ) { return false; } /// Returns true is the stream is still open virtual bool IsOpen() { return false; } /// Closes the connection /// \returns > zero on success virtual int Close() { return 0; } /// \brief Gets the size of the stream /// \return The size or -1 on error (e.g. the information is not available) virtual int64 GetSize() { return -1; } /// \brief Sets the size of the stream /// \return The new size or -1 on error (e.g. the size is not set-able) virtual int64 SetSize(int64 Size) { return -1; } /// \brief Gets the current position of the stream /// \return Current position or -1 on error (e.g. the position is not known) virtual int64 GetPos() { return -1; } /// \brief Sets the current position of the stream /// \return The new current position or -1 on error (e.g. the position can't be set) virtual int64 SetPos(int64 Pos) { return -1; } /// \brief Read bytes out of the stream /// \return > 0 on succes, which indicates the number of bytes read virtual ssize_t Read(void *Buffer, ssize_t Size, int Flags = 0) = 0; /// \brief Write bytes to the stream /// \return > 0 on succes, which indicates the number of bytes written virtual ssize_t Write(const void *Buffer, ssize_t Size, int Flags = 0) = 0; /// \brief Creates a dynamically allocated copy of the same type of stream. /// This new stream is not connected to anything. /// \return The new stream or NULL on error. virtual GStreamI *Clone() { return 0; } }; /// Socket logging types.. enum GSocketLogTypes { /// Do no logging NET_LOG_NONE = 0, /// Log a hex dump of everything NET_LOG_HEX_DUMP = 1, /// Log just the bytes NET_LOG_ALL_BYTES = 2 }; /// Virtual base class for a socket. See the documentation for GSocket for a more /// through treatment of this object's API. class GSocketI : virtual public GStreamI { public: enum SocketMsgType { SocketMsgNone, SocketMsgInfo, SocketMsgSend, SocketMsgReceive, SocketMsgWarning, SocketMsgError, }; virtual ~GSocketI() {} /// Returns the actual socket (as defined by the OS) virtual OsSocket Handle(OsSocket Set = INVALID_SOCKET) { return INVALID_SOCKET; } // Cancel virtual LCancel *GetCancel() { return NULL; } virtual void SetCancel(LCancel *c) { } // Host/Port meta data /// Returns the IP at this end of the socket virtual bool GetLocalIp ( /// Ptr to a buffer of at least 16 bytes char *IpAddr ) { return false; } /// Return the port at this end of the connection virtual int GetLocalPort() { return 0; } /// Gets the remote IP virtual bool GetRemoteIp(char *IpAddr) { return false; } /// Return the port at this end of the connection virtual int GetRemotePort() { return 0; } // Timeout /// Gets the current timeout for operations in ms virtual int GetTimeout() { return -1; } /// Sets the current timeout for operations in ms virtual void SetTimeout(int ms) {} /// Sets the continue token // virtual void SetContinue(bool *Token) {} // State /// True if there is data available to read. virtual bool IsReadable(int TimeoutMs = 0) { return false; } /// True if the socket can be written to. virtual bool IsWritable(int TimeoutMs = 0) { return false; } /// True if the socket can be accept. virtual bool CanAccept(int TimeoutMs = 0) { return false; } /// Returns whether the socket is set to blocking or not virtual bool IsBlocking() { return true; } /// Set whether the socket should block or not virtual void IsBlocking(bool block) {} /// Get the send delay setting virtual bool IsDelayed() { return true; } /// Set the send delay setting virtual void IsDelayed(bool Delay) {} // UDP /// Get UPD mode virtual bool GetUdp() { return false; } /// Set UPD mode virtual void SetUdp(bool b) {} /// Read UPD packet - virtual int ReadUdp(void *Buffer, int Size, int Flags, uint32 *Ip = 0, uint16 *Port = 0) { return 0; } + virtual int ReadUdp(void *Buffer, int Size, int Flags, uint32_t *Ip = 0, uint16_t *Port = 0) { return 0; } /// Write UPD packet - virtual int WriteUdp(void *Buffer, int Size, int Flags, uint32 Ip, uint16 Port) { return 0; } + virtual int WriteUdp(void *Buffer, int Size, int Flags, uint32_t Ip, uint16_t Port) { return 0; } // Server /// Listens on a given port for an incoming connection. virtual bool Listen(int Port = 0) { return false; } /// Accepts an incoming connection and connects the socket you pass in to the remote host. virtual bool Accept(GSocketI *c) { return false; } // Event call backs /// Called when the connection is dropped virtual void OnDisconnect() {} /// Called when data is read virtual void OnRead(char *Data, ssize_t Len) {} /// Called when data is written virtual void OnWrite(const char *Data, ssize_t Len) {} /// Called when an error occurs virtual void OnError(int ErrorCode, const char *ErrorDescription) {} /// Called when some events happens virtual void OnInformation(const char *Str) {} /// Process an error virtual int Error(void *Param) { return 0; } virtual const char *GetErrorString() { return NULL; } }; class GAppI { public: /// The idle function should return false to wait for more /// messages, otherwise it will be called continuously when no /// messages are available. typedef bool (*OnIdleProc)(void *Param); /// Destroys the object virtual ~GAppI() {} virtual bool IsOk() = 0; /// Returns this processes ID virtual OsProcessId GetProcessId() = 0; /// Returns the thread currently running the active message loop virtual OsThreadId GetGuiThreadId() = 0; virtual bool InThread() = 0; /// Resets the arguments virtual void SetAppArgs(OsAppArguments &AppArgs) = 0; /// Returns the arguemnts virtual OsAppArguments *GetAppArgs() = 0; /// Returns the n'th argument as a heap string. Free with DeleteArray(...). virtual const char *GetArgumentAt(int n) = 0; /// Enters the message loop. virtual bool Run ( /// If true this function will return when the application exits (with LgiCloseApp()). /// Otherwise if false only pending events will be processed and then the function returns. bool Loop = true, /// [Optional] Idle callback OnIdleProc IdleCallback = 0, /// [Optional] User param for IdleCallback void *IdleParam = 0 ) = 0; /// Event called to process the command line virtual void OnCommandLine() = 0; /// Event called to process files dropped on the application virtual void OnReceiveFiles(GArray &Files) = 0; /// Event called to process URLs given to the application virtual void OnUrl(const char *Url) = 0; /// Exits the event loop with the code specified virtual void Exit ( /// The application exit code. int Code = 0 ) = 0; /// \brief Parses the command line for a switch /// \return true if the option exists. virtual bool GetOption ( /// The option to look for. const char *Option, /// String to receive the value (if any) of the option GString &Value ) = 0; /// \brief Parses the command line for a switch /// \return true if the option exists. virtual bool GetOption ( /// The option to look for. const char *Option, /// The buffer to receive the value of the command line parameter or NULL if you don't care. char *Dst = 0, /// The buffer size in bytes int DstSize = 0 ) = 0; /// Gets the application conf stored in lgi.conf virtual GXmlTag *GetConfig(const char *Tag) = 0; /// Sets a single tag in the config. (Not written to disk) virtual void SetConfig(GXmlTag *Tag) = 0; /// Gets the control with the keyboard focus virtual GViewI *GetFocus() = 0; /// Gets the MIME type of a file virtual GAutoString GetFileMimeType ( /// The file to identify const char *File ) = 0; /// Get a system metric virtual int32 GetMetric ( /// One of #LGI_MET_DECOR_X, #LGI_MET_DECOR_Y LgiSystemMetric Metric ) = 0; /// Get the mouse hook instance virtual GMouseHook *GetMouseHook() = 0; /// Returns the number of cpu cores or -1 if unknown. virtual int GetCpuCount() { return -1; } /// Gets the font cache virtual class GFontCache *GetFontCache() = 0; }; class GEventSinkI { public: virtual ~GEventSinkI() {} virtual bool PostEvent(int Cmd, GMessage::Param a = 0, GMessage::Param b = 0) = 0; }; class GEventTargetI { public: virtual ~GEventTargetI() {} virtual GMessage::Result OnEvent(GMessage *Msg) = 0; }; class GEventsI : public GEventTargetI { public: virtual ~GEventsI() {} // Events virtual void OnMouseClick(GMouse &m) = 0; virtual void OnMouseEnter(GMouse &m) = 0; virtual void OnMouseExit(GMouse &m) = 0; virtual void OnMouseMove(GMouse &m) = 0; virtual bool OnMouseWheel(double Lines) = 0; virtual bool OnKey(GKey &k) = 0; virtual void OnAttach() = 0; virtual void OnCreate() = 0; virtual void OnDestroy() = 0; virtual void OnFocus(bool f) = 0; virtual void OnPulse() = 0; virtual void OnPosChange() = 0; virtual bool OnRequestClose(bool OsShuttingDown) = 0; virtual int OnHitTest(int x, int y) = 0; virtual void OnChildrenChanged(GViewI *Wnd, bool Attaching) = 0; virtual void OnPaint(GSurface *pDC) = 0; virtual int OnNotify(GViewI *Ctrl, int Flags) = 0; virtual int OnCommand(int Cmd, int Event, OsView Wnd) = 0; }; class GViewIterator { public: virtual ~GViewIterator() {} virtual GViewI *First() = 0; virtual GViewI *Last() = 0; virtual GViewI *Next() = 0; virtual GViewI *Prev() = 0; virtual size_t Length() = 0; virtual ssize_t IndexOf(GViewI *view) = 0; virtual GViewI *operator [](ssize_t Idx) = 0; }; class GViewLayoutInfo { public: struct Range { // 0 if unknown, -1 for "all available" int32 Min, Max; Range() { Min = Max = 0; } }; Range Width; Range Height; }; class LgiClass GViewI : public GEventsI, public GEventSinkI, public virtual GDomI { friend class GView; public: // Handles virtual OsView Handle() = 0; virtual OsWindow WindowHandle() = 0; virtual GView *GetGView() { return NULL; } // Heirarchy virtual bool Attach(GViewI *p) = 0; virtual bool AttachChildren() = 0; virtual bool Detach() = 0; virtual bool IsAttached() = 0; virtual GWindow *GetWindow() = 0; virtual GViewI *GetParent() = 0; virtual void SetParent(GViewI *p) = 0; virtual void Quit(bool DontDelete = false) = 0; virtual bool AddView(GViewI *v, int Where = -1) = 0; virtual bool DelView(GViewI *v) = 0; virtual bool HasView(GViewI *v) = 0; virtual GViewIterator *IterateViews() = 0; // Threading virtual bool Lock(const char *file, int line, int TimeOut = -1) = 0; virtual void Unlock() = 0; virtual bool InThread() = 0; // Properties virtual bool Enabled() = 0; virtual void Enabled(bool e) = 0; virtual bool Visible() = 0; virtual void Visible(bool v) = 0; virtual bool Focus() = 0; virtual void Focus(bool f) = 0; virtual class GDragDropSource *DropSource(GDragDropSource *Set = NULL) = 0; virtual class GDragDropTarget *DropTarget(GDragDropTarget *Set = NULL) = 0; virtual bool DropTarget(bool t) = 0; virtual bool Sunken() = 0; virtual void Sunken(bool i) = 0; virtual bool Flat() = 0; virtual void Flat(bool i) = 0; virtual bool Raised() = 0; virtual void Raised(bool i) = 0; virtual bool GetTabStop() = 0; virtual void SetTabStop(bool b) = 0; // Style virtual GCss *GetCss(bool Create = false) = 0; virtual void SetCss(GCss *css) = 0; virtual bool SetCssStyle(const char *CssStyle) = 0; virtual bool SetColour(GColour &c, bool Fore) = 0; virtual GFont *GetFont() = 0; virtual void SetFont(GFont *Fnt, bool OwnIt = false) = 0; // Name and value virtual bool Name(const char *n) = 0; virtual bool NameW(const char16 *n) = 0; virtual char *Name() = 0; virtual char16 *NameW() = 0; virtual int64 Value() = 0; virtual void Value(int64 i) = 0; virtual const char *GetClass() { return "GViewI"; } // mainly for debugging virtual GString::Array *CssClasses() { return NULL; } // Size and position virtual GRect &GetPos() = 0; virtual GRect &GetClient(bool InClientSpace = true) = 0; virtual bool SetPos(GRect &p, bool Repaint = false) = 0; virtual int X() = 0; virtual int Y() = 0; virtual GdcPt2 GetMinimumSize() = 0; virtual void SetMinimumSize(GdcPt2 Size) = 0; // Id virtual int GetId() = 0; virtual void SetId(int i) = 0; // Events and notification virtual void SendNotify(int Data = 0) = 0; virtual GViewI *GetNotify() = 0; virtual void SetNotify(GViewI *n) = 0; // Mouse virtual LgiCursor GetCursor(int x, int y) = 0; virtual bool Capture(bool c) = 0; virtual bool IsCapturing() = 0; virtual bool GetMouse(GMouse &m, bool ScreenCoords = false) = 0; // Helper virtual GViewI *FindControl(OsView hnd) = 0; virtual GViewI *FindControl(int Id) = 0; virtual int64 GetCtrlValue(int Id) = 0; virtual void SetCtrlValue(int Id, int64 i) = 0; virtual char *GetCtrlName(int Id) = 0; virtual void SetCtrlName(int Id, const char *s) = 0; virtual bool GetCtrlEnabled(int Id) = 0; virtual void SetCtrlEnabled(int Id, bool Enabled) = 0; virtual bool GetCtrlVisible(int Id) = 0; virtual void SetCtrlVisible(int Id, bool Visible) = 0; virtual bool Pour(GRegion &r) = 0; template bool GetViewById(int Id, T *&Ptr) { GViewI *Ctrl = FindControl(Id); Ptr = dynamic_cast(Ctrl); if (Ctrl != NULL && Ptr == NULL) LgiTrace("%s:%i - Can't cast '%s' to target type.\n", _FL, Ctrl->GetClass()); return Ptr != NULL; } // Points virtual void PointToScreen(GdcPt2 &p) = 0; virtual void PointToView(GdcPt2 &p) = 0; virtual bool WindowVirtualOffset(GdcPt2 *Offset) = 0; virtual GViewI *WindowFromPoint(int x, int y, bool Debug = false) = 0; virtual GdcPt2 &GetWindowBorderSize() = 0; virtual bool IsOver(GMouse &m) = 0; // Misc virtual bool Invalidate(GRect *r = 0, bool Repaint = false, bool NonClient = false) = 0; virtual bool Invalidate(GRegion *r, bool Repaint = false, bool NonClient = false) = 0; virtual void SetPulse(int Ms = -1) = 0; virtual bool OnLayout(GViewLayoutInfo &Inf) = 0; protected: virtual bool OnViewMouse(GView *v, GMouse &m) = 0; virtual bool OnViewKey(GView *v, GKey &k) = 0; }; class GMemoryPoolI { public: virtual ~GMemoryPoolI() {} virtual void *Alloc(size_t Size) = 0; virtual void Free(void *Ptr) = 0; virtual void Empty() = 0; }; #endif diff --git a/include/common/Mail.h b/include/common/Mail.h --- a/include/common/Mail.h +++ b/include/common/Mail.h @@ -1,979 +1,979 @@ /** \file \author Matthew Allen */ #ifndef __MAIL_H #define __MAIL_H #include "INet.h" #include "Base64.h" #include "Progress.h" #include "GVariant.h" #ifndef GPL_COMPATIBLE #define GPL_COMPATIBLE 0 #endif // Defines #define MAX_LINE_SIZE 1024 #define MAX_NAME_SIZE 64 #define EMAIL_LINE_SIZE 76 // #define IsDigit(c) ((c) >= '0' AND (c) <= '9') // MIME content types #define CONTENT_NONE 0 #define CONTENT_BASE64 1 #define CONTENT_QUOTED_PRINTABLE 2 #define CONTENT_OCTET_STREAM 3 // Mail logging defines #define MAIL_SEND_COLOUR Rgb24(0, 0, 0xff) #define MAIL_RECEIVE_COLOUR Rgb24(0, 0x8f, 0) #define MAIL_ERROR_COLOUR Rgb24(0xff, 0, 0) #define MAIL_WARNING_COLOUR Rgb24(0xff, 0x7f, 0) #define MAIL_INFO_COLOUR Rgb24(0, 0, 0) // Helper functions extern void TokeniseStrList(char *Str, List &Output, const char *Delim); extern char ConvHexToBin(char c); #define ConvBinToHex(i) (((i)<10)?'0'+(i):'A'+(i)-10) extern void DecodeAddrName(const char *Start, GAutoString &Name, GAutoString &Addr, const char *DefaultDomain); extern char *DecodeRfc2047(char *Str); extern char *EncodeRfc2047(char *Str, const char *CodePage, List *CharsetPrefs, ssize_t LineLength = 0); extern char *DecodeBase64Str(char *Str, int Len = -1); extern char *DecodeQuotedPrintableStr(char *Str, ssize_t Len = -1); extern bool Is8Bit(char *Text); extern int MaxLineLen(char *Text); extern char *EncodeImapString(const char *s); extern char *DecodeImapString(const char *s); extern const char *sTextPlain; extern const char *sTextHtml; extern const char *sTextXml; extern const char *sApplicationInternetExplorer; extern const char sMultipartMixed[]; extern const char sMultipartEncrypted[]; extern const char sMultipartSigned[]; extern const char sMultipartAlternative[]; extern const char sMultipartRelated[]; extern const char sAppOctetStream[]; // Classes class MailProtocol; struct MailProtocolError { int Code; GString ErrMsg; MailProtocolError() { Code = 0; } }; class MailProtocolProgress { public: uint64 Start; int Value; ssize_t Range; MailProtocolProgress() { Empty(); } void Empty() { Start = 0; Value = 0; Range = 0; } void StartTransfer(ssize_t Size) { Start = LgiCurrentTime(); Value = 0; Range = Size; } }; class LogEntry { GColour c; public: GArray Txt; LogEntry(GColour col); GColour GetColour() { return c; } bool Add(const char *t, ssize_t len = -1); }; /// Attachment descriptor class FileDescriptor : public GBase { protected: // Global int64 Size; char *MimeType; char *ContentId; // Read from file GFile File; GStreamI *Embeded; bool OwnEmbeded; int64 Offset; LMutex *Lock; // Write to memory uchar *Data; GAutoPtr DataStream; public: FileDescriptor(GStreamI *embed, int64 Offset, int64 Size, char *Name); FileDescriptor(char *name); FileDescriptor(char *data, int64 len); FileDescriptor(); ~FileDescriptor(); void SetLock(LMutex *l); LMutex *GetLock(); void SetOwnEmbeded(bool i); // Access functions GStreamI *GotoObject(); // Get data to read uchar *GetData(); // Get data from write int Sizeof(); char *GetMimeType() { return MimeType; } void SetMimeType(char *s) { DeleteArray(MimeType); MimeType = NewStr(s); } char *GetContentId() { return ContentId; } void SetContentId(char *s) { DeleteArray(ContentId); ContentId = NewStr(s); } // Decode MIME data to memory bool Decode(char *ContentType, char *ContentTransferEncoding, char *MimeData, int MimeDataLength); }; /// Address dscriptor class AddressDescriptor : public GBase { public: - uint8 Status; + uint8_t Status; uchar CC; // MAIL_ADDR_?? char *Name; char *Addr; void *Data; // application defined AddressDescriptor(AddressDescriptor *Copy = 0); ~AddressDescriptor(); void _Delete(); void Print(char *Str, int Len); ssize_t Sizeof() { return SizeofStr(Name) + SizeofStr(Addr); } bool Serialize(GFile &f, bool Write) { bool Status = true; if (Write) { WriteStr(f, Name); WriteStr(f, Addr); } else { DeleteArray(Name); Name = ReadStr(f PassDebugArgs); DeleteArray(Addr); Addr = ReadStr(f PassDebugArgs); } return Status; } }; /* class MailMessage : public GStream { char* Text; char* TextCharset; char* Html; char* HtmlCharset; public: List To; AddressDescriptor *From; AddressDescriptor *Reply; GAutoString Subject; GAutoString MessageID; GAutoString FwdMsgId; GAutoString BounceMsgId; List FileDesc; char* InternetHeader; char Priority; int MarkColour; uint8 DispositionNotificationTo : 1; // read receipt uint8 EncryptedMsg : 1; GAutoString References; // Protocol specific GAutoString UserData; // Class MailMessage(); virtual ~MailMessage(); void Empty(); virtual char *GetBody(); virtual bool SetBody(const char *Txt, int Bytes = -1, bool Copy = true, const char *Cs = 0); virtual char *GetBodyCharset(); virtual bool SetBodyCharset(const char *Cs); virtual char *GetHtml(); virtual bool SetHtml(const char *Txt, int Bytes = -1, bool Copy = true, const char *Cs = 0); virtual char *GetHtmlCharset(); virtual bool SetHtmlCharset(const char *Cs); // Logging GStream *Log; int Write(const void *Ptr, int Size, int Flags = 0); // Conversion to/from MIME GStringPipe *Raw; // High level encoding functions bool Encode (GStreamI &Out, GStream *HeadersSink, MailProtocol *Protocol, bool Mime = true); bool EncodeHeaders (GStreamI &Out, MailProtocol *Protocol, bool Mime = true); bool EncodeBody (GStreamI &Out, MailProtocol *Protocol, bool Mime = true); // Encoding mime segment data int EncodeText (GStreamI &Out, GStreamI &In); int EncodeQuotedPrintable (GStreamI &Out, GStreamI &In); int EncodeBase64 (GStreamI &Out, GStreamI &In); }; */ /// Base class for mail protocol implementations class MailProtocol { protected: char Buffer[4<<10]; LMutex SocketLock; GAutoPtr Socket; bool Error(const char *file, int line, const char *msg, ...); bool Read(); bool Write(const char *Buf = NULL, bool Log = false); virtual void OnUserMessage(char *Str) {} public: // Logging GStreamI *Logger; void Log(const char *Str, GSocketI::SocketMsgType type); // Task Progress MailProtocolProgress *Items; MailProtocolProgress *Transfer; // Settings int ErrMsgId; /// \sa #L_ERROR_ESMTP_NO_AUTHS, #L_ERROR_ESMTP_UNSUPPORTED_AUTHS GString ErrMsgFmt; /// The format for the printf GString ErrMsgParam; /// The arguments for the printf GString ProgramName; GString ExtraOutgoingHeaders; List CharsetPrefs; // Object MailProtocol(); virtual ~MailProtocol(); // Methods // GSocketI *GetSocket() { return Socket; } /// Thread safe hard close (quit now) bool CloseSocket() { LMutex::Auto l(&SocketLock, _FL); if (Socket != NULL) return Socket->Close() != 0; return false; } void SetError(int ResourceId, const char *Fmt, const char *Param = NULL) { ErrMsgId = ResourceId; ErrMsgFmt = Fmt; ErrMsgParam = Param; } }; ///////////////////////////////////////////////////////////////////// // Mail IO parent classes /// Enable STARTTLS support (requires an SSL capable socket) #define MAIL_USE_STARTTLS 0x01 /// Use authentication #define MAIL_USE_AUTH 0x02 /// Force the use of PLAIN type authentication #define MAIL_USE_PLAIN 0x04 /// Force the use of LOGIN type authentication #define MAIL_USE_LOGIN 0x08 /// Force the use of NTLM type authentication #define MAIL_USE_NTLM 0x10 /// Secure auth #define MAIL_SECURE_AUTH 0x20 /// Use SSL #define MAIL_SSL 0x40 /// OAUTH2 #define MAIL_USE_OAUTH2 0x80 /// CRAM-MD5 #define MAIL_USE_CRAM_MD5 0x100 /// Mail sending protocol class MailSink : public MailProtocol { public: /// Connection setup/shutdown virtual bool Open ( /// The transport layer to use GSocketI *S, /// The host to connect to const char *RemoteHost, /// The local domain const char *LocalDomain, /// The sink username (or NULL) const char *UserName, /// The sink password (or NULL) const char *Password, /// The port to connect with or 0 for default. int Port, /// Options: Use any of #MAIL_SSL, #MAIL_USE_STARTTLS, #MAIL_SECURE_AUTH, #MAIL_USE_PLAIN, #MAIL_USE_LOGIN etc or'd together. int Flags ) = 0; /// Close the connection virtual bool Close() = 0; // Commands available while connected /// Write the email's contents into the GStringPipe returned from /// SendStart and then call SendEnd to finish the transaction virtual GStringPipe *SendStart(List &To, AddressDescriptor *From, MailProtocolError *Err = 0) = 0; /// Finishes the mail send virtual bool SendEnd(GStringPipe *Sink) = 0; }; struct ImapMailFlags { union { struct { - uint8 ImapAnswered : 1; - uint8 ImapDeleted : 1; - uint8 ImapDraft : 1; - uint8 ImapFlagged : 1; - uint8 ImapRecent : 1; - uint8 ImapSeen : 1; - uint8 ImapExpunged :1; + uint8_t ImapAnswered : 1; + uint8_t ImapDeleted : 1; + uint8_t ImapDraft : 1; + uint8_t ImapFlagged : 1; + uint8_t ImapRecent : 1; + uint8_t ImapSeen : 1; + uint8_t ImapExpunged :1; }; uint16 All; }; ImapMailFlags(char *init = 0) { ImapAnswered = 0; ImapDeleted = 0; ImapDraft = 0; ImapFlagged = 0; ImapRecent = 0; ImapSeen = 0; ImapExpunged = 0; if (init) Set(init); } char *Get() { char s[256] = ""; int ch = 0; if (ImapAnswered) ch += sprintf_s(s+ch, sizeof(s)-ch, "\\answered "); if (ImapDeleted) ch += sprintf_s(s+ch, sizeof(s)-ch, "\\deleted "); if (ImapDraft) ch += sprintf_s(s+ch, sizeof(s)-ch, "\\draft "); if (ImapFlagged) ch += sprintf_s(s+ch, sizeof(s)-ch, "\\flagged "); if (ImapRecent) ch += sprintf_s(s+ch, sizeof(s)-ch, "\\recent "); if (ImapSeen) ch += sprintf_s(s+ch, sizeof(s)-ch, "\\seen "); if (ch == 0) return NULL; LgiAssert(ch < sizeof(s)); s[--ch] = 0; return NewStr(s); } void Set(const char *s) { All = 0; if (!s) s = ""; while (*s) { if (*s == '/' || *s == '\\') { while (*s == '/' || *s == '\\') s++; const char *e = s; while (*e && isalpha(*e)) e++; if (!_strnicmp(s, "answered", e-s)) ImapAnswered = true; else if (!_strnicmp(s, "deleted", e-s)) ImapDeleted = true; else if (!_strnicmp(s, "draft", e-s)) ImapDraft = true; else if (!_strnicmp(s, "flagged", e-s)) ImapFlagged = true; else if (!_strnicmp(s, "recent", e-s)) ImapRecent = true; else if (!_strnicmp(s, "seen", e-s)) ImapSeen = true; s = e; } else s++; } } bool operator ==(ImapMailFlags &f) { return ImapAnswered == f.ImapAnswered && ImapDeleted == f.ImapDeleted && ImapDraft == f.ImapDraft && ImapFlagged == f.ImapFlagged && ImapRecent == f.ImapRecent && ImapSeen == f.ImapSeen && ImapExpunged == f.ImapExpunged; } bool operator !=(ImapMailFlags &f) { return !(ImapAnswered == f.ImapAnswered && ImapDeleted == f.ImapDeleted && ImapDraft == f.ImapDraft && ImapFlagged == f.ImapFlagged && ImapRecent == f.ImapRecent && ImapSeen == f.ImapSeen && ImapExpunged == f.ImapExpunged); } }; /// A bulk mail handling class class MailTransaction { public: /// The index of the mail in the folder int Index; /// \sa #MAIL_POSTED_TO_GUI, #MAIL_EXPLICIT int Flags; // bool Delete; bool Status; bool Oversize; /// The mail protocol handler writes the email to this stream GStreamI *Stream; /// Flags used on the IMAP protocolf ImapMailFlags Imap; /// The user app can use this for whatever void *UserData; MailTransaction(); ~MailTransaction(); }; /// Return code from MailSrcCallback enum MailSrcStatus { /// Download the whole email DownloadAll, /// Download just the top part DownloadTop, /// Skip this email DownloadNone, /// About the whole receive DownloadAbort }; /// The callback function used by MailSource::Receive typedef MailSrcStatus (*MailSrcCallback) ( /// The currently executing transaction MailTransaction *Trans, /// The size of the email about to be downloaded uint64 Size, /// If DownloadTop is returned, you can set the number of lines to retreive here int *LinesToDownload, /// The data cookie passed into MailSource::Receive void *Data ); /// The callback function used by MailSource::Receive typedef bool (*MailReceivedCallback) ( /// The currently executing transaction MailTransaction *Trans, /// The data cookie passed into MailSource::Receive void *Data ); /// Collection of callbacks called during mail receive. You should zero this /// entire object before using it. Because if someone adds new callbacks after /// you write the calling code you wouldn't want to leave some callbacks un- /// initialized. A NULL callback is ignored. struct MailCallbacks { /// The callback data void *CallbackData; /// Called before receiving mail MailSrcCallback OnSrc; /// Called after mail received MailReceivedCallback OnReceive; }; /* /// Enable STARTTLS support (requires an SSL capable socket) #define MAIL_SOURCE_STARTTLS 0x01 /// Use authentication #define MAIL_SOURCE_AUTH 0x02 /// Force the use of PLAIN type authentication #define MAIL_SOURCE_USE_PLAIN 0x04 /// Force the use of LOGIN type authentication #define MAIL_SOURCE_USE_LOGIN 0x08 */ /// A generic mail source object class MailSource : public MailProtocol { public: /// Opens a connection to the server virtual bool Open ( /// The transport socket GSocketI *S, /// The hostname or IP of the server const char *RemoteHost, /// The port on the host to connect to int Port, /// The username for authentication const char *User, /// The password for authentication const char *Password, /// [Optional] Persistant storage of settings GDom *SettingStore, /// [Optional] Flags: #MAIL_SOURCE_STARTTLS, #MAIL_SOURCE_AUTH, #MAIL_SOURCE_USE_PLAIN, #MAIL_SOURCE_USE_LOGIN int Flags = 0) = 0; /// Closes the connection virtual bool Close() = 0; /// Returns the number of messages available on the server virtual int GetMessages() = 0; /// Receives a list of messages from the server. virtual bool Receive ( /// An array of messages to receive. The MailTransaction objects contains the index of the message to receive /// and various status values returned after the operation. GArray &Trans, /// An optional set of callback functions. MailCallbacks *Callbacks = 0 ) = 0; /// Deletes a message on the server virtual bool Delete(int Message) = 0; /// Gets the size of the message on the server virtual int Sizeof(int Message) = 0; /// Gets the size of all the messages on the server virtual bool GetSizes(GArray &Sizes) { return false; } /// Gets the unique identifier of the message virtual bool GetUid(int Message, char *Id, int IdLen) = 0; /// Gets the unique identifiers of a list of messages virtual bool GetUidList(List &Id) = 0; /// Gets the headers associated with a given message virtual char *GetHeaders(int Message) = 0; /// Sets the proxy server. e.g. HTTP mail. virtual void SetProxy(char *Server, int Port) {} }; ///////////////////////////////////////////////////////////////////// // Mail IO implementations /// SMTP implementation class MailSmtp : public MailSink { protected: bool ReadReply(const char *Str, GStringPipe *Pipe = 0, MailProtocolError *Err = 0); bool WriteText(const char *Str); public: MailSmtp(); ~MailSmtp(); bool Open(GSocketI *S, const char *RemoteHost, const char *LocalDomain, const char *UserName, const char *Password, int Port = 0, int Flags = 0); bool Close(); bool SendToFrom(List &To, AddressDescriptor *From, MailProtocolError *Err = 0); GStringPipe *SendData(MailProtocolError *Err = 0); GStringPipe *SendStart(List &To, AddressDescriptor *From, MailProtocolError *Err = 0); bool SendEnd(GStringPipe *Sink); // bool Send(MailMessage *Msg, bool Mime = false); }; class MailSendFolder : public MailSink { class MailPostFolderPrivate *d; public: MailSendFolder(char *Path); ~MailSendFolder(); bool Open(GSocketI *S, const char *RemoteHost, const char *LocalDomain, const char *UserName, const char *Password, int Port = 0, int Flags = 0); bool Close(); GStringPipe *SendStart(List &To, AddressDescriptor *From, MailProtocolError *Err = 0); bool SendEnd(GStringPipe *Sink); }; class MailPop3 : public MailSource { protected: bool ReadReply(); bool ReadMultiLineReply(char *&Str); int GetInt(); bool MailIsEnd(char *Ptr, ssize_t Len); bool ListCmd(const char *Cmd, LHashTbl, bool> &Results); const char *End; const char *Marker; int Messages; public: MailPop3(); ~MailPop3(); // Connection bool Open(GSocketI *S, const char *RemoteHost, int Port, const char *User, const char *Password, GDom *SettingStore, int Flags = 0); bool Close(); // Commands available while connected int GetMessages(); bool Receive(GArray &Trans, MailCallbacks *Callbacks = 0); bool Delete(int Message); int Sizeof(int Message); bool GetSizes(GArray &Sizes); bool GetUid(int Message, char *Id, int IdLen); bool GetUidList(List &Id); char *GetHeaders(int Message); }; class MailReceiveFolder : public MailSource { protected: class MailReceiveFolderPrivate *d; public: MailReceiveFolder(char *Path); ~MailReceiveFolder(); // Connection bool Open(GSocketI *S, const char *RemoteHost, int Port, const char *User, const char *Password, GDom *SettingStore, int Flags = 0); bool Close(); // Commands available while connected int GetMessages(); bool Receive(GArray &Trans, MailCallbacks *Callbacks = 0); bool Delete(int Message); int Sizeof(int Message); bool GetUid(int Message, char *Id, int IdLen); bool GetUidList(List &Id); char *GetHeaders(int Message); }; class MailPhp : public MailSource { protected: class MailPhpPrivate *d; bool Get(GSocketI *S, char *Uri, GStream &Out, bool ChopDot); public: MailPhp(); ~MailPhp(); // Connection bool Open(GSocketI *S, const char *RemoteHost, int Port, const char *User, const char *Password, GDom *SettingStore, int Flags = 0); bool Close(); // Commands available while connected int GetMessages(); bool Receive(GArray &Trans, MailCallbacks *Callbacks = 0); bool Delete(int Message); int Sizeof(int Message); bool GetSizes(GArray &Sizes); bool GetUid(int Message, char *Id, int IdLen); bool GetUidList(List &Id); char *GetHeaders(int Message); void SetProxy(char *Server, int Port); }; class MailImapFolder { friend class MailIMap; friend struct ImapThreadPrivate; char Sep; char *Path; public: bool NoSelect; bool NoInferiors; bool Marked; int Exists; int Recent; int Deleted; // int UnseenIndex; MailImapFolder(); virtual ~MailImapFolder(); char *GetPath(); void SetPath(const char *s); char *GetName(); void SetName(const char *s); char GetSep() { return Sep; } void operator =(LHashTbl,int> &v); }; class MailIMap : public MailSource { protected: class MailIMapPrivate *d; char Buf[2048]; List Uid; GStringPipe ReadBuf; List Dialog; void ClearDialog(); void ClearUid(); bool FillUidList(); bool WriteBuf(bool ObsurePass = false, const char *Buffer = 0, bool Continuation = false); bool ReadResponse(int Cmd = -1, bool Plus = false); bool Read(GStreamI *Out = 0, int Timeout = -1); bool ReadLine(); bool IsResponse(const char *Buf, int Cmd, bool &Ok); void CommandFinished(); public: typedef LHashTbl,GString> StrMap; struct StrRange { ssize_t Start, End; void Set(ssize_t s, ssize_t e) { Start = s; End = e; } ssize_t Len() { return End - Start; } }; // Typedefs struct Untagged { GString Cmd; GString Param; int Id; }; struct OAuthParams { enum ServiceProvider { OAuthGoogle, OAuthMicrosoft, } Provider; GString ClientID; GString ClientSecret; GString RedirURIs; GString AuthUri; GString ApiUri; // GString RevokeUri; GString Scope; GUri Proxy; GString AccessToken; GString RefreshToken; int ExpiresIn; bool IsValid() { return ClientID && ClientSecret && RedirURIs && AuthUri && // RevokeUri && Scope && ApiUri; } }; /// This callback is used to notify the application using this object of IMAP fetch responses. /// \returns true if the application wants to continue reading and has taken ownership of the strings in "Parts". typedef bool (*FetchCallback) ( /// The IMAP object class MailIMap *Imap, /// The message sequence number char *Msg, /// The fetch parts (which the callee needs to own if returning true) StrMap &Parts, /// The user data passed to the Fetch function void *UserData ); // Object MailIMap(); ~MailIMap(); // Mutex bool Lock(const char *file, int line); bool LockWithTimeout(int Timeout, const char *file, int line); void Unlock(); // General char GetFolderSep(); char *EncodePath(const char *Path); char *GetCurrentPath(); bool GetExpungeOnExit(); void SetExpungeOnExit(bool b); bool ServerOption(char *Opt); bool IsOnline(); const char *GetWebLoginUri(); void SetOAuthParams(OAuthParams &p); void SetParentWindow(GViewI *wnd); void SetCancel(LCancel *Cancel); ssize_t ParseImapResponse(char *Buffer, ssize_t BufferLen, GArray &Ranges, int Names); // Connection bool Open(GSocketI *S, const char *RemoteHost, int Port, const char *User, const char *Password, GDom *SettingStore, int Flags = 0); bool Close(); // Non-threadsafe soft close (normal operation) bool GetCapabilities(GArray &s); // Commands available while connected bool Receive(GArray &Trans, MailCallbacks *Callbacks = 0); int GetMessages(); bool Delete(int Message); bool Delete(bool ByUid, const char *Seq); int Sizeof(int Message); bool GetSizes(GArray &Sizes); bool GetUid(int Message, char *Id, int IdLen); bool GetUidList(List &Id); char *GetHeaders(int Message); char *SequenceToString(GArray *Seq); // Imap specific commands /// This method wraps the imap FETCH command /// \returns the number of records accepted by the callback fn int Fetch ( /// True if 'Seq' is a UID, otherwise it's a sequence bool ByUid, /// The sequence number or UID const char *Seq, /// The parts to retrieve const char *Parts, /// Data is returned to the caller via this callback function FetchCallback Callback, /// A user defined param to pass back to the 'Callback' function. void *UserData, /// [Optional] The raw data received will be written to this stream if provided, else NULL. GStreamI *RawCopy = 0, /// [Optional] The rough size of the fetch... used to pre-allocate a buffer to receive data. int64 SizeHint = -1 ); /// Appends a message to the specified folder bool Append ( /// The folder to write to const char *Folder, /// [Optional] Flags for the message ImapMailFlags *Flags, /// The rfc822 body of the message const char *Msg, /// [Out] The UID of the message appended (if known, can be empty if not known) GString &NewUid ); bool GetFolders(GArray &Folders); bool SelectFolder(const char *Path, StrMap *Values = 0); char *GetSelectedFolder(); int GetMessages(const char *Path); bool CreateFolder(MailImapFolder *f); bool DeleteFolder(const char *Path); bool RenameFolder(const char *From, const char *To); bool SetFolderFlags(MailImapFolder *f); /// Expunges (final delete) any deleted messages the current folder. bool ExpungeFolder(); // Uid methods bool CopyByUid(GArray &InUids, const char *DestFolder); bool SetFlagsByUid(GArray &Uids, const char *Flags); /// Idle processing... /// \returns true if something happened bool StartIdle(); bool OnIdle(int Timeout, GArray &Resp); bool FinishIdle(); bool Poll(int *Recent = 0, GArray *New = 0); bool Status(char *Path, int *Recent); bool Search(bool Uids, GArray &SeqNumbers, const char *Filter); // Utility static bool Http(GSocketI *S, GAutoString *OutHeaders, GAutoString *OutBody, int *StatusCode, const char *InMethod, const char *InUri, const char *InHeaders, const char *InBody); }; #endif diff --git a/include/win32/GSymLookup.h b/include/win32/GSymLookup.h --- a/include/win32/GSymLookup.h +++ b/include/win32/GSymLookup.h @@ -1,315 +1,315 @@ /// \file /// \author Matthew Allen #ifndef _GSYMLOOKUP_H_ #define _GSYMLOOKUP_H_ #ifdef WIN32 #include "ImageHlp.h" #include #include #undef _T #include #if _MSC_VER < 1300 typedef DWORD DWORD_PTR; #define __w64 #endif #ifdef _UNICODE #define TCHAR_FORMAT "%S" #else #define TCHAR_FORMAT "%s" #endif typedef BOOL (__stdcall *SYMINITIALIZEPROC)( HANDLE, LPSTR, BOOL ); typedef BOOL (__stdcall *SYMCLEANUPPROC)( HANDLE ); typedef BOOL (__stdcall * STACKWALKPROC) ( DWORD, HANDLE, HANDLE, LPSTACKFRAME, LPVOID, PREAD_PROCESS_MEMORY_ROUTINE,PFUNCTION_TABLE_ACCESS_ROUTINE, PGET_MODULE_BASE_ROUTINE, PTRANSLATE_ADDRESS_ROUTINE ); typedef LPVOID (__stdcall *SYMFUNCTIONTABLEACCESSPROC)( HANDLE, DWORD ); typedef DWORD (__stdcall *SYMGETMODULEBASEPROC)( HANDLE, DWORD ); typedef BOOL (__stdcall *SYMGETSYMFROMADDRPROC) ( HANDLE, DWORD, PDWORD, PIMAGEHLP_SYMBOL ); #ifdef _WIN64 typedef BOOL (WINAPI *pSymGetLineFromAddr64)(HANDLE hProcess, DWORD64 dwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line); #else typedef BOOL (__stdcall *proc_SymGetLineFromAddr)( HANDLE hProcess, DWORD dwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE Line); #endif typedef DWORD (__stdcall *proc_SymGetOptions)(VOID); typedef DWORD (__stdcall *proc_SymSetOptions)(DWORD SymOptions); typedef BOOL (WINAPI *pSymFromAddr)(HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFO Symbol); /// Lookup the file/line information for an instruction pointer value class GSymLookup { HMODULE DbgHelp; HANDLE hProcess; SYMINITIALIZEPROC SymInitialize; SYMCLEANUPPROC SymCleanup; STACKWALKPROC StackWalk; SYMFUNCTIONTABLEACCESSPROC SymFunctionTableAccess; SYMGETMODULEBASEPROC SymGetModuleBase; SYMGETSYMFROMADDRPROC SymGetSymFromAddr; #ifdef _WIN64 pSymGetLineFromAddr64 SymGetLineFromAddr64; #else proc_SymGetLineFromAddr SymGetLineFromAddr; #endif proc_SymGetOptions SymGetOptions; proc_SymSetOptions SymSetOptions; bool InitOk; public: typedef NativeInt Addr; GSymLookup() { hProcess = GetCurrentProcess(); InitOk = 0; DbgHelp = LoadLibrary(_T("dbghelp.dll")); if (DbgHelp) { SymInitialize = (SYMINITIALIZEPROC) GetProcAddress(DbgHelp, "SymInitialize"); SymCleanup = (SYMCLEANUPPROC) GetProcAddress(DbgHelp, "SymCleanup"); StackWalk = (STACKWALKPROC) GetProcAddress(DbgHelp, "StackWalk"); SymFunctionTableAccess = (SYMFUNCTIONTABLEACCESSPROC) GetProcAddress(DbgHelp, "SymFunctionTableAccess"); SymGetModuleBase = (SYMGETMODULEBASEPROC) GetProcAddress(DbgHelp, "SymGetModuleBase"); SymGetSymFromAddr = (SYMGETSYMFROMADDRPROC) GetProcAddress(DbgHelp, "SymGetSymFromAddr"); #ifdef _WIN64 SymGetLineFromAddr64 = (pSymGetLineFromAddr64) GetProcAddress(DbgHelp, "SymGetLineFromAddr64"); #else SymGetLineFromAddr = (proc_SymGetLineFromAddr) GetProcAddress(DbgHelp, "SymGetLineFromAddr"); #endif SymGetOptions = (proc_SymGetOptions) GetProcAddress(DbgHelp, "SymGetOptions"); SymSetOptions = (proc_SymSetOptions) GetProcAddress(DbgHelp, "SymSetOptions"); DWORD dwOpts = SymGetOptions(); DWORD Set = SymSetOptions(dwOpts | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST); char s[256]; _getcwd(s, sizeof(s)); #ifdef _MSC_VER char path[MAX_PATH] = ""; size_t sz; getenv_s(&sz, path, sizeof(path), "path"); #else char *path = getenv("path"); #endif int pathlen = (int)strlen(path); int len = pathlen + (int)strlen(s) + 256; char *all = (char*)malloc(len); if (all) { #if _MSC_VER >= 1300 strcpy_s(all, len, path); #else strcpy(all, path); #endif if (all[strlen(all)-1] != ';') { #if _MSC_VER >= 1300 strcat_s(all, len, ";"); #else strcat(all, ";"); #endif } #ifdef _DEBUG char *Mode = "Debug"; #else char *Mode = "Release"; #endif #if _MSC_VER >= 1300 sprintf_s(all+strlen(all), len-strlen(all), "%s\\%s", s, Mode); #else sprintf(all+strlen(all), "%s\\%s", s, Mode); #endif InitOk = SymInitialize(hProcess, all, true) != 0; free(all); } } } ~GSymLookup() { if (DbgHelp) { SymCleanup(hProcess); FreeLibrary(DbgHelp); } } bool GetStatus() { return InitOk; } /// Looks up the file and line related to an instruction pointer address /// \returns non zero on success bool Lookup ( /// The return string buffer char *buf, /// The buffer's length int buffer_len, /// The address Addr *Ip, /// Cound of IP's int IpLen ) { if (!buf || buffer_len < 1 || !Ip || IpLen < 1) return false; #if _MSC_VER >= 1300 #define Sprintf sprintf_s #else #define Sprintf snprintf #endif bool Status = true; char *buf_end = buf + buffer_len; for (int i=0; iSizeOfStruct = sizeof(symbolBuffer); pSymbol->MaxNameLength = 512; memset(&Line, 0, sizeof(Line)); Line.SizeOfStruct = sizeof(Line); TCHAR *mod = _tcsrchr(module, '\\'); if (mod) mod++; else mod = module; bool found_addr = false; if (SymGetSymFromAddr(hProcess, (DWORD)Ip[i], &symDisplacement, pSymbol)) { if (SymGetLineFromAddr(hProcess, (DWORD_PTR)Ip[i], &symDisplacement, &Line)) { int Ch = Sprintf(buf, buf_end-buf, " %p: " TCHAR_FORMAT ", %s:%i", (void*)Ip[i], mod, Line.FileName, Line.LineNumber); if (Ch > 0) buf += Ch; else Status = false; found_addr = true; } else if (pSymbol->Name[0] != '$') { int Ch = Sprintf(buf, buf_end-buf, " %p: " TCHAR_FORMAT ", %s+0x%x", (void*)Ip[i], mod, pSymbol->Name, symDisplacement); if (Ch > 0) buf += Ch; else Status = false; found_addr = true; } } if (!found_addr) { int Ch = Sprintf(buf, buf_end-buf, " %p: " TCHAR_FORMAT, (void*)Ip[i], mod); if (Ch > 0) buf += Ch; else Status = false; } } else { int Ch = Sprintf(buf, buf_end-buf, " %p: unknown module", (void*)Ip[i]); if (Ch > 0) buf += Ch; else Status = false; } int Ch = Sprintf(buf, buf_end-buf, "\r\n"); if (Ch > 0) buf += Ch; } return Status; } int BackTrace(long Eip, long Ebp, Addr *addr, int len) { if (!addr || len < 1) return 0; int i = 0; // Save the stack trace Addr RegEbp = Ebp; if (!Ebp) { memset(addr, 0, sizeof(Addr) * len); #if defined(_WIN64) LgiAssert(!"Not impl"); #elif defined(_MSC_VER) // Microsoft C++ _asm { mov eax, ebp mov RegEbp, eax } #else // GCC asm("movl %%ebp, %%eax;" "movl %%eax, %0;" :"=r"(RegEbp) /* output */ : /* no input */ :"%eax" /* clobbered register */ ); #endif } if (Eip) addr[i++] = Eip; for (; i= 0) - addr[i] = (Addr) *((uint8**)RegEbp + 1); + addr[i] = (Addr) *((uint8_t**)RegEbp + 1); RegEbp = *(Addr*)RegEbp; } return i; } }; #endif #endif \ No newline at end of file diff --git a/src/common/Coding/GParseCpp.cpp b/src/common/Coding/GParseCpp.cpp --- a/src/common/Coding/GParseCpp.cpp +++ b/src/common/Coding/GParseCpp.cpp @@ -1,2339 +1,2339 @@ #include #include #include "Lgi.h" #include "GParseCpp.h" #include "LThreadEvent.h" #include "GLexCpp.h" #include "GVariant.h" #include "GScriptingPriv.h" #include "GXmlTree.h" #include "GToken.h" #define CheckToken(t) if (!t) { Error = true; break; } #if defined(WINDOWS) && defined(_MSC_VER) && !defined(WIN64) #define Debug __asm int 3 #else #define Debug assert(0) #endif static const char16 sRoundBracketStart[] = { '(', 0 }; static const char16 sRoundBracketEnd[] = { ')', 0 }; static const char16 sDefined[] = {'d','e','f','i','n','e','d',0}; static const char16 sCpp[] = {'_','_','c','p','l','u','s','p','l','u','s',0}; enum LoopState { LsLooping, LsFinished, LsError }; struct PreprocessState { bool ParentIsActive; bool IfBranchClaimed; }; enum PreprocessSymbol { SourceBlock, HashIf, HashElif, HashIfndef, HashIfdef, HashElse, HashEndif, HashPragma, HashError, HashWarning, HashDefine, HashUndef, HashInclude, }; const char *PreprocessSymbolNames[] = { "SourceBlock", "HashIf", "HashElif", "HashIfndef", "HashIfdef", "HashElse", "HashEndif", "HashPragma", "HashError", "HashWarning", "HashDefine", "HashUndef", "HashInclude", }; /// A file is parsed into a stream of these preprocessed blocks. The content /// of the block may be present in unlexed form, or parsed form. This saves /// on having to re-lex files over and over. class PreprocessBlock { friend struct GSourceFile; GArray Lines; public: /// The type of block this is. PreprocessSymbol Type; /// The start line in the original source code of this block. int BlockLine; // Unparsed string data char16 *Start; int Len; // -or- // Lexed data int CurToken; int Line; GArray Tokens; PreprocessBlock() { Type = SourceBlock; Start = NULL; Len = 0; CurToken = 0; BlockLine = 0; // Invalid line.. } char16 *NextToken() { if (Start != NULL) LgiAssert(0); // Call GSourceFile::Lex first if (CurToken >= Tokens.Length()) return NULL; Line = Lines[CurToken]; return Tokens[CurToken++]; } int GetLine() { if (CurToken >= Tokens.Length()) Lines.Last(); return Lines[CurToken]; } }; bool CmpToken(char16 *a, const char *b) { if (!a || !b) return false; while (*a && *b) { if (ToLower(*a) != ToLower(*b)) return false; a++; b++; } return !*a && !*b; } bool HasToken(GArray &a, const char *b) { if (!a.Length() || !b) return false; for (int i=0; i IncludePaths; GArray PreDefines; GArray Source; GAutoString SearchTerm; WorkUnit ( GArray &IncPaths, GArray &PreDefs, GArray &Src ) { int i; for (i=0; i Tokens; GSymbol *Parent; GArray Children; const char *DefineFile; int DefineLine; bool FriendDecl; bool ForwardDecl; GSymbol(const char *def_file, int def_line) { Parent = 0; Type = SymNone; File = NULL; Line = 0; FriendDecl = false; ForwardDecl = false; DefineFile = def_file; DefineLine = def_line; } ~GSymbol() { Children.DeleteObjects(); } void Add(GSymbol *s) { if (!Children.HasItem(s)) { Children.Add(s); s->Parent = this; } } }; struct GCppStringPool { int Size; int Used; GArray Mem; GCppStringPool(int sz) { Used = Size = sz; } ~GCppStringPool() { Mem.DeleteArrays(); } char16 *Alloc(const char16 *s, ssize_t len) { if (len < 0) len = StrlenW(s); int remaining = Size - Used; char16 *n; if (len + 1 > remaining) { n = new char16[Size]; Mem.Add(n); Used = 0; } else { LgiAssert(Mem.Length() > 0); n = Mem.Last() + Used; } memcpy(n, s, sizeof(*s) * len); n[len] = 0; Used += len + 1; return n; } }; char16 *LexPoolAlloc(void *Context, const char16 *s, ssize_t len) { return ((GCppStringPool*)Context)->Alloc(s, len); } struct GSourceFile { - uint32 Id; + uint32_t Id; GAutoString Path; GCppStringPool Pool; GAutoWString Raw; bool Active; GArray Stack; int CurBlock; GArray Blocks; PreprocessBlock *Block; GSourceFile() : Pool(64 << 10) { Id = 0; CurBlock = 0; Block = NULL; Active = true; } PreprocessBlock *Current() { if (!Block && CurBlock == 0) Block = &Blocks[CurBlock]; return Block; } PreprocessBlock *Next() { CurBlock++; if (CurBlock >= Blocks.Length()) { Block = NULL; return NULL; } Block = &Blocks[CurBlock]; return Block; } bool Read(const char *path) { if (!Path.Reset(NewStr(path))) return false; GAutoString f(ReadTextFile(Path)); if (!f) return false; Raw.Reset(Utf8ToWide(f)); return true; } bool Lex(PreprocessBlock &blk) { if (blk.Start) { char16 *Cur = blk.Start; char16 *End = blk.Start + blk.Len; int Line = blk.BlockLine; char16 *t; // Temporarily NULL terminate this block char16 OldEndChar = *End; *End = 0; if (blk.Type == HashInclude) { // Special include parsing... while (*Cur && (t = LexCpp(Cur, LexPoolAlloc, &Pool, &Line))) { if (CmpToken(t, "<")) { char16 delim = '>'; char16 *End = StrchrW(t = Cur, delim); *End = 0; } else if (*t == '\"' || *t == '\'') { // Trim the string markers char16 delim = *t++; char16 *End = StrchrW(t, delim); *End = 0; } blk.Tokens.Add(t); blk.Lines.Add(Line); break; } } else if (blk.Type == HashDefine) { // Special #define parsing to remove the line continuation characters... while (*Cur && (t = LexCpp(Cur, LexPoolAlloc, &Pool, &Line))) { if (CmpToken(t, "\\")) { // Is this a line continuation char? char16 *End = Cur; bool AllWhiteSpace = true; while (*End && *End != '\r' && *End != '\n') { if (*End != ' ' && *End != '\t') { AllWhiteSpace = false; break; } End++; } if (AllWhiteSpace) { continue; } } blk.Tokens.Add(t); blk.Lines.Add(Line); } } else { // General code parsing while (*Cur && (t = LexCpp(Cur, LexPoolAlloc, &Pool, &Line))) { blk.Tokens.Add(t); blk.Lines.Add(Line); } } // Restore the string to normal *End = OldEndChar; // Clear the unparsed data ptr blk.Start = NULL; } return NULL; } void DumpBlocks() { #if 1 GXmlTree t; GFile f; char *Leaf = strrchr(Path, DIR_CHAR); char Out[MAX_PATH]; LgiMakePath(Out, sizeof(Out), "c:\\temp", Leaf + 1); char *Ext = LgiGetExtension(Out); strcpy(Ext, "xml"); if (f.Open(Out, O_WRITE)) { f.SetSize(0); GXmlTag r("Dump"); r.SetAttr("Path", Path); for (int i=0; iSetAttr("Line", b.BlockLine); GAutoString a(WideToUtf8(b.Start, b.Len)); c->SetContent(a); } t.Write(&r, &f); f.Close(); } else LgiAssert(0); #endif } /* bool At(const char *file, int line) { char *d = Path ? strrchr(Path, DIR_CHAR) : NULL; if (!d) return false; return stricmp(d+1, file) == 0 && Line == line; } */ }; typedef LHashTbl, GSymbol*> SourceScopeMap; struct GSourceScope : public SourceScopeMap { GSourceScope() : SourceScopeMap(5000) { } ~GSourceScope() { DeleteObjects(); } bool Add(char16 *k, GSymbol *v) { if (Find(k)) { return false; } return SourceScopeMap::Add(k, v); } GSymbol *Define(char16 *name, GSymbolType Type, const char *def_file, int def_line) { GSymbol *s = Find(name); if (!s) Add(name, s = new GSymbol(def_file, def_line)); s->Type = Type; s->File = NULL; s->Line = 0; return s; } }; enum KeywordType { KwNone, KwSpecifier, KwType, KwQualifier, KwModifier, KwUserType, KwVisibility, KwPreprocessor }; enum MsgType { MsgError, MsgWarning, MsgInfo, }; template void AddHash(T tbl, const char *name, V type) { char16 s[256]; Strcpy(s, CountOf(s), name); tbl.Add(s, type); } struct GCppParserWorker : public GCompileTools { GCppParserPriv *d; GAutoPtr w; GCppStringPool GeneralPool; GArray Srcs; GArray Scopes; // from global to local LHashTbl, KeywordType> Keywords; LHashTbl, PreprocessSymbol> PreprocessSyms; LHashTbl, char*> IncludePathFiles; char *FindInclude(char *file); bool Preprocess(GSourceFile *sf); void InitScopes(); GSourceScope *CurrentScope() { return Scopes[Scopes.Length()-1]; } GAutoWString GetSymbolName(GArray &a, bool IsEnum = false); GSymbol *ParseDecl(GSourceFile *sf, char16 *t); GSymbol *ParseUserType(GSourceFile *sf, char16 *t); GSymbol *ParseTypedef(GSourceFile *sf); bool ParsePreprocessor(GSourceFile *sf); public: GCppParserWorker(GCppParserPriv *priv); ~GCppParserWorker(); GSourceFile *ParseCpp(const char *Path); GSymbol *Resolve(char16 *Symbol); int Evaluate(GArray &Exp); void DoWork(WorkUnit *wk); int Main(); void Msg(MsgType Type, const char *Fmt, ...); }; struct GCppParserPriv { GArray IncPaths; GAutoPtr Worker; }; GCppParserWorker::GCppParserWorker(GCppParserPriv *priv) : GeneralPool(4 << 10) { d = priv; AddHash(Keywords, "auto", KwSpecifier); AddHash(Keywords, "extern", KwSpecifier); AddHash(Keywords, "static", KwSpecifier); AddHash(Keywords, "register", KwSpecifier); AddHash(Keywords, "mutable", KwSpecifier); AddHash(Keywords, "bool", KwType); AddHash(Keywords, "char", KwType); AddHash(Keywords, "wchar_t", KwType); AddHash(Keywords, "short", KwType); AddHash(Keywords, "int", KwType); AddHash(Keywords, "long", KwType); AddHash(Keywords, "float", KwType); AddHash(Keywords, "double", KwType); AddHash(Keywords, "void", KwType); #ifdef WIN32 AddHash(Keywords, "__int64", KwType); AddHash(Keywords, "_W64", KwModifier); #endif AddHash(Keywords, "unsigned", KwModifier); AddHash(Keywords, "signed", KwModifier); AddHash(Keywords, "__declspec", KwModifier); AddHash(Keywords, "template", KwModifier); AddHash(Keywords, "__stdcall", KwModifier); AddHash(Keywords, "friend", KwModifier); AddHash(Keywords, "operator", KwModifier); AddHash(Keywords, "class", KwUserType); AddHash(Keywords, "struct", KwUserType); AddHash(Keywords, "union", KwUserType); AddHash(Keywords, "enum", KwUserType); AddHash(Keywords, "public:", KwVisibility); AddHash(Keywords, "private:", KwVisibility); AddHash(Keywords, "protected:", KwVisibility); AddHash(Keywords, "#if", KwPreprocessor); AddHash(Keywords, "#ifdef", KwPreprocessor); AddHash(Keywords, "#ifndef", KwPreprocessor); AddHash(Keywords, "#elif", KwPreprocessor); AddHash(Keywords, "#else", KwPreprocessor); AddHash(Keywords, "#endif", KwPreprocessor); AddHash(Keywords, "#include", KwPreprocessor); AddHash(Keywords, "#define", KwPreprocessor); AddHash(Keywords, "#undef", KwPreprocessor); AddHash(Keywords, "#error", KwPreprocessor); AddHash(Keywords, "#warning", KwPreprocessor); AddHash(Keywords, "#pragma", KwPreprocessor); AddHash(PreprocessSyms, "#if", HashIf); AddHash(PreprocessSyms, "#elif", HashElif); AddHash(PreprocessSyms, "#ifndef", HashIfndef); AddHash(PreprocessSyms, "#ifdef", HashIfdef); AddHash(PreprocessSyms, "#else", HashElse); AddHash(PreprocessSyms, "#endif", HashEndif); AddHash(PreprocessSyms, "#pragma", HashPragma); AddHash(PreprocessSyms, "#error", HashError); AddHash(PreprocessSyms, "#warning", HashWarning); AddHash(PreprocessSyms, "#define", HashDefine); AddHash(PreprocessSyms, "#undef", HashUndef); AddHash(PreprocessSyms, "#include", HashInclude); } GCppParserWorker::~GCppParserWorker() { Srcs.DeleteObjects(); Scopes.DeleteObjects(); IncludePathFiles.DeleteArrays(); } void GCppParserWorker::Msg(MsgType Type, const char *Fmt, ...) { va_list Arg; va_start(Arg, Fmt); #ifdef WIN32 char Buf[256]; #ifdef _MSC_VER vsnprintf_s(Buf, sizeof(Buf), _TRUNCATE, Fmt, Arg); #else vsnprintf(Buf, sizeof(Buf), Fmt, Arg); #endif OutputDebugStringA(Buf); #else vprintf(Fmt, Arg); #endif va_end(Arg); if (Type == MsgError) Debug; } char *GCppParserWorker::FindInclude(char *File) { if (IncludePathFiles.Length() == 0) { for (int i=0; iIncludePaths.Length(); i++) { GDirectory dir; char p[MAX_PATH]; for (bool b=dir.First(w->IncludePaths[i]); b; b=dir.Next()) { char *Leaf = dir.GetName(); if (!dir.IsDir()) { char *ext = LgiGetExtension(Leaf); if (ext && ext[0] == 'h' && !ext[1]) { if (dir.Path(p, sizeof(p))) { if (!IncludePathFiles.Find(Leaf)) IncludePathFiles.Add(Leaf, NewStr(p)); } } } } } } char *p = IncludePathFiles.Find(File); return p; } void GCppParserWorker::InitScopes() { Scopes.DeleteObjects(); Scopes.Add(new GSourceScope); #ifdef WIN32 char16 buf[256]; GSymbol *s; #ifdef _MSC_VER s = Scopes[0]->Define(L"_MSC_VER", SymDefineValue, _FL); int ch = swprintf_s(buf, CountOf(buf), L"%i", _MSC_VER); s->Tokens.Add(GeneralPool.Alloc(buf, ch)); s->File = "none"; #endif s = Scopes[0]->Define(L"WIN32", SymDefineValue, _FL); s->File = "none"; #endif } struct RoundBracket { int Depth; int Sections; bool HasPtr; RoundBracket() { Depth = 0; Sections = 0; HasPtr = false; } void Parse(GArray &a) { for (int i=0; i= 0); } else if (CmpToken(t, "*") && Sections == 1) { HasPtr = true; } } LgiAssert(Depth == 0); } }; GAutoWString GCppParserWorker::GetSymbolName(GArray &in, bool IsEnum) { GArray a = in; GAutoWString ret; GStringPipe p(64); RoundBracket Rb; int LastName = -1; bool FuncPtr = false; int Operator = -1; char16 *t, *prev = NULL; Rb.Parse(a); if (Rb.Sections == 2 && Rb.HasPtr) FuncPtr = true; Rb.Sections = 0; for (int i=0; iType == SymDefineValue || sym->Type == SymDefineFunction) { if (sym->Type == SymDefineFunction) { // Resolve the arguments... t = a[++i]; if (!CmpToken(t, "(")) { Msg(MsgError, "Missing '(' in call to '%S' define.\n", t); break; } int Depth = 1; while ((t = a[++i])) { if (CmpToken(t, "(")) { Depth++; } else if (CmpToken(t, ")")) { if (--Depth <= 0) break; } } Msg(MsgWarning, "NotImpl: Parsing of #define arguments.\n"); } a.DeleteAt(i, true); for (int n=0; nTokens.Length(); n++) { char16 *s = sym->Tokens[n]; a.AddAt(i+n, s); } i--; } else if (sym->Type == SymTypedef) { a.DeleteAt(i, true); if (i < a.Length()) { char16 *Next = a[i]; a.DeleteAt(i, true); for (int n=0; nTokens.Length(); n++) { char16 *s = sym->Tokens[n]; if (StricmpW(s, t)) a.AddAt(i+n, s); else a.AddAt(i+n, Next); } i--; RoundBracket rb; rb.Parse(a); if (rb.Sections == 2 && rb.HasPtr) FuncPtr = true; } else { // This can happen when you define a typedef to twice GAutoWString Empty; return Empty; } } else { goto IsAlpha; } } else if (CmpToken(t, "(")) { if (Rb.Depth == 0) Rb.Sections++; Rb.Depth++; } else if (CmpToken(t, ")")) { Rb.Depth--; LgiAssert(Rb.Depth >= 0); } else if (IsAlpha(*t) || *t == '_') { IsAlpha: if (FuncPtr) { if (Rb.Depth == 1 && Rb.Sections == 1) { LastName = i; } } else if (Rb.Depth == 0) { LastName = i; } } prev = t; } if (Operator >= 0) { for (int i=Operator; i Operator) p.Write(L"_", sizeof(char16)); p.Write(t, StrlenW(t) * sizeof(char16) ); } } else { if (IsEnum && LastName < 0) return GAutoWString(); LgiAssert(LastName >= 0); t = a[LastName]; p.Write(t, StrlenW(t) * sizeof(char16) ); } ret.Reset(p.NewStrW()); LgiAssert(ret); return ret; } GSymbol *GCppParserWorker::Resolve(char16 *Symbol) { for (int i = Scopes.Length()-1; i >= 0; i--) { GSourceScope *scope = Scopes[i]; GSymbol *sym = scope->Find(Symbol); if (sym) return sym; } return NULL; } GVariant *GetArg(GArray &Values, int i) { if (i < 0 || i >= Values.Length()) return NULL; if (Values[i].Type == GV_OPERATOR) return NULL; return &Values[i]; } int FindMatchingBracket(GArray &Exp, int Start) { int Depth = 1; for (int i = Start + 1; i &Exp) { bool Error = false; GArray Values; for (int i=0; i Sub; for (int n=i+1; nTokens); Values.New() = Val; } else { Values.New() = 0; } } } } } if (Error) return false; // Evaluate the contents of Values while (Values.Length() > 1) { int StartLength = Values.Length(); int OpIdx = -1; int Prec = -1; for (int i=0; iCastBool(); break; } default: { Msg(MsgError, "Impl this op.\n"); break; } } Values.DeleteAt(OpIdx + 1, true); break; } case OpInfix: { GVariant *a = GetArg(Values, OpIdx - 1); GVariant *b = GetArg(Values, OpIdx + 1); if (!a || !b) { Msg(MsgError, "Missing op args.\n"); break; } switch (OpVar.Value.Op) { #define CombineUsingOp(op, type, cpp_op) \ case op: \ { \ *a = a->Cast##type() cpp_op b->Cast##type(); \ break; \ } CombineUsingOp(OpOr, Bool, ||); CombineUsingOp(OpAnd, Bool, &&); CombineUsingOp(OpGreaterThan, Int32, >); CombineUsingOp(OpGreaterThanEqual, Int32, >=); CombineUsingOp(OpLessThan, Int32, <); CombineUsingOp(OpLessThanEqual, Int32, <=); CombineUsingOp(OpEquals, Int32, ==); CombineUsingOp(OpNotEquals, Int32, !=); CombineUsingOp(OpPlus, Int32, +); CombineUsingOp(OpMinus, Int32, -); CombineUsingOp(OpMul, Int32, *); CombineUsingOp(OpDiv, Int32, /); CombineUsingOp(OpMod, Int32, %); default: { Msg(MsgError, "Impl this op.\n"); break; } } Values.DeleteAt(OpIdx, true); Values.DeleteAt(OpIdx, true); break; } default: { Msg(MsgError, "Impl this op type.\n"); break; } } LgiAssert(StartLength > Values.Length()); if (Values.Length() >= StartLength) { Msg(MsgError, "Nothing happened.\n"); break; } } return Values.Length() == 1 ? Values[0].CastInt32() : 0; } #define IsWhite(c) ( (c) == ' ' || (c) == '\t' ) #define SkipNewLine(Cur) \ if (Cur[0] == '\r' && Cur[1] == '\n') \ Cur += 2; \ else if (Cur[0] == '\r') \ Cur++; \ else if (Cur[0] == '\n') \ Cur++; \ Line++; bool GCppParserWorker::Preprocess(GSourceFile *sf) { char16 *Cur = sf->Raw; int Line = 1; char16 Cmd[256]; PreprocessBlock *Blk = NULL; while (*Cur) { // Find Start of line while (IsWhite(*Cur)) Cur++; if (*Cur == '#') { // Emit a source code block if (Blk) { Blk->Len = Cur - Blk->Start; if (Blk->Len <= 8) { bool IsWhiteSpace = true; for (int i=0; iLen; i++) { if (Blk->Start[i] != ' ' && Blk->Start[i] != '\t' && Blk->Start[i] != '\r' && Blk->Start[i] != '\n') { IsWhiteSpace = false; } } if (IsWhiteSpace) { Blk->Start = NULL; Blk->Len = 0; } else { Blk = NULL; } } else { Blk = NULL; } } // Read over the preprocessor command and save the name char16 *o = Cmd; char16 *e = Cmd + 255; *o++ = *Cur++; while (IsWhite(*Cur)) Cur++; while ( *Cur && !IsWhite(*Cur) && *Cur != '\r' && *Cur != '\n' && o < e) { *o++ = *Cur++; } *o++ = 0; while (IsWhite(*Cur)) Cur++; // Create a block for the statement if (Blk == NULL) Blk = &sf->Blocks.New(); Blk->BlockLine = Line; Blk->Type = PreprocessSyms.Find(Cmd); LgiAssert(Blk->Type != SourceBlock); Blk->Start = Cur; // Scan till the end of line... char16 *LastNonWhite = NULL; do { while (*Cur && *Cur != '\r' && *Cur != '\n') { if (!IsWhite(*Cur)) LastNonWhite = Cur; Cur++; } if (LastNonWhite && *LastNonWhite == '\\' && Blk->Type == HashDefine) { // Multi-line #define... SkipNewLine(Cur); } else { SkipNewLine(Cur); break; } } while (*Cur); Blk->Len = Cur - Blk->Start; while (Blk->Len > 0 && strchr(" \r\t\n", Blk->Start[Blk->Len - 1])) Blk->Len--; Blk = NULL; } else { if (!Blk) { Blk = &sf->Blocks.New(); Blk->BlockLine = Line; Blk->Start = Cur; } // Scan till the end of line... while (*Cur && *Cur != '\r' && *Cur != '\n') { if (Cur[0] == '/' && Cur[1] == '*') { // Process multi-line comment... for (Cur += 2; *Cur; ) { if (*Cur == '\r' || *Cur == '\n') { SkipNewLine(Cur); } else if (Cur[0] == '*' && Cur[1] == '/') { Cur += 2; break; } else Cur++; } } else { Cur++; } } // Skip over new line... SkipNewLine(Cur); } } if (Blk) { Blk->Len = Cur - Blk->Start; } // sf->DumpBlocks(); return true; } GSourceFile *GCppParserWorker::ParseCpp(const char *Path) { GSourceFile *sf = NULL; for (int i=0; iPath && !strcmp(s->Path, Path)) { sf = s; break; } } if (!sf) { sf = new GSourceFile; if (sf) { if (!sf->Read(Path)) { delete sf; return NULL; } Srcs.Add(sf); Preprocess(sf); } else return NULL; } bool IsExternC = false; char16 *t; PreprocessBlock *blk; while ((blk = sf->Current())) { if (blk->Type != SourceBlock) { if (!ParsePreprocessor(sf)) break; } else if (sf->Active) { bool Error = false; sf->Lex(*blk); while (!Error) { t = blk->NextToken(); if (!t) break; KeywordType kt = Keywords.Find(t); switch (kt) { case KwPreprocessor: { LgiAssert(!"We shouldn't see these here at all."); Error = true; break; } case KwUserType: { GSymbol *sym = ParseUserType(sf, t); if (sym) { if (sf->Active) { GAutoWString SymName = GetSymbolName(sym->Tokens, sym->Type == SymEnum); if (SymName) { if (CurrentScope()->Find(SymName)) { DeleteObj(sym); } else { CurrentScope()->Add(SymName, sym); } } else if (sym->Type == SymEnum) { // Unnamed enum static int Idx = 1; #ifdef _MSC_VER char16 Buf[256]; swprintf_s(Buf, CountOf(Buf), L"UnnamedEnum%i", Idx++); #else char Tmp[256]; sprintf_s(Tmp, CountOf(Tmp), "UnnamedEnum%i", Idx++); GAutoWString Buf(Utf8ToWide(Tmp)); #endif if (CurrentScope()->Find(SymName)) { DeleteObj(sym); } else { CurrentScope()->Add(Buf, sym); } } else { Msg(MsgError, "%s:%i - Failed to get symbol name.\n", _FL); DeleteObj(sym); } } else { DeleteObj(sym); } } if (sf->Current()) blk = sf->Current(); break; } default: { if (IsExternC && CmpToken(t, "}")) { if (sf->Active) IsExternC = false; } else if (CmpToken(t, "typedef")) { GSymbol *sym = ParseTypedef(sf); if (sym) { GAutoWString Name = GetSymbolName(sym->Tokens); if (Name) { GSymbol *existing = CurrentScope()->Find(Name); if (existing) { Msg(MsgError, "Symbol '%S' already exists.\n", Name.Get()); } else { CurrentScope()->Add(Name, sym); sym->File = sf->Path; sym->Line = blk->GetLine(); sym = NULL; } } DeleteObj(sym); } } else if (CmpToken(t, ";")) { } else { GSymbol *sym = ParseDecl(sf, t); if (sym) { if (sf->Active) { if (sym->Type == SymExternC) { IsExternC = true; delete sym; } else { GAutoWString Name = GetSymbolName(sym->Tokens); if (Name) { GSymbol *e = Scopes[0]->Find(Name); if (e) { DeleteObj(sym); } else { Scopes[0]->Add(Name, sym); } } else { Msg(MsgError, "%s:%i - Failed to get name of decl.\n", _FL); DeleteObj(sym); } } } else { DeleteObj(sym); } } if (sf->Current()) blk = sf->Current(); } break; } } } sf->Next(); } else { sf->Next(); } } return sf; } GSymbol *GCppParserWorker::ParseDecl(GSourceFile *sf, char16 *t) { PreprocessBlock *blk = sf->Current(); int ExternC = CmpToken(t, "extern") ? 1 : 0; GSymbol *sym = new GSymbol(_FL); sym->Type = SymVariable; sym->File = sf->Path; sym->Line = blk->GetLine(); if (CmpToken(t, "virtual")) sym->Type = SymFunction; if (CmpToken(t, "friend")) sym->FriendDecl = true; else sym->Tokens.Add(t); GArray Temp; if (CmpToken(t, "template")) { Temp.Add(t); while ((t = blk->NextToken())) { Temp.Add(t); if (CmpToken(t, ">")) break; } if (!t) blk = sf->Next(); } LoopState Loop = LsLooping; int ScopeDepth = 0; while (Loop == LsLooping && blk) { if (blk->Type != SourceBlock) { // if (stristr(sf->Path, "GDragAndDrop.cpp") && blk->BlockLine == if (!ParsePreprocessor(sf)) { Loop = LsError; break; } blk = sf->Current(); } else if (!sf->Active) { blk = sf->Next(); } else { sf->Lex(*blk); while (Loop == LsLooping) { t = blk->NextToken(); if (!t) { blk = sf->Next(); break; } if (CmpToken(t, "{")) { if (ExternC == 2 && CmpToken(t, "{")) { sym->Type = SymExternC; Loop = LsFinished; break; } ScopeDepth++; } else if (CmpToken(t, "}")) { if (ScopeDepth <= 0) { Loop = LsError; break; } if (--ScopeDepth <= 0) { Loop = LsFinished; break; } } else if (ScopeDepth == 0) { KeywordType kt = Keywords.Find(t); if (kt == KwUserType && sym->Type != SymFunction) { GSymbol *ut = ParseUserType(sf, t); if (ut) ut->FriendDecl = sym->FriendDecl; DeleteObj(sym); sym = ut; Loop = LsFinished; if (sf->Current()) blk = sf->Current(); break; } if (ExternC > 0) { if (ExternC == 1 && CmpToken(t, "\"C\"")) { ExternC++; } else if (ExternC == 2 && CmpToken(t, "{")) { sym->Type = SymExternC; Loop = LsFinished; break; } else { ExternC = 0; } } if (CmpToken(t, ";")) { Loop = LsFinished; break; } if (CmpToken(t, "(")) { sym->Type = SymFunction; } sym->Tokens.Add(t); } } } } return sym; } GSymbol *GCppParserWorker::ParseTypedef(GSourceFile *sf) { GSymbol *sym = new GSymbol(_FL); sym->Type = SymTypedef; PreprocessBlock *blk = sf->Current(); char16 *t = blk->NextToken(); if (t == NULL) { delete sym; return NULL; } KeywordType kt = Keywords.Find(t); if (kt == KwUserType) { DeleteObj(sym); return ParseUserType(sf, t); } sym->Tokens.Add(t); while ((t = blk->NextToken())) { if (CmpToken(t, ";")) break; sym->Tokens.Add(t); } return sym; } GSymbol *GCppParserWorker::ParseUserType(GSourceFile *sf, char16 *t) { if (!sf || !t) return NULL; GSymbol *sym = new GSymbol(_FL); int InScope = 0; bool InParentList = false; bool Finished = false; if (CmpToken(t, "union")) sym->Type = SymUnion; else if (CmpToken(t, "class")) sym->Type = SymClass; else if (CmpToken(t, "struct")) sym->Type = SymStruct; else if (CmpToken(t, "enum")) sym->Type = SymEnum; else { delete sym; return NULL; } PreprocessBlock *blk = sf->Current(); sym->Tokens.Add(t); sym->File = sf->Path; sym->Line = blk->GetLine(); while (blk && !Finished) { if (blk->Type != SourceBlock) { if (!ParsePreprocessor(sf)) Finished = true; blk = sf->Current(); } else if (!sf->Active) { blk = sf->Next(); } else { sf->Lex(*blk); while ((t = blk->NextToken())) { if (CmpToken(t, "{")) { InScope++; } else if (CmpToken(t, "}")) { ProcessEndBracket: if (InScope > 0) { InScope--; } else { LgiAssert(!"End scope mis-match."); Finished = true; break; } } else if (CmpToken(t, ":")) { if (InScope == 0) { InParentList = true; } } else if (CmpToken(t, ";")) { if (!InScope) { Finished = true; break; } } else { KeywordType kt = Keywords.Find(t); if (kt == KwVisibility) { } else if (kt == KwPreprocessor) { LgiAssert(0); } else if (kt == KwUserType) { GSymbol *cls = ParseUserType(sf, t); if (cls) { if (sf->Active) { sym->Add(cls); } else { DeleteObj(cls); } } if (sf->Current()) blk = sf->Current(); } else if (InScope > 0) { if (sym->Type == SymEnum) { GSymbol *def = new GSymbol(_FL); if (def) { bool GotEquals = false; def->File = sf->Path; def->Line = blk->GetLine(); while ((t = blk->NextToken())) { if (CmpToken(t, ",")) break; if (CmpToken(t, "}")) { sym->Add(def); goto ProcessEndBracket; } if (CmpToken(t, "=")) GotEquals = true; else if (GotEquals) def->Tokens.Add(t); } sym->Add(def); } else { Msg(MsgError, "%s:%i - Failed to create defn.\n", _FL); } } else { GSymbol *def = ParseDecl(sf, t); if (def) { if (sf->Active && !def->FriendDecl) { sym->Add(def); } else { DeleteObj(def); } } else { Msg(MsgError, "%s:%i - Failed to parse defn.\n", _FL); } if (sf->Current()) blk = sf->Current(); } } else if (InParentList) { } else { sym->Tokens.Add(t); } } } if (!Finished) blk = sf->Next(); } } return sym; } char16 *FindEol(char16 *s) { char16 *LastNonWhite = NULL; while (*s) { if (!strchr(WhiteSpace, *s)) LastNonWhite = s; else if (*s == '\n') { if (!LastNonWhite || *LastNonWhite != '\\') break; } s++; } return s; } bool HasNonWhiteSpace(char16 *Start, char16 *End) { while (*Start && Start < End) { if (Start[0] == '/' && Start[1] == '/') { // Single line comments don't contribute to non-whitespace for (Start += 2; Start= End) return false; else continue; } else if (Start[0] == '/' && Start[1] == '*') { // Start of multi-line comment that doesn't contribute to non-whitespace for (Start += 2; Start= End) return false; else continue; } if (!strchr(" \t\r", *Start)) { return true; } Start++; } return false; } bool GCppParserWorker::ParsePreprocessor(GSourceFile *sf) { char16 *t; PreprocessBlock *blk = sf->Current(); if (blk->Type == SourceBlock) { LgiAssert(0); return false; } bool Status = true; sf->Lex(*blk); /* LgiTrace("%s:%i - %s, stack=%i\n", sf->Path.Get(), blk->BlockLine, PreprocessSymbolNames[blk->Type], sf->Stack.Length()); */ switch (blk->Type) { case HashIf: { PreprocessState &ps = sf->Stack.New(); ps.ParentIsActive = sf->Active; ps.IfBranchClaimed = Evaluate(blk->Tokens) != 0; if (sf->Active) sf->Active = ps.IfBranchClaimed; break; } case HashElif: { PreprocessState &ps = sf->Stack.Last(); if (!ps.IfBranchClaimed) { ps.IfBranchClaimed = Evaluate(blk->Tokens) != 0; if (sf->Active) sf->Active = ps.IfBranchClaimed; } else { sf->Active = false; } break; } case HashIfndef: { t = blk->NextToken(); if (!t) { Status = false; break; } GSymbol *def = Resolve(t); PreprocessState &ps = sf->Stack.New(); ps.ParentIsActive = sf->Active; ps.IfBranchClaimed = def == NULL || ( def->Type != SymDefineValue && def->Type != SymDefineFunction ); if (sf->Active) sf->Active = ps.IfBranchClaimed; break; } case HashIfdef: { t = blk->NextToken(); if (!t) { Status = false; break; } GSymbol *def = Resolve(t); PreprocessState &ps = sf->Stack.New(); ps.ParentIsActive = sf->Active; ps.IfBranchClaimed = def != NULL && ( def->Type == SymDefineValue || def->Type == SymDefineFunction ); if (sf->Active) sf->Active = ps.IfBranchClaimed; break; } case HashElse: { PreprocessState &ps = sf->Stack.Last(); if (!ps.IfBranchClaimed) { ps.IfBranchClaimed = true; if (ps.ParentIsActive) sf->Active = true; } else { sf->Active = false; } break; } case HashEndif: { LgiAssert(sf->Stack.Length() > 0); PreprocessState &ps = sf->Stack.Last(); sf->Active = ps.ParentIsActive; sf->Stack.Length(sf->Stack.Length() - 1); break; } case HashPragma: case HashError: case HashWarning: { break; } case HashDefine: { t = blk->NextToken(); if (!t) { Status = false; break; } GSymbol *sym = NULL; if (sf->Active) { sym = Scopes[0]->Define(t, SymDefineValue, _FL); sym->File = sf->Path; sym->Line = blk->BlockLine; if (blk->Tokens.Length() > 1) { sym->Tokens = blk->Tokens; sym->Tokens.DeleteAt(0, true); } else { sym->Tokens.Length(0); } if (sym->Tokens.Length() > 0 && CmpToken(sym->Tokens[0], "(")) sym->Type = SymDefineFunction; } break; } case HashUndef: { t = blk->NextToken(); if (!t) { Status = false; break; } GSymbol *s = Scopes[0]->Find(t); if (s) { Scopes[0]->Delete(t); delete s; } break; } case HashInclude: { // Include a file t = blk->NextToken(); if (!t) { Status = false; break; } GAutoString FileName8(WideToUtf8(t)); if (!FileName8) { Status = false; break; } // Resolve filename char p[MAX_PATH]; bool Exists = false; char *IncPath = NULL; if (LgiIsRelativePath(FileName8)) { LgiMakePath(p, sizeof(p), sf->Path, ".."); LgiMakePath(p, sizeof(p), p, FileName8); if ((Exists = FileExists(p))) IncPath = p; } if (!Exists) { if ((IncPath = FindInclude(FileName8))) Exists = true; } if (Exists) { ParseCpp(IncPath); } break; } default: { LgiAssert(!"Impl me"); Status = false; break; } } sf->Next(); return Status; } void GCppParserWorker::DoWork(WorkUnit *wk) { w.Reset(wk); if (w->SearchTerm) { // Do search... } else if (w->Source.Length()) { // Parse source code... for (int i=0; iSource.Length(); i++) { char *ext = LgiGetExtension(w->Source[i]); if (!ext) continue; if (!stricmp(ext, "c") || !stricmp(ext, "cpp")) { InitScopes(); uint64 Start = LgiCurrentTime(); if (!stricmp(ext, "cpp")) { Scopes[0]->Define((char16*)sCpp, SymDefineValue, _FL); } ParseCpp(w->Source[i]); uint64 Time = LgiCurrentTime() - Start; #if 0 char16 *k; for (GSymbol *s = Scopes[0]->First(&k); s; s = Scopes[0]->Next(&k)) { LgiTrace(" %S = %p\n", k, s); } #endif LgiTrace("%i of %i: %s, %i syms, %i ms\n", i, w->Source.Length(), w->Source[i].Get(), Scopes[0]->Length(), (int)Time); } else { // No parser impl yet } Srcs.DeleteObjects(); } } else LgiAssert(!"Unknown work unit type."); } GCppParser::GCppParser() { d = new GCppParserPriv; } GCppParser::~GCppParser() { delete d; } void GCppParser::ParseCode ( GArray &IncludePaths, GArray &PreDefines, GArray &Source ) { WorkUnit *w = new WorkUnit(IncludePaths, PreDefines, Source); if (w) { if (!d->Worker) d->Worker.Reset(new GCppParserWorker(d)); d->Worker->DoWork(w); } } void GCppParser::Search(const char *Str, SearchResultsCb Callback, void *CallbackData) { WorkUnit *w = new WorkUnit(Str); if (w) { if (!d->Worker) d->Worker.Reset(new GCppParserWorker(d)); d->Worker->DoWork(w); } } diff --git a/src/common/Coding/GScriptCompiler.cpp b/src/common/Coding/GScriptCompiler.cpp --- a/src/common/Coding/GScriptCompiler.cpp +++ b/src/common/Coding/GScriptCompiler.cpp @@ -1,3748 +1,3748 @@ /// \file #include "Lgi.h" #include "GScripting.h" #include "GScriptingPriv.h" #include "GLexCpp.h" #include "GString.h" #include "GToken.h" #define GetTok(c) ((c) < Tokens.Length() ? Tokens[c] : NULL) #define GetTokType(c) ((c) < Tokens.Length() ? ExpTok.Find(Tokens[c]) : TNone) #define GV_VARIANT GV_MAX const char *sDebugger = "Debugger"; int GFunctionInfo::_Infos = 0; enum GTokenType { #define _(type, str) T##type, #include "GTokenType.h" #undef _ }; struct LinkFixup { int Tok; size_t Offset; int Args; GFunctionInfo *Func; }; struct Node { typedef GArray NodeExp; struct VariablePart { GVariant Name; NodeExp Array; bool Call; GArray Args; VariablePart() { Call = false; } ~VariablePart() { Args.DeleteObjects(); } }; // Heirarchy NodeExp Child; // One of the following are valid: GOperator Op; // -or- bool Constant; int Tok; GArray Lst; GTokenType ConstTok; // -or- GFunc *ContextFunc; GArray Args; // -or- GFunctionInfo *ScriptFunc; // -or- GArray Variable; // Used during building GVarRef Reg; GVarRef ArrayIdx; void Init() { Op = OpNull; ContextFunc = 0; ScriptFunc = 0; Constant = false; Tok = -1; ConstTok = TNone; Reg.Empty(); ArrayIdx.Empty(); } void SetOp(GOperator o, int t) { Init(); Op = o; Tok = t; } void SetConst(int t, GTokenType e) { Init(); Constant = true; Tok = t; ConstTok = e; } void SetConst(GArray &list_tokens, GTokenType e) { Init(); Constant = true; Lst = list_tokens; ConstTok = e; } void SetContextFunction(GFunc *m, int tok) { Init(); ContextFunc = m; Tok = tok; } void SetScriptFunction(GFunctionInfo *m, int tok) { Init(); ScriptFunc = m; Tok = tok; } void SetVar(char16 *Var, int t) { Init(); Variable[0].Name = Var; Tok = t; } bool IsVar() { return Variable.Length() > 0; } bool IsContextFunc() { return ContextFunc != 0; } bool IsScriptFunc() { return ScriptFunc != 0; } bool IsConst() { return Constant; } }; GCompiledCode::GCompiledCode() : Globals(SCOPE_GLOBAL), Debug(0, -1) { SysContext = NULL; UserContext = NULL; } GCompiledCode::GCompiledCode(GCompiledCode ©) : Globals(SCOPE_GLOBAL), Debug(0, -1) { *this = copy; } GCompiledCode::~GCompiledCode() { } GCompiledCode &GCompiledCode::operator =(const GCompiledCode &c) { Globals = c.Globals; ByteCode = c.ByteCode; Types = c.Types; Debug = c.Debug; Methods = c.Methods; FileName = c.FileName; Source = c.Source; return *this; } GFunctionInfo *GCompiledCode::GetMethod(const char *Name, bool Create) { for (unsigned i=0; iGetName(); if (!strcmp(Fn, Name)) return Methods[i]; } if (Create) { GAutoRefPtr n(new GFunctionInfo(Name)); if (n) { Methods.Add(n); return n; } } return 0; } int GCompiledCode::ObjectToSourceAddress(size_t ObjAddr) { return Debug.Find(ObjAddr); } const char *GCompiledCode::AddrToSourceRef(size_t ObjAddr) { static char Status[256]; size_t Addr = ObjAddr; int LineNum = ObjectToSourceAddress(Addr); // Search for the start of the instruction... while (Addr > 0 && LineNum < 0) LineNum = ObjectToSourceAddress(--Addr); char *Dir = FileName ? strrchr(FileName, DIR_CHAR) : NULL; size_t PathLen = Dir ? Dir - FileName : 0; if (LineNum >= 0) sprintf_s( Status, sizeof(Status), "%s:%i", FileName ? (PathLen > 24 ? Dir + 1 : FileName.Get()) : "(untitled)", LineNum); else sprintf_s(Status, sizeof(Status), "0x%x", (int)ObjAddr); return Status; } GVariant *GCompiledCode::Set(const char *Name, GVariant &v) { int i = Globals.Var(Name, true); if (i >= 0) { Globals[i] = v; return &Globals[i]; } return 0; } template void UnEscape(T *t) { T *i = t, *o = t; while (*i) { if (*i == '\\') { i++; switch (*i) { case '\\': *o++ = '\\'; i++; break; case 'n': *o++ = '\n'; i++; break; case 'r': *o++ = '\r'; i++; break; case 't': *o++ = '\t'; i++; break; case 'b': *o++ = '\b'; i++; break; } } else *o++ = *i++; } *o++ = 0; } class TokenRanges { GArray FileNames; struct Range { int Start, End; ssize_t File; int Line; }; GArray Ranges; char fl[MAX_PATH + 32]; public: TokenRanges() { Empty(); } void Empty() { Ranges.Length(0); } ssize_t Length() { return Ranges.Length(); } /// Gets the file/line at a given token const char *operator [](ssize_t Tok) { Range *r = NULL; for (unsigned i=0; i= rng->Start && Tok <= rng->End) { r = rng; break; } } if (!r) r = &Ranges.Last(); if (r->File >= (ssize_t)FileNames.Length()) { LgiAssert(!"Invalid file index."); return "#err: invalid file index"; } else { sprintf_s(fl, sizeof(fl), "%s:%i", FileNames[r->File].Get(), r->Line); } return fl; } const char *GetLine(int Line) { if (Ranges.Length() > 0) { Range &r = Ranges.Last(); if (r.File < (ssize_t)FileNames.Length()) sprintf_s(fl, sizeof(fl), "%s:%i", FileNames[r.File].Get(), Line); else return "#err: invalid file index."; } else { sprintf_s(fl, sizeof(fl), "$unknown:%i", Line); } return fl; } ssize_t GetFileIndex(const char *FileName) { for (unsigned i=0; iFile != FileId || r->Line != Line) { // Start a new range... r = &Ranges.New(); r->Start = r->End = TokIndex; r->File = FileId; r->Line = Line; } else { r->End = TokIndex; } } }; /// Scripting language compiler implementation class GCompilerPriv : public GCompileTools, public GScriptUtils { LHashTbl, GVariantType> Types; size_t JumpLoc; public: GScriptContext *SysCtx; GScriptContext *UserCtx; GCompiledCode *Code; GStream *Log; GArray Tokens; TokenRanges Lines; char16 *Script; LHashTbl, GFunc*> Methods; int Regs; GArray Scopes; GArray Fixups; LHashTbl, char16*> Defines; LHashTbl, GTokenType> ExpTok; GDom *ScriptArgs; GVarRef ScriptArgsRef; bool ErrShowFirstOnly; GArray ErrLog; #ifdef _DEBUG GArray RegAllocators; #endif GCompilerPriv() { ErrShowFirstOnly = true; SysCtx = NULL; UserCtx = NULL; Code = 0; Log = 0; Script = 0; Regs = 0; ScriptArgs = NULL; ScriptArgsRef.Empty(); #define LNULL NULL #define _(type, str) if (str) ExpTok.Add(L##str, T##type); #include "GTokenType.h" #undef _ #undef LNULL Types.Add("int", GV_INT32); Types.Add("short", GV_INT32); Types.Add("long", GV_INT32); Types.Add("int8", GV_INT32); Types.Add("int16", GV_INT32); Types.Add("int32", GV_INT32); Types.Add("int64", GV_INT64); Types.Add("uint8", GV_INT32); Types.Add("uint16", GV_INT32); Types.Add("uint32", GV_INT32); Types.Add("uint64", GV_INT64); Types.Add("bool", GV_BOOL); Types.Add("boolean", GV_BOOL); Types.Add("double", GV_DOUBLE); Types.Add("float", GV_DOUBLE); Types.Add("char", GV_STRING); Types.Add("GDom", GV_DOM); Types.Add("void", GV_VOID_PTR); Types.Add("LDateTime", GV_DATETIME); Types.Add("GHashTable", GV_HASHTABLE); Types.Add("GOperator", GV_OPERATOR); Types.Add("GView", GV_GVIEW); Types.Add("GMouse", GV_GMOUSE); Types.Add("GKey", GV_GKEY); Types.Add("GVariant", GV_VARIANT); // Types.Add("binary", GV_BINARY); // Types.Add("List", GV_LIST); // Types.Add("GDom&", GV_DOMREF); } ~GCompilerPriv() { Empty(); } void Empty() { SysCtx = NULL; UserCtx = NULL; DeleteObj(Code); Log = 0; Tokens.DeleteArrays(); Lines.Empty(); DeleteArray(Script); Defines.DeleteArrays(); } /// Prints the error message bool OnError ( /// The line number (less than 0) or Token index (greater than 0) int LineOrTok, /// The format for the string (printf) const char *Msg, /// Variable argument list ... ) { if (!ErrShowFirstOnly || ErrLog.Length() == 0) { char Buf[512]; va_list Arg; va_start(Arg, Msg); #ifndef WIN32 #define _vsnprintf vsnprintf #endif vsprintf_s(Buf, sizeof(Buf), Msg, Arg); Log->Print("%s - Error: %s\n", LineOrTok < 0 ? Lines.GetLine(-LineOrTok) : Lines[LineOrTok], Buf); va_end(Arg); ErrLog.New() = Buf; } return false; // Always return false to previous caller } void DebugInfo(int Tok) { const char *Line = Lines[Tok]; if (Line) { const char *c = strrchr(Line, ':'); if (c) { Code->Debug.Add(Code->ByteCode.Length(), ::atoi(c+1)); } } } /// Assemble a zero argument instruction - bool Asm0(int Tok, uint8 Op) + bool Asm0(int Tok, uint8_t Op) { DebugInfo(Tok); ssize_t Len = Code->ByteCode.Length(); if (Code->ByteCode.Length(Len + 1)) { GPtr p; p.u8 = &Code->ByteCode[Len]; *p.u8++ = Op; } else return false; return true; } /// Assemble one arg instruction - bool Asm1(int Tok, uint8 Op, GVarRef a) + bool Asm1(int Tok, uint8_t Op, GVarRef a) { DebugInfo(Tok); ssize_t Len = Code->ByteCode.Length(); if (Code->ByteCode.Length(Len + 5)) { GPtr p; p.u8 = &Code->ByteCode[Len]; *p.u8++ = Op; *p.r++ = a; } else return false; return true; } /// Assemble two arg instruction - bool Asm2(int Tok, uint8 Op, GVarRef a, GVarRef b) + bool Asm2(int Tok, uint8_t Op, GVarRef a, GVarRef b) { DebugInfo(Tok); ssize_t Len = Code->ByteCode.Length(); if (Code->ByteCode.Length(Len + 9)) { GPtr p; p.u8 = &Code->ByteCode[Len]; *p.u8++ = Op; *p.r++ = a; *p.r++ = b; } else return false; return true; } /// Assemble three arg instruction - bool Asm3(int Tok, uint8 Op, GVarRef a, GVarRef b, GVarRef c) + bool Asm3(int Tok, uint8_t Op, GVarRef a, GVarRef b, GVarRef c) { DebugInfo(Tok); ssize_t Len = Code->ByteCode.Length(); if (Code->ByteCode.Length(Len + 1 + (sizeof(GVarRef) * 3) )) { GPtr p; p.u8 = &Code->ByteCode[Len]; *p.u8++ = Op; *p.r++ = a; *p.r++ = b; *p.r++ = c; } else return false; return true; } /// Assemble four arg instruction - bool Asm4(int Tok, uint8 Op, GVarRef a, GVarRef b, GVarRef c, GVarRef d) + bool Asm4(int Tok, uint8_t Op, GVarRef a, GVarRef b, GVarRef c, GVarRef d) { DebugInfo(Tok); ssize_t Len = Code->ByteCode.Length(); if (Code->ByteCode.Length(Len + 1 + (sizeof(GVarRef) * 4) )) { GPtr p; p.u8 = &Code->ByteCode[Len]; *p.u8++ = Op; if (!a.Valid()) AllocNull(a); *p.r++ = a; if (!b.Valid()) AllocNull(b); *p.r++ = b; if (!c.Valid()) AllocNull(c); *p.r++ = c; if (!d.Valid()) AllocNull(d); *p.r++ = d; } else return false; return true; } /// Assemble 'n' length arg instruction - bool AsmN(int Tok, uint8 Op, GArray &Args) + bool AsmN(int Tok, uint8_t Op, GArray &Args) { DebugInfo(Tok); ssize_t Len = Code->ByteCode.Length(); if (Code->ByteCode.Length(Len + 1 + (sizeof(GVarRef) * Args.Length()) )) { GPtr p; p.u8 = &Code->ByteCode[Len]; *p.u8++ = Op; for (unsigned i=0; iGetIncludeFile(v.Str())); if (IncCode) { Lex(IncCode, v.Str()); } else { DeleteArray(t); return OnError(-Line, "Ctx failed to include '%s'", v.Str()); } } else { if (LgiIsRelativePath(v.Str())) { char p[MAX_PATH]; LgiMakePath(p, sizeof(p), FileName, ".."); LgiMakePath(p, sizeof(p), p, v.Str()); v = p; } if (FileExists(v.Str())) { IncCode.Reset(ReadTextFile(v.Str())); if (IncCode) Lex(IncCode, v.Str()); else { DeleteArray(t); return OnError(-Line, "Couldn't read '%s'", v.Str()); } } else { DeleteArray(t); return OnError(-Line, "Couldn't include '%s'", v.Str()); } } } else { OnError(-Line, "No file for #include."); } } else if (!StrnicmpW(t + 1, sDefine, Len = StrlenW(sDefine))) { GAutoWString Name(LexCpp(s, LexStrdup)); if (Name && IsAlpha(*Name)) { char16 *Start = s; while (*Start && strchr(WhiteSpace, *Start)) Start++; char16 *Eol = StrchrW(Start, '\n'); if (!Eol) Eol = Start + StrlenW(Start); while (Eol > Start && strchr(WhiteSpace, Eol[-1])) Eol--; Defines.Add(Name, NewStrW(Start, Eol - Start)); s = Eol; } } DeleteArray(t); continue; } char16 *DefineValue; if (IsAlpha(*t) && (DefineValue = Defines.Find(t))) { char16 *Def = DefineValue, *f; while ((f = LexCpp(Def, LexStrdup))) { Lines.Add((int)Tokens.Length(), FileIndex, Line); Tokens.Add(f); } DeleteArray(t); } else { Lines.Add((int)Tokens.Length(), FileIndex, Line); Tokens.Add(t); } } // end of "while (t = LexCpp)" loop if (!Script) { Script = w; } else { DeleteArray(w); } return true; } /// Create a null var ref void AllocNull(GVarRef &r) { r.Scope = SCOPE_GLOBAL; if (Code->Globals.NullIndex < 0) Code->Globals.NullIndex = (int)Code->Globals.Length(); r.Index = Code->Globals.NullIndex; Code->Globals[r.Index].Type = GV_NULL; } /// Allocate a variant and ref GVariant *PreAllocVariant(GVarRef &r) { r.Scope = SCOPE_GLOBAL; r.Index = (int)Code->Globals.Length(); return &Code->Globals[r.Index]; } /// Allocate a constant double void AllocConst(GVarRef &r, double d) { r.Scope = SCOPE_GLOBAL; if (Code->Globals.Length()) { // Check for existing int GVariant *p = &Code->Globals[0]; GVariant *e = p + Code->Globals.Length(); while (p < e) { if (p->Type == GV_DOUBLE && p->Value.Dbl == d) { r.Index = (int) (p - &Code->Globals[0]); return; } p++; } } r.Index = (int)Code->Globals.Length(); Code->Globals[r.Index] = d; } /// Allocate a constant bool void AllocConst(GVarRef &r, bool b) { r.Scope = SCOPE_GLOBAL; if (Code->Globals.Length()) { // Check for existing int GVariant *p = &Code->Globals[0]; GVariant *e = p + Code->Globals.Length(); while (p < e) { if (p->Type == GV_BOOL && p->Value.Bool == b) { r.Index = (int)(p - &Code->Globals[0]); return; } p++; } } r.Index = (int)Code->Globals.Length(); Code->Globals[r.Index] = b; } /// Allocate a constant int void AllocConst(GVarRef &r, int64 i) { r.Scope = SCOPE_GLOBAL; GVariantType Type = i <= INT_MAX && i >= INT_MIN ? GV_INT32 : GV_INT64; if (Code->Globals.Length()) { // Check for existing int GVariant *p = &Code->Globals[0]; GVariant *e = p + Code->Globals.Length(); while (p < e) { if (p->Type == Type) { if (Type == GV_INT32 && p->Value.Int == i) { r.Index = (int) (p - &Code->Globals[0]); return; } else if (Type == GV_INT64 && p->Value.Int64 == i) { r.Index = (int) (p - &Code->Globals[0]); return; } } p++; } } // Allocate new global r.Index = (int)Code->Globals.Length(); if (Type == GV_INT32) Code->Globals[r.Index] = (int32)i; else Code->Globals[r.Index] = i; } void AllocConst(GVarRef &r, int i) { AllocConst(r, (int64)i); } /// Allocate a constant string void AllocConst(GVarRef &r, char *s, ssize_t len = -1) { LgiAssert(s != 0); if (len < 0) len = strlen(s); r.Scope = SCOPE_GLOBAL; r.Index = (int)Code->Globals.Length(); for (unsigned i=0; iGlobals.Length(); i++) { if (Code->Globals[i].Type == GV_STRING) { char *c = Code->Globals[i].Str(); if (*s == *c && strcmp(s, c) == 0) { r.Index = i; return; } } } GVariant &v = Code->Globals[r.Index]; v.Type = GV_STRING; if ((v.Value.String = NewStr(s, len))) { UnEscape(v.Value.String); } } /// Allocate a constant wide string void AllocConst(GVarRef &r, char16 *s, ssize_t len) { LgiAssert(s != 0); char *utf = WideToUtf8(s, len); if (!utf) utf = NewStr(""); r.Scope = SCOPE_GLOBAL; r.Index = (int)Code->Globals.Length(); for (unsigned i=0; iGlobals.Length(); i++) { if (Code->Globals[i].Type == GV_STRING) { char *c = Code->Globals[i].Str(); if (*utf == *c && strcmp(utf, c) == 0) { r.Index = i; DeleteArray(utf); return; } } } GVariant &v = Code->Globals[r.Index]; v.Type = GV_STRING; if ((v.Value.String = utf)) { UnEscape(v.Value.String); } } /// Find a variable by name, creating it if needed GVarRef FindVariable(GVariant &Name, bool Create) { GVarRef r = {0, -1}; // Look for existing variable... ssize_t i; for (i=Scopes.Length()-1; i>=0; i--) { r.Index = Scopes[i]->Var(Name.Str(), false); if (r.Index >= 0) { r.Scope = Scopes[i]->Scope; return r; } } // Create new variable on most recent scope i = Scopes.Length() - 1; r.Index = Scopes[i]->Var(Name.Str(), Create); if (r.Index >= 0) { r.Scope = Scopes[i]->Scope; } return r; } /// Build asm to assign a var ref bool AssignVarRef(Node &n, GVarRef &Value) { /* Examples and what their assembly should look like: a = Value Assign a <- Value a[b] = Value R0 = AsmExpression(b); ArraySet a[R0] <- Value a[b].c = Value R0 = AsmExpression(b); R0 = ArrayGet a[R0] DomSet(R0, "c", Null, Value) a[b].c[d] R0 = AsmExpression(b); R0 = ArrayGet a[R0]; R1 = AsmExpression(d); R0 = DomGet(R0, "c", R1); ArraySet R0[R1] = Value a.b[c].d = Value R1 = AsmExpression(c); R0 = DomGet(a, "b", R1); DomSet(R1, "d", Null, Value) // resolve initial array if (parts > 1 && part[0].array) { // cur = ArrayGet(part[0].var, part[0].array) } else { cur = part[0] } // dom loop over loop (p = 0 to parts - 2) { if (part[p+1].array) arr = exp(part[p+1].array) else arr = null cur = DomGet(cur, part[p+1].var, arr) } // final part if (part[parts-1].arr) { arr = exp(part[parts-1].arr) ArraySet(cur, arr) } else { DomSet(cur, part[parts-1].var, null, value) } */ if (!Value.Valid()) { LgiAssert(!"Invalid value to assign.\n"); return false; } if (!n.IsVar()) { LgiAssert(!"Target must be a variable."); return false; } // Gets the first part of the variable. GVarRef Cur = FindVariable(n.Variable[0].Name, true); if (!Cur.Valid()) return false; if (n.Variable.Length() > 1) { // Do any initial array dereference if (n.Variable[0].Array.Length()) { // Assemble the array index's expression into 'Idx' GVarRef Idx; Idx.Empty(); if (!AsmExpression(&Idx, n.Variable[0].Array)) return OnError(n.Tok, "Error creating bytecode for array index."); GVarRef Dst = Cur; if (!Dst.IsReg()) { AllocReg(Dst, _FL); } // Assemble array load instruction Asm3(n.Tok, IArrayGet, Dst, Cur, Idx); Cur = Dst; // Cleanup DeallocReg(Idx); } // Do all the DOM "get" instructions unsigned p; for (p=0; pOwnStr(NewStrW(t + 1, Len - 2)); } else if (StrchrW(t, '.')) { // double *v = atof(t); } else if (t[0] == '0' && tolower(t[1]) == 'x') { // hex integer *v = htoi(t + 2); } else { // decimal integer int64 i = Atoi(t); if (i <= INT_MAX && i >= INT_MIN) *v = (int32)i; else *v = i; } return true; } /// Convert a token stream to a var ref bool TokenToVarRef(Node &n, GVarRef *&LValue) { if (n.Reg.Valid()) { if (LValue && n.Reg != *LValue) { // Need to assign to LValue LgiAssert(*LValue != n.Reg); Asm2(n.Tok, IAssign, *LValue, n.Reg); } } else { if (n.IsVar()) { // Variable unsigned p = 0; bool HasScriptArgs = Scopes.Length() <= 1 && ScriptArgs != NULL; GVarRef v = FindVariable(n.Variable[p].Name, /*!HasScriptArgs ||*/ LValue != NULL); if (v.Index < 0) { if (HasScriptArgs) { if (AllocReg(v, _FL)) { char16 *VarName = GetTok((unsigned)n.Tok); if (!ScriptArgsRef.Valid()) { // Setup the global variable to address the script argument variable ScriptArgsRef.Scope = SCOPE_GLOBAL; ScriptArgsRef.Index = (int)Code->Globals.Length(); GVariant &v = Code->Globals[ScriptArgsRef.Index]; v.Type = GV_DOM; v.Value.Dom = ScriptArgs; } GVarRef Name, Null; AllocConst(Name, VarName, -1); AllocNull(Null); Asm4(n.Tok, IDomGet, v, ScriptArgsRef, Name, Null); } else return false; } else { Node::VariablePart &vp = n.Variable[p]; return OnError(n.Tok, "Undefined variable: %s", vp.Name.Str()); } } else { if (v.Scope == SCOPE_OBJECT) { // We have to load the variable into a register GVarRef Reg, ThisPtr, MemberIndex, Null; if (!AllocReg(Reg, _FL)) return OnError(n.Tok, "Couldn't alloc register."); ThisPtr.Scope = SCOPE_LOCAL; ThisPtr.Index = 0; AllocConst(MemberIndex, v.Index); AllocNull(Null); Asm4(n.Tok, IDomGet, Reg, ThisPtr, MemberIndex, Null); v = Reg; // Object variable now in 'Reg' } if (n.ConstTok == TTypeId) { if (!v.IsReg()) { // Because we are casting to it's DOM ptr, // make sure it's a register first so we don't lose the // actual variable. GVarRef reg; if (!AllocReg(reg, _FL)) return OnError(n.Tok, "Couldn't alloc register."); LgiAssert(reg != v); Asm2(n.Tok, IAssign, reg, v); v = reg; } // Casting to DOM will give as access to the type info for a GCustomType. // This currently doesn't work with other types :( Asm1(n.Tok, ICast, v); Code->ByteCode.Add(GV_DOM); } } n.Reg = v; LValue = NULL; LgiAssert(v.Scope != SCOPE_OBJECT); // Does it have an array deref? if (n.Variable[p].Array.Length()) { // Evaluate the array indexing expression if (!AsmExpression(&n.ArrayIdx, n.Variable[p].Array)) { return OnError(n.Tok, "Error creating byte code for array index."); } // Do we need to create code to load the value from the array? GVarRef Val; if (AllocReg(Val, _FL)) { Asm3(n.Tok, IArrayGet, Val, n.Reg, n.ArrayIdx); n.Reg = Val; } else return OnError(n.Tok, "Error allocating register."); } // Load DOM parts... for (++p; p Args; Node::VariablePart &Part = n.Variable[p]; char *nm = Part.Name.Str(); AllocConst(Name, nm, strlen(nm)); if (Part.Array.Length()) { if (!AsmExpression(&Arr, Part.Array)) { return OnError(n.Tok, "Can't assemble array expression."); } } else if (Part.Call) { for (unsigned i=0; i Call; Call[0] = Dst; Call[1] = n.Reg; Call[2] = Name; // This must always be a global, the decompiler requires access // to the constant to know how many arguments to print. And it // can only see the globals. AllocConst(Call[3], (int)Args.Length()); Call.Add(Args); AsmN(n.Tok, IDomCall, Call); } else { Asm4(n.Tok, IDomGet, Dst, n.Reg, Name, Arr); } n.Reg = Dst; } } else if (n.IsConst()) { // Constant switch (n.ConstTok) { case TTrue: { AllocConst(n.Reg, true); break; } case TFalse: { AllocConst(n.Reg, false); break; } case TNull: { AllocNull(n.Reg); break; } default: { if (n.Lst.Length()) { // List/array constant GVariant *v = PreAllocVariant(n.Reg); if (v) { v->SetList(); for (unsigned i=0; i a(new GVariant); if (!ConvertStringToVariant(a, t)) break; v->Value.Lst->Insert(a.Release()); } } } else { char16 *t = Tokens[n.Tok]; if (*t == '\"' || *t == '\'') { // string ssize_t Len = StrlenW(t); AllocConst(n.Reg, t + 1, Len - 2); } else if (StrchrW(t, '.')) { // double AllocConst(n.Reg, atof(t)); } else if (t[0] == '0' && tolower(t[1]) == 'x') { // hex integer AllocConst(n.Reg, htoi(t + 2)); } else { // decimal integer AllocConst(n.Reg, Atoi(t)); } } break; } } LValue = NULL; } else if (n.IsContextFunc()) { // Method call, create byte codes to put func value into n.Reg GVarRef *OutRef; if (LValue) { OutRef = LValue; } else { if (!AllocReg(n.Reg, _FL)) { return OnError(n.Tok, "Can't allocate register for method return value."); } OutRef = &n.Reg; } DebugInfo(n.Tok); GArray a; for (unsigned i=0; iByteCode.Length(); ssize_t Size = 1 + sizeof(GFunc*) + sizeof(GVarRef) + 2 + (a.Length() * sizeof(GVarRef)); Code->ByteCode.Length(Len + Size); GPtr p; - uint8 *Start = &Code->ByteCode[Len]; + uint8_t *Start = &Code->ByteCode[Len]; p.u8 = Start; *p.u8++ = ICallMethod; *p.fn++ = n.ContextFunc; *p.r++ = *OutRef; *p.u16++ = (uint16) n.Args.Length(); for (unsigned i=0; iGetName(); if (*FnName == 'D' && !_stricmp(FnName, sDebugger)) { Asm0(n.Tok, IDebug); return true; } if (n.ScriptFunc->GetParams() != n.Args.Length()) { return OnError( n.Tok, "Wrong number of parameters: %i (%s expects %i)", n.Args.Length(), FnName, n.ScriptFunc->GetParams()); } // Call to a script function, create byte code to call function GVarRef *OutRef; if (LValue) { OutRef = LValue; } else { if (!AllocReg(n.Reg, _FL)) { return OnError(n.Tok, "Can't allocate register for method return value."); } OutRef = &n.Reg; } DebugInfo(n.Tok); GArray a; for (unsigned i=0; iByteCode.Length(); size_t Size = 1 + // instruction - sizeof(uint32) + // address of function + sizeof(uint32_t) + // address of function sizeof(uint16) + // size of frame sizeof(GVarRef) + // return value 2 + // number of args (a.Length() * sizeof(GVarRef)); // args Code->ByteCode.Length(Len + Size); GPtr p; - uint8 *Start = &Code->ByteCode[Len]; + uint8_t *Start = &Code->ByteCode[Len]; p.u8 = Start; *p.u8++ = ICallScript; if (n.ScriptFunc->StartAddr && n.ScriptFunc->FrameSize.Get()) { // Compile func address straight into code... *p.u32++ = n.ScriptFunc->StartAddr; *p.u16++ = *n.ScriptFunc->FrameSize.Get(); } else { // Add link time fix up LinkFixup &Fix = Fixups.New(); Fix.Tok = n.Tok; Fix.Args = (int)n.Args.Length(); Fix.Offset = p.u8 - &Code->ByteCode[0]; Fix.Func = n.ScriptFunc; *p.u32++ = 0; *p.u16++ = 0; } *p.r++ = *OutRef; *p.u16++ = (uint16) n.Args.Length(); for (unsigned i=0; i e(new Node::NodeExp); if (!e) return OnError(Cur, "Mem alloc error."); if (!Expression(Cur, *e)) return OnError(Cur, "Couldn't parse func call argument expression."); vp.Args.Add(e.Release()); t = GetTok(Cur); if (!t) return OnError(Cur, "Unexpected end of file."); if (!StricmpW(t, sComma)) Cur++; else if (!StricmpW(t, sEndRdBracket)) break; else return OnError(Cur, "Expecting ',', didn't get it."); } } t = GetTok(Cur+1); } // Check for DOM operator... if (StricmpW(t, sPeriod) == 0) { // Got Dom operator Cur += 2; t = GetTok(Cur); if (!t) return OnError(Cur, "Unexpected eof."); Var.Variable.New().Name = t; } else break; } return true; } /// Parse expression into a node tree - bool Expression(uint32 &Cur, GArray &n, int Depth = 0) + bool Expression(uint32_t &Cur, GArray &n, int Depth = 0) { if (Cur >= Tokens.Length()) return OnError(Cur, "Unexpected end of file."); char16 *t; bool PrevIsOp = true; while ((t = Tokens[Cur])) { GTokenType Tok = ExpTok.Find(t); if (Tok == TTypeId) { char16 *v; if (!DoTypeId(Cur, v)) return false; Node &Var = n.New(); t = v; Cur--; DoVariableNode(Cur, Var, t); Var.ConstTok = TTypeId; Cur++; } else if (Tok == TStartRdBracket) { Cur++; if (!Expression(Cur, n[n.Length()].Child, Depth + 1)) return false; PrevIsOp = false; } else if (Tok == TEndRdBracket) { if (Depth > 0) Cur++; break; } else if (Tok == TComma || Tok == TSemiColon) { break; } else if (Depth == 0 && Tok == TEndSqBracket) { break; } else if (Tok == TTrue || Tok == TFalse || Tok == TNull) { n.New().SetConst(Cur++, Tok); } else { GOperator o = IsOp(t, PrevIsOp); if (o != OpNull) { // Operator PrevIsOp = 1; n.New().SetOp(o, Cur); } else { PrevIsOp = 0; GVariant m; m = t; GFunc *f = Methods.Find(m.Str()); GFunctionInfo *sf = 0; char16 *Next; if (f) { Node &Call = n.New(); Call.SetContextFunction(f, Cur++); // Now parse arguments // Get the start bracket if ((t = GetTok(Cur))) { if (StricmpW(t, sStartRdBracket) == 0) Cur++; else return OnError(Cur, "Function missing '('"); } else return OnError(Cur, "No token."); // Parse the args as expressions while ((t = GetTok(Cur))) { GTokenType Tok = ExpTok.Find(t); if (Tok == TComma) { // Do nothing... Cur++; } else if (Tok == TEndRdBracket) { break; } else if (Tok == TSemiColon) { return OnError(Cur, "Unexpected ';'"); } else if (!Expression(Cur, Call.Args.New())) { return OnError(Cur, "Can't parse function argument."); } } } else if ( ( sf = Code->GetMethod ( m.Str(), (Next = GetTok(Cur+1)) != 0 && StricmpW(Next, sStartRdBracket) == 0 ) ) ) { Node &Call = n.New(); Call.SetScriptFunction(sf, Cur++); // Now parse arguments // Get the start bracket if ((t = GetTok(Cur))) { if (StricmpW(t, sStartRdBracket) == 0) Cur++; else return OnError(Cur, "Function missing '('"); } else return OnError(Cur, "No token."); // Parse the args as expressions while ((t = GetTok(Cur))) { if (StricmpW(t, sComma) == 0) { // Do nothing... Cur++; } else if (StricmpW(t, sEndRdBracket) == 0) { break; } else if (!Expression(Cur, Call.Args[Call.Args.Length()])) { return OnError(Cur, "Can't parse function argument."); } } } else if (IsAlpha(*t)) { // Variable... Node &Var = n.New(); if (!DoVariableNode(Cur, Var, t)) return false; } else if (*t == '\'' || *t == '\"' || LgiIsNumber(t)) { // Constant string or number n.New().SetConst(Cur, TLiteral); } else if (Tok == TStartCurlyBracket) { // List definition GArray Values; Cur++; int Index = 0; while ((t = Tokens[Cur])) { GTokenType Tok = ExpTok.Find(t); if (Tok == TComma) { Index++; } else if (Tok == TEndCurlyBracket) { break; } else if (*t == '\'' || *t == '\"' || LgiIsNumber(t)) { Values[Index] = Cur; } else { return OnError(Cur, "Unexpected token '%S' in list definition", t); } Cur++; } n.New().SetConst(Values, TLiteral); } else { // Unknown return OnError(Cur, "Unknown token '%S'", t); } } Cur++; } } return true; } /// Allocate a register (must be mirrored with DeallocReg) bool AllocReg(GVarRef &r, const char *file, int line) { for (int i=0; iPrint("CompileError:Register[%i] allocated by %s\n", n, a); } } } #endif return false; } /// Deallocate a register bool DeallocReg(GVarRef &r) { if (r.Scope == SCOPE_REGISTER && r.Index >= 0) { int Bit = 1 << r.Index; LgiAssert((Bit & Regs) != 0); Regs &= ~Bit; #ifdef _DEBUG RegAllocators[r.Index].Empty(); #endif } return true; } /// Count allocated registers int RegAllocCount() { int c = 0; for (int i=0; i &n) { GStringPipe e; for (unsigned i=0; iMethod.Get()); } else if (n[i].ScriptFunc) { e.Print("%s(...)", n[i].ScriptFunc->Name.Get()); } else { e.Print("#err#"); } } return e.NewStr(); } /// Creates byte code to evaluate an expression bool AsmExpression ( /// Where the result got stored GVarRef *Result, /// The nodes to create code for GArray &n, /// The depth of recursion int Depth = 0 ) { // Resolve any sub-expressions and store their values for (unsigned i = 0; i < n.Length(); i++) { if (!n[i].IsVar() && n[i].Child.Length()) { AllocReg(n[i].Reg, _FL); AsmExpression(&n[i].Reg, n[i].Child, Depth + 1); } } while (n.Length() > 1) { // Find which operator to handle first #ifdef _DEBUG size_t StartLength = n.Length(); #endif int OpIdx = -1; int Prec = -1; int Ops = 0; for (unsigned i=0; i n; if (Expression(Cur, n)) { bool Status = AsmExpression(Result, n); return Status; } return false; } /// Converts a variable to it's type information - bool DoTypeId(uint32 &Cur, char16 *&Var) + bool DoTypeId(uint32_t &Cur, char16 *&Var) { if (GetTokType(Cur) != TTypeId) return OnError(Cur, "Expecting 'typeid'."); Cur++; if (GetTokType(Cur) != TStartRdBracket) return OnError(Cur, "Expecting '('."); Cur++; char16 *t = GetTok(Cur); if (!t || !IsAlpha(*t)) return OnError(Cur, "Expecting variable name."); Cur++; if (GetTokType(Cur) != TEndRdBracket) return OnError(Cur, "Expecting ')'."); Cur++; Var = t; return true; } /// Parses statements - bool DoStatements(uint32 &Cur, bool *LastWasReturn, bool MoreThanOne = true) + bool DoStatements(uint32_t &Cur, bool *LastWasReturn, bool MoreThanOne = true) { while (Cur < Tokens.Length()) { char16 *t = GetTok(Cur); if (!t) break; GTokenType Tok = ExpTok.Find(t); switch (Tok) { case TTypeId: { return OnError(Cur, "typeif only valid in an expression."); } case TSemiColon: { Cur++; break; } case TDebug: { Asm0(Cur++, IDebug); break; } case TBreakPoint: { Asm0(Cur++, IBreakPoint); break; } case TReturn: { Cur++; if (!DoReturn(Cur)) return false; if (LastWasReturn) *LastWasReturn = true; break; } case TEndCurlyBracket: case TFunction: { return true; } case TIf: { if (!DoIf(Cur)) return false; if (LastWasReturn) *LastWasReturn = false; break; } case TFor: { if (!DoFor(Cur)) return false; if (LastWasReturn) *LastWasReturn = false; break; } case TWhile: { if (!DoWhile(Cur)) return false; if (LastWasReturn) *LastWasReturn = false; break; } case TExtern: { if (!DoExtern(Cur)) return false; if (LastWasReturn) *LastWasReturn = false; break; } default: { if (!DoExpression(Cur, 0)) return false; GTokenType Tok = ExpTok.Find(GetTok(Cur)); if (Tok == TSemiColon) Cur++; if (LastWasReturn) *LastWasReturn = false; break; } } if (!MoreThanOne) break; } return true; } /// Parses if/else if/else construct - bool DoIf(uint32 &Cur) + bool DoIf(uint32_t &Cur) { Cur++; char16 *t; if (GetTokType(Cur) == TStartRdBracket) { Cur++; // Compile and asm code to evaluate the expression GVarRef Result; int ExpressionTok = Cur; if (DoExpression(Cur, &Result)) { t = GetTok(Cur); if (!t || StricmpW(t, sEndRdBracket)) return OnError(Cur, "if missing ')'."); Cur++; t = GetTok(Cur); if (!t) return OnError(Cur, "if missing body statement."); // Output the jump instruction Asm1(ExpressionTok, IJumpZero, Result); DeallocReg(Result); size_t JzOffset = Code->ByteCode.Length(); Code->ByteCode.Length(JzOffset + sizeof(int32)); if (!StricmpW(t, sStartCurlyBracket)) { // Statement block Cur++; while ((t = GetTok(Cur))) { if (!StricmpW(t, sSemiColon)) { Cur++; } else if (!StricmpW(t, sEndCurlyBracket)) { Cur++; break; } else if (!DoStatements(Cur, NULL)) { return false; } } } else { // Single statement if (!DoStatements(Cur, NULL, false)) return false; } // Check for else... if ((t = GetTok(Cur)) && StricmpW(t, sElse) == 0) { // Add a jump for the "true" part of the expression to // jump over the "else" part. Asm0(Cur, IJump); size_t JOffset = Code->ByteCode.Length(); if (Code->ByteCode.Length(JOffset + 4)) { // Initialize the ptr to zero int32 *Ptr = (int32*)&Code->ByteCode[JOffset]; *Ptr = 0; // Resolve jz to here... if (JzOffset) { // Adjust the "if" jump point int32 *Ptr = (int32*)&Code->ByteCode[JzOffset]; *Ptr = (int32) (Code->ByteCode.Length() - JzOffset - sizeof(int32)); JzOffset = 0; } // Compile the else block Cur++; if ((t = GetTok(Cur)) && StricmpW(t, sStartCurlyBracket) == 0) { // 'Else' Statement block Cur++; while ((t = GetTok(Cur))) { if (!StricmpW(t, sSemiColon)) { Cur++; } else if (!StricmpW(t, sEndCurlyBracket)) { Cur++; break; } else if (!DoStatements(Cur, NULL)) { return false; } } } else { // Single statement if (!DoStatements(Cur, NULL, false)) return false; } // Resolve the "JOffset" jump that takes execution of // the 'true' part over the 'else' part Ptr = (int32*)&Code->ByteCode[JOffset]; *Ptr = (int32) (Code->ByteCode.Length() - JOffset - sizeof(int32)); if (*Ptr == 0) { // Empty statement... so delete the Jump instruction Code->ByteCode.Length(JOffset - 1); } } else OnError(Cur, "Mem alloc"); } if (JzOffset) { // Adjust the jump point int32 *Ptr = (int32*)&Code->ByteCode[JzOffset]; size_t CurLen = Code->ByteCode.Length(); *Ptr = (int32) (CurLen - JzOffset - sizeof(int32)); LgiAssert(*Ptr != 0); } return true; } } else return OnError(Cur, "if missing '('"); return false; } - GArray &GetByteCode() + GArray &GetByteCode() { return Code->ByteCode; } class GJumpZero { GCompilerPriv *Comp; int JzOffset; public: - GJumpZero(GCompilerPriv *d, uint32 &Cur, GVarRef &r) + GJumpZero(GCompilerPriv *d, uint32_t &Cur, GVarRef &r) { // Create jump instruction to jump over the body if the expression evaluates to false Comp = d; Comp->Asm1(Cur, IJumpZero, r); Comp->DeallocReg(r); JzOffset = (int) Comp->GetByteCode().Length(); Comp->GetByteCode().Length(JzOffset + sizeof(int32)); } ~GJumpZero() { // Resolve jump int32 *Ptr = (int32*) &Comp->GetByteCode()[JzOffset]; *Ptr = (int32) (Comp->GetByteCode().Length() - (JzOffset + sizeof(int32))); } }; /// Parses while construct - bool DoWhile(uint32 &Cur) + bool DoWhile(uint32_t &Cur) { Cur++; char16 *t = GetTok(Cur); if (!t || StricmpW(t, sStartRdBracket)) return OnError(Cur, "Expecting '(' after 'while'"); Cur++; // Store start of condition code size_t ConditionStart = Code->ByteCode.Length(); // Compile condition evalulation GVarRef r; if (!DoExpression(Cur, &r)) return false; // Create jump instruction to jump over the body if the expression evaluates to false { GJumpZero Jump(this, Cur, r); if (!(t = GetTok(Cur)) || StricmpW(t, sEndRdBracket)) return OnError(Cur, "Expecting ')'"); Cur++; // Compile the body of the loop if (!(t = GetTok(Cur))) return OnError(Cur, "Unexpected eof."); Cur++; if (StricmpW(t, sStartCurlyBracket) == 0) { // Block while ((t = GetTok(Cur))) { if (!StricmpW(t, sSemiColon)) { Cur++; } else if (!StricmpW(t, sEndCurlyBracket)) { Cur++; break; } else if (!DoStatements(Cur, NULL)) { return false; } } } else { // Single statement DoStatements(Cur, NULL, false); } // Jump to condition evaluation at 'ConditionStart' Asm0(Cur, IJump); size_t JOffset = Code->ByteCode.Length(); Code->ByteCode.Length(JOffset + sizeof(int32)); int32 *Ptr = (int32*) &Code->ByteCode[JOffset]; *Ptr = (int32) (ConditionStart - Code->ByteCode.Length()); } return true; } /// Parses for construct - bool DoFor(uint32 &Cur) + bool DoFor(uint32_t &Cur) { /* For loop asm structure: +---------------------------+ | Pre-condition expression | +---------------------------+ | Eval loop contdition exp. |<--+ | | | | JUMP ZERO (jumpz) |---+-+ +---------------------------+ | | | Body of loop... | | | | | | | | | | | | | | | | | | | | | | | +---------------------------+ | | | Post-cond. expression | | | | | | | | JUMP |---+ | +---------------------------+ | | Following code... |<----+ . . */ Cur++; char16 *t = GetTok(Cur); if (!t || StricmpW(t, sStartRdBracket)) return OnError(Cur, "Expecting '(' after 'for'"); Cur++; // Compile initial statement GVarRef r; t = GetTok(Cur); if (!t) return false; if (StricmpW(t, sSemiColon) && !DoExpression(Cur, 0)) return false; t = GetTok(Cur); // Look for ';' if (!t || StricmpW(t, sSemiColon)) return OnError(Cur, "Expecting ';'"); Cur++; // Store start of condition code size_t ConditionStart = Code->ByteCode.Length(); // Compile condition evalulation if (!DoExpression(Cur, &r)) return false; { GJumpZero Jmp(this, Cur, r); t = GetTok(Cur); // Look for ';' if (!t || StricmpW(t, sSemiColon)) return OnError(Cur, "Expecting ';'"); Cur++; // Compile the post expression code size_t PostCodeStart = Code->ByteCode.Length(); t = GetTok(Cur); if (StricmpW(t, sEndRdBracket) && !DoExpression(Cur, 0)) return false; // Store post expression code in temp variable - GArray PostCode; + GArray PostCode; size_t PostCodeLen = Code->ByteCode.Length() - PostCodeStart; if (PostCodeLen) { PostCode.Length(PostCodeLen); memcpy(&PostCode[0], &Code->ByteCode[PostCodeStart], PostCodeLen); // Delete the post expression off the byte code, we are putting it after the block code Code->ByteCode.Length(PostCodeStart); } // Look for ')' t = GetTok(Cur); if (!t || StricmpW(t, sEndRdBracket)) return OnError(Cur, "Expecting ')'"); Cur++; // Compile body of loop if ((t = GetTok(Cur)) && StricmpW(t, sStartCurlyBracket) == 0) { Cur++; while ((t = GetTok(Cur))) { if (!StricmpW(t, sSemiColon)) { Cur++; } else if (!StricmpW(t, sEndCurlyBracket)) { Cur++; break; } else if (!DoStatements(Cur, NULL)) { return false; } } } // Add post expression code if (PostCodeLen) { size_t Len = Code->ByteCode.Length(); Code->ByteCode.Length(Len + PostCodeLen); memcpy(&Code->ByteCode[Len], &PostCode[0], PostCodeLen); } // Jump to condition evaluation at 'ConditionStart' Asm0(Cur, IJump); size_t JOffset = Code->ByteCode.Length(); Code->ByteCode.Length(JOffset + sizeof(int32)); int32 *Ptr = (int32*) &Code->ByteCode[JOffset]; *Ptr = (int32) (ConditionStart - Code->ByteCode.Length()); } return true; } /// Compiles return construct - bool DoReturn(uint32 &Cur) + bool DoReturn(uint32_t &Cur) { GVarRef ReturnValue; char16 *t; int StartTok = Cur; GArray Exp; if (!Expression(Cur, Exp)) { return OnError(Cur, "Failed to compile return expression."); } else if (!AsmExpression(&ReturnValue, Exp)) { return OnError(Cur, "Failed to assemble return expression."); } if (!(t = GetTok(Cur)) || StricmpW(t, sSemiColon)) { return OnError(Cur, "Expecting ';' after return expression."); } Asm1(StartTok, IRet, ReturnValue); DeallocReg(ReturnValue); Cur++; return true; } // Compile a method definition bool DoFunction ( /// Cursor token index - uint32 &Cur, + uint32_t &Cur, /// [Optional] Current struct / class. /// If not NULL this is a method definition for said class. GCustomType *Struct = NULL ) { bool Status = false; bool LastInstIsReturn = false; if (!JumpLoc) { size_t Len = Code->ByteCode.Length(); if (Code->ByteCode.Length(Len + 5)) { GPtr p; p.u8 = &Code->ByteCode[Len]; *p.u8++ = IJump; *p.i32++ = 0; JumpLoc = Len + 1; } else OnError(Cur, "Mem alloc failed."); } GString FunctionName; GCustomType::Method *StructMethod = NULL; // Member function of script struct/class GFunctionInfo *ScriptMethod = NULL; // Standalone scripting function char16 *Name = GetTok(Cur); if (Name) { Cur++; char16 *t = GetTok(Cur); if (!t || StricmpW(t, sStartRdBracket)) return OnError(Cur, "Expecting '(' in function."); FunctionName = Name; // Parse parameters GArray Params; Cur++; while ((t = GetTok(Cur))) { if (IsAlpha(*t)) { Params.New() = t; Cur++; if (!(t = GetTok(Cur))) goto UnexpectedFuncEof; } if (!StricmpW(t, sComma)) ; else if (!StricmpW(t, sEndRdBracket)) { Cur++; break; } else goto UnexpectedFuncEof; Cur++; } if (Struct) { StructMethod = Struct->DefineMethod(FunctionName, Params, Code->ByteCode.Length()); } else { ScriptMethod = Code->GetMethod(FunctionName, true); if (!ScriptMethod) return OnError(Cur, "Can't define method '%s'.", FunctionName.Get()); ScriptMethod->Params = Params; - ScriptMethod->StartAddr = (uint32)Code->ByteCode.Length(); + ScriptMethod->StartAddr = (uint32_t)Code->ByteCode.Length(); } // Parse start of body if (!(t = GetTok(Cur))) goto UnexpectedFuncEof; if (StricmpW(t, sStartCurlyBracket)) return OnError(Cur, "Expecting '{'."); // Setup new scope(s) GAutoPtr ObjectScope; if (Struct) { // The object scope has to be first so that local variables take // precedence over object member variables. if (ObjectScope.Reset(new GVariables(Struct))) Scopes.Add(ObjectScope); } GVariables LocalScope(SCOPE_LOCAL); if (Struct) LocalScope.Var("This", true); for (unsigned i=0; iFrameSize = (uint16)LocalScope.Length(); else if (ScriptMethod) ScriptMethod->FrameSize.Reset(new uint16(LocalScope.Length())); else LgiAssert(!"What are you defining exactly?"); Status = true; Cur++; // LgiTrace("Added method %s @ %i, stack=%i, args=%i\n", f->Name.Str(), f->StartAddr, f->FrameSize, f->Params.Length()); break; } // Parse statement in the body if (!DoStatements(Cur, &LastInstIsReturn)) return OnError(Cur, "Can't compile function body."); } // Remove any scopes we created Scopes.PopLast(); if (Struct) Scopes.PopLast(); } if (!LastInstIsReturn) { GVarRef RetVal; AllocNull(RetVal); Asm1(Cur, IRet, RetVal); } return Status; UnexpectedFuncEof: return OnError(Cur, "Unexpected EOF in function."); } GExternFunc::ExternType ParseExternType(GArray &Strs) { GExternFunc::ExternType Type; Type.Out = false; Type.Ptr = 0; Type.ArrayLen = 1; Type.Base = GV_NULL; Type.Unsigned = false; bool InArray = false; for (unsigned i=0; i Tok; const char16 *t; while ((t = GetTok(Cur))) { if (!StricmpW(t, sStartRdBracket)) { Cur++; break; } else Tok.Add(t); Cur++; } if (Tok.Length() < 3) { return OnError(Cur, "Not enough tokens in extern decl."); } // First token is library name GExternFunc *e = new GExternFunc; if (!e) return OnError(Cur, "Alloc error."); Code->Externs.Add(e); e->Type = ExternFunc; char16 sQuotes[] = {'\'','\"',0}; GAutoWString LibName(TrimStrW(Tok[0], sQuotes)); e->Lib.Reset(WideToUtf8(LibName)); Tok.DeleteAt(0, true); e->Method = Tok.Last(); Tok.DeleteAt(Tok.Length()-1, true); if (!ValidStr(e->Method)) { Code->Externs.Length(Code->Externs.Length()-1); return OnError(Cur, "Failed to get extern method name."); } // Parse return type e->ReturnType = ParseExternType(Tok); // Parse argument types Tok.Length(0); while ((t = GetTok(Cur))) { if (!StricmpW(t, sEndRdBracket)) { e->ArgType.New() = ParseExternType(Tok); Cur++; break; } else if (!StricmpW(t, sComma)) { e->ArgType.New() = ParseExternType(Tok); } else Tok.Add(t); Cur++; } if ((t = GetTok(Cur))) { if (StricmpW(t, sSemiColon)) return OnError(Cur, "Expecting ';' in extern decl."); Cur++; } Methods.Add(e->Method, e); return true; } struct ExpPart { GOperator Op; int Value; ExpPart() { Op = OpNull; Value = 0; } }; int Evaluate(GArray &Exp, size_t Start, size_t End) { GArray p; // Find outer brackets ssize_t e; for (e = End; e >= (ssize_t)Start; e--) { if (Exp[e][0] == ')') break; } for (size_t i = Start; i <= End; i++) { char16 *t = Exp[i]; if (*t == '(') { p.New().Value = Evaluate(Exp, i + 1, e - 1); i = e; } else { GOperator op = IsOp(t, 0); if (op) { p.New().Op = op; } else if (IsDigit(*t)) { p.New().Value = AtoiW(t); } else { LgiAssert(0); break; } } } while (p.Length() > 1) { int HighPrec = 32; int Idx = -1; for (unsigned i=0; i 0 && Idx < (int)p.Length() - 1) { switch (p[Idx].Op) { case OpPlus: p[Idx-1].Value += p[Idx+1].Value; break; case OpMinus: p[Idx-1].Value -= p[Idx+1].Value; break; case OpMul: p[Idx-1].Value *= p[Idx+1].Value; break; case OpDiv: if (p[Idx+1].Value) p[Idx-1].Value /= p[Idx+1].Value; else LgiAssert(!"Div 0"); break; default: LgiAssert(!"Impl me."); break; } p.DeleteAt(Idx, true); p.DeleteAt(Idx, true); } else { LgiAssert(0); break; } } else { LgiAssert(!"Impl me."); } } if (p.Length() == 1) { LgiAssert(p[0].Op == OpNull); return p[0].Value; } LgiAssert(0); return 0; } int ByteSizeFromType(char *s, GVariantType t) { switch (t) { case GV_INT32: { char n[16], *o = n; for (char *c = s; *c; c++) { if (IsDigit(*c) && o < n + sizeof(n) - 1) *o++ = *c; } *o++ = 0; int bits = ::atoi(n); switch (bits) { case 8: return 1; case 16: return 2; } return 4; break; } case GV_INT64: { return 8; } case GV_WSTRING: { return sizeof(char16); } default: break; } return 1; } /// Compiles struct construct - bool DoStruct(uint32 &Cur) + bool DoStruct(uint32_t &Cur) { bool Status = false; // Parse struct name and setup a type char16 *t; GCustomType *Def = Code->Types.Find(t = GetTok(Cur)); if (!Def) Code->Types.Add(t, Def = new GCustomType(t)); Cur++; t = GetTok(Cur); if (!t || StricmpW(t, sStartCurlyBracket)) return OnError(Cur, "Expecting '{'"); Cur++; // Parse members while ((t = GetTok(Cur))) { // End of type def? GTokenType tt = ExpTok.Find(t); if (tt == TEndCurlyBracket) { Cur++; tt = GetTokType(Cur); if (!tt || tt != TSemiColon) return OnError(Cur, "Expecting ';' after '}'"); Status = true; break; } if (tt == TFunction) { Cur++; if (!DoFunction(Cur, Def)) return false; } else { // Parse member field GVariant TypeName = t; GCustomType *NestedType = 0; GVariantType Type = Types.Find(TypeName.Str()); if (!Type) { // Check other custom types NestedType = Code->GetType(t); if (!NestedType) return OnError(Cur, "Unknown type '%S' in struct definition.", t); // Ok, nested type. Type = GV_CUSTOM; } Cur++; if (!(t = GetTok(Cur))) goto EofError; bool Pointer = false; if (t[0] == '*' && t[1] == 0) { Pointer = true; Cur++; if (!(t = GetTok(Cur))) goto EofError; } GVariant Name = t; Cur++; if (!(t = GetTok(Cur))) goto EofError; int Array = 1; if (!StricmpW(t, sStartSqBracket)) { // Array Cur++; GArray Exp; while ((t = GetTok(Cur))) { Cur++; if (!StricmpW(t, sEndSqBracket)) break; Exp.Add(t); } Array = Evaluate(Exp, 0, Exp.Length()-1); } int MemberAddr = Def->AddressOf(Name.Str()); if (MemberAddr >= 0) return OnError(Cur, "Member '%s' can't be defined twice.", Name.Str()); if (NestedType) { if (!Def->DefineField(Name.Str(), NestedType, Array)) return OnError(Cur, "Failed to define field '%s'.", Name.Str()); } else { int Bytes = ByteSizeFromType(TypeName.Str(), Type); if (!Def->DefineField(Name.Str(), Type, Bytes, Array)) return OnError(Cur, "Failed to define field '%s'.", Name.Str()); } t = GetTok(Cur); if (StricmpW(t, sSemiColon)) return OnError(Cur, "Expecting ';'"); Cur++; } } return Status; EofError: return OnError(Cur, "Unexpected EOF."); } /// Compiler entry point bool Compile() { - uint32 Cur = 0; + uint32_t Cur = 0; JumpLoc = 0; // Setup the global scope Scopes.Length(0); Scopes.Add(&Code->Globals); // Compile the code... while (Cur < Tokens.Length()) { char16 *t = GetTok(Cur); if (!t) break; if (*t == '#' || StricmpW(t, sSemiColon) == 0) { Cur++; } else if (!StricmpW(t, sFunction)) { if (!DoFunction(++Cur)) return false; } else if (!StricmpW(t, sStruct)) { if (!DoStruct(++Cur)) return false; } else if (!StricmpW(t, sEndCurlyBracket)) { return OnError(Cur, "Not expecting '}'."); } else if (!StricmpW(t, sExtern)) { if (!DoExtern(++Cur)) return false; } else { if (JumpLoc) { GPtr p; p.u8 = &Code->ByteCode[JumpLoc]; - *p.u32 = (uint32) (Code->ByteCode.Length() - (JumpLoc + 4)); + *p.u32 = (uint32_t) (Code->ByteCode.Length() - (JumpLoc + 4)); JumpLoc = 0; } if (!DoStatements(Cur, NULL)) { return OnError(Cur, "Statement compilation failed."); } } } if (JumpLoc) { GPtr p; p.u8 = &Code->ByteCode[JumpLoc]; - *p.u32 = (uint32) (Code->ByteCode.Length() - (JumpLoc + 4)); + *p.u32 = (uint32_t) (Code->ByteCode.Length() - (JumpLoc + 4)); JumpLoc = 0; } // Do link time fixups... for (unsigned i=0; iStartAddr) { if (f.Args != f.Func->Params.Length()) { return OnError(f.Tok, "Function call '%s' has wrong arg count (caller=%i, method=%i).", f.Func->Name.Get(), f.Args, f.Func->Params.Length()); } else if (!f.Func->FrameSize.Get()) { return OnError(f.Tok, "Function call '%s' has no frame size.", f.Func->Name.Get()); } else { GPtr p; p.u8 = &Code->ByteCode[f.Offset]; LgiAssert(*p.u32 == 0); *p.u32++ = f.Func->StartAddr; *p.u16++ = *f.Func->FrameSize.Get(); } } else { return OnError(f.Tok, "Function '%s' not defined.", f.Func->Name.Get()); } } Fixups.Length(0); return true; } }; GCompiler::GCompiler() { d = new GCompilerPriv; } GCompiler::~GCompiler() { DeleteObj(d); } bool GCompiler::Compile ( GAutoPtr &Code, GScriptContext *SysContext, GScriptContext *UserContext, const char *FileName, const char *Script, GDom *Args ) { if (!Script) return false; GStringPipe p; if (SysContext && SysContext->GetLog()) d->Log = SysContext->GetLog(); else if (UserContext && UserContext->GetLog()) d->Log = UserContext->GetLog(); else d->Log = &p; d->Methods.Empty(); if (SysContext) { GHostFunc *f = SysContext->GetCommands(); for (int i=0; f[i].Method; i++) { f[i].Context = SysContext; d->Methods.Add(f[i].Method, &f[i]); } } d->SysCtx = SysContext; d->UserCtx = UserContext; if (d->UserCtx) { GHostFunc *f = d->UserCtx->GetCommands(); for (int i=0; f[i].Method; i++) { f[i].Context = d->UserCtx; if (!d->Methods.Find(f[i].Method)) d->Methods.Add(f[i].Method, f+i); else LgiAssert(!"Conflicting name of method in application's context."); } } if (!Code) Code.Reset(new GCompiledCode); bool Status = false; d->Code = dynamic_cast(Code.Get()); if (d->Code) { d->Code->UserContext = UserContext; d->Code->SysContext = SysContext; d->Code->SetSource(FileName, Script); bool LexResult = d->Lex((char*)Script, FileName); if (LexResult) { d->ScriptArgs = Args; Status = d->Compile(); } else { d->OnError(0, "Failed to lex script.\n"); } } else { d->OnError(0, "Allocation failed.\n"); } d->Code = NULL; return Status; } ////////////////////////////////////////////////////////////////////// class GScriptEnginePrivate { public: GViewI *Parent; SystemFunctions SysContext; GScriptContext *UserContext; GCompiledCode *Code; GVmDebuggerCallback *Callback; GVariant ReturnValue; GScriptEnginePrivate() { UserContext = NULL; Parent = NULL; Code = NULL; Callback = NULL; } }; GScriptEngine::GScriptEngine(GViewI *parent, GScriptContext *UserContext, GVmDebuggerCallback *Callback) { d = new GScriptEnginePrivate; d->Parent = parent; d->UserContext = UserContext; d->Callback = Callback; d->SysContext.SetEngine(this); } GScriptEngine::~GScriptEngine() { DeleteObj(d); } GCompiledCode *GScriptEngine::GetCurrentCode() { return d->Code; } bool GScriptEngine::Compile(GAutoPtr &Obj, GScriptContext *UserContext, char *Script, const char *FileName, GDom *Args) { if (!Script) { LgiAssert(!"Param error"); return NULL; } GCompiler Comp; return Comp.Compile(Obj, &d->SysContext, UserContext ? UserContext : d->UserContext, FileName, Script, Args); } GExecutionStatus GScriptEngine::Run(GCompiledCode *Obj, GVariant *Ret, const char *TempPath) { GExecutionStatus Status = ScriptError; d->Code = Obj; if (d->Code) { GVirtualMachine Vm(d->Callback); if (TempPath) Vm.SetTempPath(TempPath); Status = Vm.Execute(d->Code, 0, NULL, true, Ret ? Ret : &d->ReturnValue); d->Code = NULL; } return Status; } GExecutionStatus GScriptEngine::RunTemporary(GCompiledCode *Obj, char *Script, GVariant *Ret) { GExecutionStatus Status = ScriptError; GCompiledCode *Code = dynamic_cast(Obj); if (Script && Code) { GAutoPtr Temp(new GCompiledCode(*Code)); - uint32 TempLen = (uint32) Temp->Length(); + uint32_t TempLen = (uint32_t) Temp->Length(); d->Code = Temp; GCompiler Comp; if (Comp.Compile(Temp, &d->SysContext, d->UserContext, Temp->GetFileName(), Script, NULL)) { GVirtualMachine Vm(d->Callback); Status = Vm.Execute(dynamic_cast(Temp.Get()), TempLen, NULL, true, Ret ? Ret : &d->ReturnValue); } d->Code = NULL; } return Status; } bool GScriptEngine::EvaluateExpression(GVariant *Result, GDom *VariableSource, char *Expression) { if (!Result || !VariableSource || !Expression) { LgiAssert(!"Param error"); return false; } // Create trivial script to evaluate the expression GStringPipe p; p.Print("return %s;", Expression); GAutoString a(p.NewStr()); // Compile the script GCompiler Comp; GAutoPtr Obj; if (!Comp.Compile(Obj, NULL, NULL, NULL, a, VariableSource)) { LgiAssert(0); return false; } // Execute the script GVirtualMachine Vm(d->Callback); GCompiledCode *Code = dynamic_cast(Obj.Get()); GExecutionStatus s = Vm.Execute(Code, 0, NULL, true, Result ? Result : &d->ReturnValue); if (s == ScriptError) { LgiAssert(0); return false; } return true; } GStream *GScriptEngine::GetConsole() { if (d->SysContext.GetLog()) return d->SysContext.GetLog(); if (d->UserContext && d->UserContext->GetLog()) return d->UserContext->GetLog(); return NULL; } bool GScriptEngine::SetConsole(GStream *t) { d->SysContext.SetLog(t); if (d->UserContext) d->UserContext->SetLog(t); return true; } bool GScriptEngine::CallMethod(GCompiledCode *Obj, const char *Method, LScriptArguments &Args) { GCompiledCode *Code = dynamic_cast(Obj); if (!Code || !Method) return false; GFunctionInfo *i = Code->GetMethod(Method); if (!i) return false; GVirtualMachine Vm(d->Callback); Args.Vm = &Vm; GExecutionStatus Status = Vm.ExecuteFunction(Code, i, Args, NULL); Args.Vm = NULL; return Status != ScriptError; } GScriptContext *GScriptEngine::GetSystemContext() { return &d->SysContext; } diff --git a/src/common/Coding/GScriptLibrary.cpp b/src/common/Coding/GScriptLibrary.cpp --- a/src/common/Coding/GScriptLibrary.cpp +++ b/src/common/Coding/GScriptLibrary.cpp @@ -1,996 +1,996 @@ #include #include #include "Lgi.h" #include "GScripting.h" #include "GScriptingPriv.h" #include "GToken.h" #include "GProcess.h" #include "LgiRes.h" ////////////////////////////////////////////////////////////////////////////////////// char16 sChar[] = L"char"; char16 sInt[] = { 'i','n','t', 0 }; char16 sUInt[] = { 'u','i','n','t', 0 }; char16 sInt32[] = { 'i','n','t','3','2', 0 }; char16 sUInt32[] = { 'u','i','n','t','3','2', 0 }; char16 sInt64[] = { 'i','n','t','6','4', 0 }; char16 sHWND[] = { 'H','W','N','D', 0 }; char16 sDWORD[] = { 'D','W','O','R','D', 0 }; char16 sLPTSTR[] = { 's','L','P','T','S','T','R', 0 }; char16 sLPCTSTR[] = { 's','L','P','C','T','S','T','R', 0 }; char16 sElse[] = { 'e','l','s','e', 0 }; char16 sIf[] = { 'i','f',0 }; char16 sFunction[] = { 'f','u','n','c','t','i','o','n',0 }; char16 sExtern[] = { 'e','x','t','e','r','n',0 }; char16 sFor[] = { 'f','o','r',0 }; char16 sWhile[] = { 'w','h','i','l','e',0 }; char16 sReturn[] = { 'r','e','t','u','r','n',0 }; char16 sInclude[] = { 'i','n','c','l','u','d','e',0 }; char16 sDefine[] = { 'd','e','f','i','n','e',0 }; char16 sStruct[] = { 's','t','r','u','c','t',0 }; char16 sTrue[] = { 't','r','u','e',0 }; char16 sFalse[] = { 'f','a','l','s','e',0 }; char16 sNull[] = { 'n','u','l','l',0 }; char16 sOutParam[] = { '_','o','u','t','_',0}; char16 sHash[] = { '#', 0 }; char16 sPeriod[] = { '.', 0 }; char16 sComma[] = { ',', 0 }; char16 sSemiColon[] = { ';', 0 }; char16 sStartRdBracket[] = { '(', 0 }; char16 sEndRdBracket[] = { ')', 0 }; char16 sStartSqBracket[] = { '[', 0 }; char16 sEndSqBracket[] = { ']', 0 }; char16 sStartCurlyBracket[] = { '{', 0 }; char16 sEndCurlyBracket[] = { '}', 0 }; ////////////////////////////////////////////////////////////////////////////////////// GExecutionStatus GHostFunc::Call(GScriptContext *Ctx, LScriptArguments &Args) { return (Ctx->*(Func))(Args) ? ScriptSuccess : ScriptError; } const char *InstToString(GInstruction i) { #undef _i #define _i(name, opcode, desc) \ case name: return desc; switch (i) { AllInstructions } return "#err"; } GStream LScriptArguments::NullConsole; ////////////////////////////////////////////////////////////////////////////////////// int GScriptUtils::atoi(char16 *s) { int i = 0; if (s) { char b[64]; ssize_t Len = StrlenW(s) * sizeof(*s); ssize_t Bytes = LgiBufConvertCp(b, "utf-8", sizeof(b), (const void*&)s, LGI_WideCharset, Len); b[Bytes/sizeof(*b)] = 0; i = ::atoi(b); } return i; } int64 GScriptUtils::atoi64(char16 *s) { int64 i = 0; if (s) { #ifdef _MSC_VER i = _wtoi64(s); #else char b[64]; ssize_t Len = StrlenW(s) * sizeof(*s); ssize_t Bytes = LgiBufConvertCp(b, "utf-8", sizeof(b), (const void*&)s, LGI_WideCharset, Len); b[Bytes/sizeof(*b)] = 0; i = strtoll(b, 0, 10); #endif } return i; } double GScriptUtils::atof(char16 *s) { double i = 0; if (s) { char b[64]; ssize_t Len = StrlenW(s) * sizeof(*s); ssize_t Bytes = LgiBufConvertCp(b, "utf-8", sizeof(b), (const void*&)s, LGI_WideCharset, Len); b[Bytes/sizeof(*b)] = 0; i = ::atof(b); } return i; } int GScriptUtils::htoi(char16 *s) { int i = 0; if (s) { char b[64]; ssize_t Len = StrlenW(s) * sizeof(*s); ssize_t Bytes = LgiBufConvertCp(b, "utf-8", sizeof(b), (const void*&)s, LGI_WideCharset, Len); b[Bytes/sizeof(*b)] = 0; i = ::htoi(b); } return i; } ////////////////////////////////////////////////////////////////////////////////////// SystemFunctions::SystemFunctions() { Engine = NULL; Log = NULL; #ifdef WINNATIVE Brk = NULL; #endif } SystemFunctions::~SystemFunctions() { } GStream *SystemFunctions::GetLog() { return Log; } bool SystemFunctions::SetLog(GStream *log) { LgiAssert(Log == NULL); Log = log; return true; } void SystemFunctions::SetEngine(GScriptEngine *Eng) { Engine = Eng; } bool SystemFunctions::LoadString(LScriptArguments &Args) { if (Args.Length() != 1) return false; *Args.GetReturn() = LgiLoadString(Args[0]->CastInt32()); return true; } bool SystemFunctions::Sprintf(LScriptArguments &Args) { if (Args.Length() < 1) return false; char *Fmt = Args[0]->Str(); if (!Fmt) return false; #if defined(LINUX) || defined(MAC) // No support for sprintf with generated args... hack a string up // Formatting widths etc not supported. GArray s; int i = 1; for (char *f = Fmt; *f; f++) { if (f[0] == '%' && f[1] != '%') { f++; // Skip '%' // char *Fmt = f; while (*f && !IsAlpha(*f)) f++; // Skip formatting.. if (i >= Args.Length()) break; // No more arguments... switch (*f) { case 's': { // String... char *v = Args[i]->CastString(); if (v) s.Add(v, strlen(v)); else s.Add((char*)"(null)", 4); break; } case 'c': { char *Str = Args[i]->Str(); s.Add(Str ? *Str : '?'); break; } case 'f': case 'g': { break; } case 'u': case 'd': case 'i': { // Int... GString v; v.Printf("%i", Args[i]->CastInt32()); s.Add(v.Get(), v.Length()); break; } } f++; i++; } else s.Add(*f); } s.Add(0); // NULL terminate *Args.GetReturn() = s.AddressOf(); #else GArray Params; va_list a; unsigned i = 1; for (char *f = Fmt; *f; f++) { if (f[0] == '%' && f[1] != '%') { char *t = f + 1; while (*t && !IsAlpha(*t)) t++; if (i >= Args.Length()) { LgiAssert(!"Not enough args."); break; } switch (*t) { case 's': { Params.Add((UNativeInt)Args[i++]->Str()); break; } case 'c': { char *Str = Args[i++]->Str(); Params.Add(Str ? *Str : '?'); break; } case 'f': case 'g': { union tmp { double Dbl; struct { - uint32 High; - uint32 Low; + uint32_t High; + uint32_t Low; }; } Tmp; Tmp.Dbl = Args[i++]->CastDouble(); Params.Add(Tmp.High); Params.Add(Tmp.Low); break; } default: { Params.Add(Args[i++]->CastInt32()); break; } } f = *t ? t + 1 : t; } } a = (va_list) &Params[0]; #ifndef WIN32 #define _vsnprintf vsnprintf #endif char Buf[1024]; vsprintf_s(Buf, sizeof(Buf), Fmt, a); *Args.GetReturn() = Buf; #endif return true; } bool SystemFunctions::ReadTextFile(LScriptArguments &Args) { if (Args.Length() == 1 && FileExists(Args[0]->CastString())) { if (Args.GetReturn()->OwnStr(::ReadTextFile(Args[0]->CastString()))) return true; } return false; } bool SystemFunctions::WriteTextFile(LScriptArguments &Args) { if (Args.Length() == 2) { GFile f; if (f.Open(Args[0]->CastString(), O_WRITE)) { f.SetSize(0); GVariant *v = Args[1]; if (v) { switch (v->Type) { default: break; case GV_STRING: { size_t Len = strlen(v->Value.String); *Args.GetReturn() = f.Write(v->Value.String, Len) == Len; return true; break; } case GV_BINARY: { *Args.GetReturn() = f.Write(v->Value.Binary.Data, v->Value.Binary.Length) == v->Value.Binary.Length; return true; break; } } } } } return false; } GView *SystemFunctions::CastGView(GVariant &v) { switch (v.Type) { default: break; case GV_DOM: return dynamic_cast(v.Value.Dom); case GV_GVIEW: return v.Value.View; } return 0; } bool SystemFunctions::SelectFiles(LScriptArguments &Args) { GFileSelect s; if (Args.Length() > 0) s.Parent(CastGView(*Args[0])); GToken t(Args.Length() > 1 ? Args[1]->CastString() : 0, ",;:"); for (unsigned i=0; i 2 ? Args[2]->CastString() : 0); s.MultiSelect(Args.Length() > 3 ? Args[3]->CastInt32() != 0 : true); if (s.Open()) { Args.GetReturn()->SetList(); for (int i=0; iValue.Lst->Insert(new GVariant(s[i])); } } return true; } bool SystemFunctions::Sleep(LScriptArguments &Args) { if (Args.Length() != 1) return false; LgiSleep(Args[0]->CastInt32()); return true; } bool SystemFunctions::ToString(LScriptArguments &Args) { GStringPipe p; const char *Sep = ", "; for (unsigned i=0; iToString(); p.Print("%s%s", i?Sep:"", s.Get()); } Args.GetReturn()->OwnStr(p.NewStr()); return true; } bool SystemFunctions::Print(LScriptArguments &Args) { GStream *Out = Log ? Log : (Engine ? Engine->GetConsole() : NULL); for (unsigned n=0; Out && nWrite("NULL", 4); continue; } #if 1 size_t Len = strlen(f); Out->Write(f, Len); #else char *i = f, *o = f; for (; *i; i++) { if (*i == '\\') { i++; switch (*i) { case 'n': *o++ = '\n'; break; case 'r': *o++ = '\r'; break; case 't': *o++ = '\t'; break; case '\\': *o++ = '\\'; break; case '0': *o++ = 0; break; } } else { *o++ = *i; } } *o = 0; Out->Write(f, o - f); #endif } return true; } bool SystemFunctions::FormatSize(LScriptArguments &Args) { if (Args.Length() != 1) return false; char s[64]; LgiFormatSize(s, sizeof(s), Args[0]->CastInt64()); *Args.GetReturn() = s; return true; } bool SystemFunctions::ClockTick(LScriptArguments &Args) { *Args.GetReturn() = (int64)LgiCurrentTime(); return true; } bool SystemFunctions::Now(LScriptArguments &Args) { Args.GetReturn()->Empty(); Args.GetReturn()->Type = GV_DATETIME; Args.GetReturn()->Value.Date = new LDateTime; Args.GetReturn()->Value.Date->SetNow(); return true; } bool SystemFunctions::New(LScriptArguments &Args) { if (Args.Length() < 1 || !Args[0]) return false; Args.GetReturn()->Empty(); char *sType = Args[0]->CastString(); if (!sType) return false; if (IsDigit(*sType)) { // Binary block int Bytes = ::atoi(sType); if (!Bytes) return false; return Args.GetReturn()->SetBinary(Bytes, new char[Bytes], true); } GVariant *Ret = Args.GetReturn(); GDomProperty Type = LgiStringToDomProp(sType); switch (Type) { case TypeList: { Ret->SetList(); break; } case TypeHashTable: { Ret->SetHashTable(); break; } case TypeSurface: { Ret->Empty(); Ret->Type = GV_GSURFACE; if ((Ret->Value.Surface.Ptr = new GMemDC)) { Ret->Value.Surface.Ptr->AddRef(); Ret->Value.Surface.Own = true; } break; } case TypeFile: { Ret->Empty(); #if 1 Ret->Type = GV_STREAM; Ret->Value.Stream.Ptr = new GFile; if (Ret->Value.Stream.Ptr) Ret->Value.Stream.Own = true; #else Ret->Type = GV_GFILE; if ((Ret->Value.File.Ptr = new GFile)) { Ret->Value.File.Ptr->AddRef(); Ret->Value.File.Own = true; } #endif break; } case TypeDateTime: { Ret->Empty(); Ret->Type = GV_DATETIME; Ret->Value.Date = new LDateTime; break; } default: { Ret->Empty(); GCompiledCode *c = Engine ? Engine->GetCurrentCode() : NULL; if (!c) return false; GAutoWString o(Utf8ToWide(sType)); GCustomType *t = c->GetType(o); if (t) { int ArrayLength = Args.Length() > 1 ? Args[1]->CastInt32() : 1; if (ArrayLength > 0) { Ret->Type = GV_CUSTOM; Ret->Value.Custom.Dom = t; - Ret->Value.Custom.Data = new uint8[t->Sizeof() * ArrayLength]; + Ret->Value.Custom.Data = new uint8_t[t->Sizeof() * ArrayLength]; } } } } return true; } bool SystemFunctions::Delete(LScriptArguments &Args) { if (Args.Length() != 1) return false; GVariant *v = Args[0]; if (v->Type == GV_CUSTOM) { DeleteArray(v->Value.Custom.Data); v->Empty(); } else { v->Empty(); } *Args.GetReturn() = true; return true; } class GFileListEntry : public GDom { bool Folder; GVariant Name; int64 Size; LDateTime Modified; public: GFileListEntry(GDirectory *d) { Folder = d->IsDir(); Name = d->GetName(); Size = d->GetSize(); Modified.Set(d->GetLastWriteTime()); } bool GetVariant(const char *Var, GVariant &Value, char *Arr = 0) { GDomProperty p = LgiStringToDomProp(Var); switch (p) { case ObjName: Value = Name; break; case ObjLength: Value = Size; break; case FileFolder: Value = Folder; break; case FileModified: Value = &Modified; break; default: return false; } return true; } }; bool SystemFunctions::DeleteFile(LScriptArguments &Args) { if (Args.Length() != 1) return false; char *f = Args[0]->CastString(); if (f) *Args.GetReturn() = FileDev->Delete(Args[0]->CastString()); else *Args.GetReturn() = false; return true; } bool SystemFunctions::CurrentScript(LScriptArguments &Args) { GCompiledCode *Code; if (Engine && (Code = Engine->GetCurrentCode())) { *Args.GetReturn() = Code->GetFileName(); return true; } return false; } bool SystemFunctions::PathExists(LScriptArguments &Args) { if (Args.Length() == 0) return false; GDirectory d; if (d.First(Args[0]->CastString(), NULL)) { if (d.IsDir()) *Args.GetReturn() = 2; else *Args.GetReturn() = 1; } else { *Args.GetReturn() = 0; } return true; } bool SystemFunctions::PathJoin(LScriptArguments &Args) { char p[MAX_PATH] = ""; for (unsigned i=0; iCastString(); if (i) LgiMakePath(p, sizeof(p), p, s); else strcpy_s(p, sizeof(p), s); } if (*p) *Args.GetReturn() = p; else Args.GetReturn()->Empty(); return true; } bool SystemFunctions::PathSep(LScriptArguments &Args) { *Args.GetReturn() = DIR_STR; return true; } bool SystemFunctions::ListFiles(LScriptArguments &Args) { if (Args.Length() < 1) return false; Args.GetReturn()->SetList(); GDirectory d; char *Pattern = Args.Length() > 1 ? Args[1]->CastString() : 0; char *Folder = Args[0]->CastString(); for (int b=d.First(Folder); b; b=d.Next()) { if (!Pattern || MatchStr(Pattern, d.GetName())) { Args.GetReturn()->Value.Lst->Insert(new GVariant(new GFileListEntry(&d))); } } return true; } bool SystemFunctions::CreateSurface(LScriptArguments &Args) { Args.GetReturn()->Empty(); if (Args.Length() < 2) return false; int x = Args[0]->CastInt32(); int y = Args[1]->CastInt32(); GColourSpace Cs = CsNone; if (Args.Length() > 2) { GVariant *Type = Args[2]; const char *c; if (Type->IsInt()) { // Bit depth... convert to default Colour Space. Cs = GBitsToColourSpace(Type->CastInt32()); } else if ((c = Type->Str())) { // Parse string colour space def Cs = GStringToColourSpace(Type->Str()); } } if (!Cs) // Catch all error cases and make it the default screen depth. Cs = GdcD->GetColourSpace(); if ((Args.GetReturn()->Value.Surface.Ptr = new GMemDC(x, y, Cs))) { Args.GetReturn()->Type = GV_GSURFACE; Args.GetReturn()->Value.Surface.Own = true; Args.GetReturn()->Value.Surface.Ptr->AddRef(); } return true; } bool SystemFunctions::MessageDlg(LScriptArguments &Args) { if (Args.Length() < 4) return false; GViewI *Parent = CastGView(*Args[0]); char *Msg = Args[1]->Str(); char *Title = Args[2]->Str(); - uint32 Btns = Args[3]->CastInt32(); + uint32_t Btns = Args[3]->CastInt32(); int Btn = LgiMsg(Parent, Msg, Title, Btns); *Args.GetReturn() = Btn; return true; } bool SystemFunctions::GetInputDlg(LScriptArguments &Args) { if (Args.Length() < 4) return false; GViewI *Parent = CastGView(*Args[0]); char *InitVal = Args[1]->Str(); char *Msg = Args[2]->Str(); char *Title = Args[3]->Str(); bool Pass = Args.Length() > 4 ? Args[4]->CastInt32() != 0 : false; GInput Dlg(Parent, InitVal, Msg, Title, Pass); if (Dlg.DoModal()) { *Args.GetReturn() = Dlg.GetStr(); } return true; } bool SystemFunctions::GetViewById(LScriptArguments &Args) { Args.GetReturn()->Empty(); if (Args.Length() < 2) return false; GViewI *Parent = CastGView(*Args[0]); int Id = Args[1]->CastInt32(); if (!Parent || Id <= 0) return false; if (Parent->GetViewById(Id, Args.GetReturn()->Value.View)) { Args.GetReturn()->Type = GV_GVIEW; } return true; } bool SystemFunctions::Execute(LScriptArguments &Args) { if (Args.Length() < 2) return false; GProcess e; GStringPipe p; char *Exe = Args[0]->CastString(); char *Arguments = Args[1]->CastString(); bool Status = e.Run(Exe, Arguments, 0, true, 0, &p); if (Status) { GAutoString o(p.NewStr()); *Args.GetReturn() = o; } else if (Log) { - uint32 ErrCode = e.GetErrorCode(); + uint32_t ErrCode = e.GetErrorCode(); GAutoString ErrMsg = LgiErrorCodeToString(ErrCode); if (ErrMsg) Log->Print("Error: Execute(\"%s\",\"%s\") failed with '%s'\n", Exe, Arguments, ErrMsg.Get()); else Log->Print("Error: Execute(\"%s\",\"%s\") failed with '0x%x'\n", Exe, Arguments, ErrCode); } return Status; } bool SystemFunctions::System(LScriptArguments &Args) { if (Args.Length() < 2) return false; char *Exe = Args[0]->Str(); char *Arg = Args[1]->Str(); *Args.GetReturn() = LgiExecute(Exe, Arg); return true; } bool SystemFunctions::OsName(LScriptArguments &Args) { *Args.GetReturn() = LgiGetOsName(); return true; } bool SystemFunctions::OsVersion(LScriptArguments &Args) { GArray Ver; LgiGetOs(&Ver); Args.GetReturn()->SetList(); for (int i=0; i<3; i++) Args.GetReturn()->Value.Lst->Insert(new GVariant(Ver[i])); return true; } #define DefFn(Name) \ GHostFunc(#Name, 0, (ScriptCmd)&SystemFunctions::Name) GHostFunc SystemLibrary[] = { // String handling DefFn(LoadString), DefFn(FormatSize), DefFn(Sprintf), DefFn(Print), DefFn(ToString), // Containers/objects DefFn(New), DefFn(Delete), // Files DefFn(ReadTextFile), DefFn(WriteTextFile), DefFn(SelectFiles), DefFn(ListFiles), DefFn(DeleteFile), DefFn(CurrentScript), DefFn(PathJoin), DefFn(PathExists), DefFn(PathSep), // Time DefFn(ClockTick), DefFn(Sleep), DefFn(Now), // Images DefFn(CreateSurface), // UI DefFn(MessageDlg), DefFn(GetInputDlg), DefFn(GetViewById), // System DefFn(Execute), DefFn(System), DefFn(OsName), DefFn(OsVersion), // End of list marker GHostFunc(0, 0, 0), }; GHostFunc *SystemFunctions::GetCommands() { return SystemLibrary; } diff --git a/src/common/Coding/GScriptVM.cpp b/src/common/Coding/GScriptVM.cpp --- a/src/common/Coding/GScriptVM.cpp +++ b/src/common/Coding/GScriptVM.cpp @@ -1,2095 +1,2095 @@ #include #include "Lgi.h" #include "GScripting.h" #include "GScriptingPriv.h" #include "GBox.h" #include "GTabView.h" #include "GTextLog.h" #include "LList.h" #include "GToolBar.h" #include "GToken.h" #include "GTableLayout.h" #include "GTextLabel.h" #include "GScrollBar.h" #include "GMatrix.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 Args, void *Arg); +extern "C" uint64 __cdecl CallExtern64(void *FuncAddr, NativeInt *Ret, uint32_t Args, void *Arg); #elif defined(LINUX) #include #endif int GVariantCmp(GVariant *a, GVariant *b, NativeInt Data) { GVariant *Param = (GVariant*) 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(); GVariant av, bv; if (a->Value.Dom->GetValue(Fld, av) && b->Value.Dom->GetValue(Fld, bv)) { return GVariantCmp(&av, &bv, 0); } } else if (a->Type == GV_INT32 && b->Type == GV_INT32) { return a->CastInt32() - b->CastInt32(); } else { LgiAssert(!"Impl a handler for this type."); } return 0; } inline GVariantType DecidePrecision(GVariantType a, GVariantType 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 GVariantType ComparePrecision(GVariantType a, GVariantType 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(GVariant *v, GVariant &cache) { if (v->Type == GV_STRING) return v->Str(); cache = *v; return cache.CastString(); } inline int CompareVariants(GVariant *a, GVariant *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: { GVariant 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.. GVariant *Val = a->IsNull() ? b : a; if (Val->IsNull()) { LgiAssert(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; } } GExecutionStatus GExternFunc::Call(GScriptContext *Ctx, LScriptArguments &Args) { if (!Lib || !Method) return ScriptError; GStream *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; } GArray Val; GArray Mem; bool UnsupportedArg = false; Val.Length(Args.Length() << 1); GPointer 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; } } } } GLibrary 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(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)a, b); + 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) #ifdef COCOA #warning FIXME #else // 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??? LgiAssert(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; GArray AsmAddr; unsigned ViewLine; GAutoString Source; int SrcLines; GAutoString Asm; int AsmLines; }; class GVirtualMachinePriv : public GRefCount { GVariant ArrayTemp; char *CastArrayIndex(GVariant *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 CurrentFrameSize; + uint32_t CurrentFrameSize; ssize_t PrevFrameStart; size_t ReturnIp; GVarRef ReturnValue; }; enum RunType { RunContinue, RunStepInstruction, RunStepLine, RunStepOut }; GStream *Log; GCompiledCode *Code; GExecutionStatus Status; GPtr c; GVariant Reg[MAX_REGISTER]; GArray Locals; GVariant *Scope[SCOPE_MAX]; GArray Frames; RunType StepType; GVmDebuggerCallback *DbgCallback; GVmDebugger *Debugger; GVirtualMachine *Vm; LScriptArguments *ArgsOutput; bool BreakCpp; GArray BreakPts; GString TempPath; GVirtualMachinePriv(GVirtualMachine *vm, GVmDebuggerCallback *Callback) { Vm = vm; BreakCpp = false; ArgsOutput = NULL; Log = NULL; Code = NULL; Debugger = NULL; DbgCallback = Callback; ZeroObj(Scope); } ~GVirtualMachinePriv() { } void DumpVariant(GStream *Log, GVariant &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("(GDom*) %p", v.Value.Dom); break; case GV_HASHTABLE: { Log->Print("(GHashTable*) %p {", v.Value.Hash); int n = 0; // const char *k; // for (GVariant *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 (GVariant *i=v.Value.Lst->First(); i; i = v.Value.Lst->Next(), n++) { Log->Print("%s%i=", n?",":"", n); DumpVariant(Log, *i); } 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*)v.Value.Binary.Data)[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(GVariant *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 (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); } if (Vm && Vm->OpenDebugger(Code)) { if (!Debugger->GetCode()) { GAutoPtr Cp(new GCompiledCode(*Code)); Debugger->OwnCompiledCode(Cp); GStringPipe AsmBuf; Decompile(Code->UserContext, Code, &AsmBuf); GAutoString Asm(AsmBuf.NewStr()); Debugger->SetSource(Asm); } Debugger->OnAddress(Address); GString m; m.Printf("%s (%s:%i)", Msg, LgiGetLeaf(File), Line); Debugger->OnError(m); Debugger->Run(); } else { LgiAssert(!"Scripting engine exception"); LgiTrace("%s:%i - Exception @ %i: %s\n", File, Line, Address, Msg); } } GExecutionStatus Decompile(GScriptContext *Context, GCompiledCode *Code, GStream *log) { GExecutionStatus Status = ScriptSuccess; LgiAssert(sizeof(GVarRef) == 4); GPtr c; - uint8 *Base = &Code->ByteCode[0]; + uint8_t *Base = &Code->ByteCode[0]; c.u8 = Base; - uint8 *e = c.u8 + Code->ByteCode.Length(); + uint8_t *e = c.u8 + Code->ByteCode.Length(); GStream *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++) { GFunctionInfo *Info = Code->Methods[m]; if (Info->StartAddr >= 0) Fn.Add(Info->StartAddr, Info->Name.Get()); else LgiAssert(!"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; } - GExecutionStatus Setup(GCompiledCode *code, uint32 StartOffset, GStream *log, GFunctionInfo *Func, LScriptArguments *Args) + GExecutionStatus Setup(GCompiledCode *code, uint32_t StartOffset, GStream *log, GFunctionInfo *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); LgiAssert(sizeof(GVarRef) == 4); - uint8 *Base = c.u8 = &Code->ByteCode[0]; - uint8 *e = c.u8 + Code->ByteCode.Length(); + 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(); #if 1 const char *SourceFileName = Code->GetFileName(); char Obj[MAX_PATH]; if (SourceFileName) { if (strchr(SourceFileName, DIR_CHAR)) strcpy_s(Obj, sizeof(Obj), SourceFileName); else if (TempPath != NULL) LgiMakePath(Obj, sizeof(Obj), TempPath, SourceFileName); else { LGetSystemPath(LSP_TEMP, Obj, sizeof(Obj)); LgiMakePath(Obj, sizeof(Obj), Obj, SourceFileName); } char *Ext = LgiGetExtension(Obj); if (Ext) strcpy_s(Ext, sizeof(Obj)-(Ext-Obj), "asm"); else strcat_s(Obj, sizeof(Obj), ".asm"); } else { GAutoString 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)); } LgiMakePath(Obj, sizeof(Obj), DataPath, "Script.asm"); } { GDirectory SrcD, ObjD; bool OutOfDate = true; if (SrcD.First(SourceFileName, NULL) != 0 && ObjD.First(Obj, NULL) != 0) { OutOfDate = ObjD.GetLastWriteTime() < SrcD.GetLastWriteTime(); } if (OutOfDate || Debugger) { GFile f; GStringPipe p; GStream *Out = NULL; if (Debugger) { Out = &p; } else if (f.Open(Obj, O_WRITE)) { f.SetSize(0); Out = &f; } if (Out) { GExecutionStatus Decomp = Decompile(Code->UserContext, Code, Out); f.Close(); if (Decomp != ScriptSuccess) { LgiAssert(!"Decompilation failed."); return ScriptError; } if (Debugger) { GAutoString a(p.NewStr()); 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 LgiAssert(Func->FrameSize.Get()); Sf.CurrentFrameSize = *Func->FrameSize.Get(); AddLocalSize(Sf.CurrentFrameSize); if (Args) { // Put the arguments of the function call into the local array for (unsigned i=0; iLength(); i++) { Locals[LocalsBase+i] = *(*Args)[i]; } } // Set IP to start of function c.u8 = Base + Func->StartAddr; } 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; } GExecutionStatus Run(RunType Type) { LgiAssert(Code != NULL); - uint8 *Base = &Code->ByteCode[0]; - uint8 *e = Base + Code->ByteCode.Length(); + uint8_t *Base = &Code->ByteCode[0]; + uint8_t *e = Base + Code->ByteCode.Length(); 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 // LHashTbl, int> &Debug = Code->Debug; 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 == 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; } } if (Debugger && Status != ScriptError) Debugger->OnAddress(CurrentScriptAddress); return Status; } }; GVirtualMachine::GVirtualMachine(GVmDebuggerCallback *callback) { d = new GVirtualMachinePriv(this, callback); d->AddRef(); } GVirtualMachine::GVirtualMachine(GVirtualMachine *vm) { d = vm->d; d->AddRef(); } GVirtualMachine::~GVirtualMachine() { if (d->Vm == this) d->Vm = NULL; d->DecRef(); } -GExecutionStatus GVirtualMachine::Execute(GCompiledCode *Code, uint32 StartOffset, GStream *Log, bool StartImmediately, GVariant *Return) +GExecutionStatus GVirtualMachine::Execute(GCompiledCode *Code, uint32_t StartOffset, GStream *Log, bool StartImmediately, GVariant *Return) { if (!Code) return ScriptError; LScriptArguments Args(this, Return); GExecutionStatus s = d->Setup(Code, StartOffset, Log, NULL, &Args); if (s != ScriptSuccess || !StartImmediately) return s; return d->Run(GVirtualMachinePriv::RunContinue); } GExecutionStatus GVirtualMachine::ExecuteFunction(GCompiledCode *Code, GFunctionInfo *Func, LScriptArguments &Args, GStream *Log, LScriptArguments *ArgsOut) { GCompiledCode *Cc = dynamic_cast(Code); if (!Cc) return ScriptError; GExecutionStatus s = d->Setup(Cc, 0, Log, Func, &Args); if (s != ScriptSuccess) return s; d->ArgsOutput = ArgsOut; Args.Vm = this; GExecutionStatus r = d->Run(GVirtualMachinePriv::RunContinue); Args.Vm = NULL; return r; } GVmDebugger *GVirtualMachine::OpenDebugger(GCompiledCode *Code, const char *Assembly) { if (!d->Debugger) { if (!d->DbgCallback) return NULL; d->Debugger = d->DbgCallback->AttachVm(this, Code, Assembly); } return d->Debugger; } bool GVirtualMachine::StepInstruction() { GExecutionStatus s = d->Run(GVirtualMachinePriv::RunStepInstruction); return s != ScriptError; } bool GVirtualMachine::StepLine() { GExecutionStatus s = d->Run(GVirtualMachinePriv::RunStepLine); return s != ScriptError; } bool GVirtualMachine::StepOut() { GExecutionStatus s = d->Run(GVirtualMachinePriv::RunStepOut); return s != ScriptError; } bool GVirtualMachine::BreakExecution() { return false; } bool GVirtualMachine::Continue() { GExecutionStatus s = d->Run(GVirtualMachinePriv::RunContinue); return s != ScriptError; } bool GVirtualMachine::BreakPoint(const char *File, int Line, bool Add) { return false; } bool GVirtualMachine::BreakPoint(int Addr, bool Add) { if (Add) d->BreakPts.Add(Addr); else d->BreakPts.Delete(Addr); return true; } void GVirtualMachine::SetBreakCpp(bool Brk) { d->BreakCpp = Brk; } void GVirtualMachine::SetTempPath(const char *Path) { d->TempPath = Path; } //////////////////////////////////////////////////////////////////// /* bool GTypeDef::GetVariant(const char *Name, GVariant &Value, char *Arr) { GMember *m = Members.Find(Name); if (!m || !Object) { LgiAssert(!"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, GVariant &Value, char *Arr) { GMember *m = Members.Find(Name); if (!m || !Object) { LgiAssert(!"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 IconsData[] = +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, }; GInlineBmp 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 GScriptVmDebuggerPriv; class GDebugView : public GTextView3 { GScriptVmDebuggerPriv *d; int CurLine; int ErrorLine; GString Error; GArray BreakPts; public: GDebugView(GScriptVmDebuggerPriv *priv); ~GDebugView(); void SetError(const char *Err); int GetCurLine() { return CurLine; } int GetAddr(); void ScrollToCurLine(); void PourText(size_t Start, ssize_t Length) override; void OnPaintLeftMargin(GSurface *pDC, GRect &r, GColour &colour) override; void OnPaint(GSurface *pDC) override; bool Breakpoint(int Addr); }; struct GScriptVmDebuggerPriv { // Current script bool OwnVm; GAutoPtr Vm; GVmDebuggerCallback *Callback; GString Script, Assembly; GArray Blocks; size_t CurrentAddr; GArray LineIsAsm; GAutoPtr Obj; GVariant Return; bool AcceptNotify; // Ui bool RunLoop; GView *Parent; GBox *Main; GBox *Sub; LList *SourceLst; GTabView *Tabs; GDebugView *Text; LList *Locals, *Globals, *Registers, *Stack; GTextLog *Log; GToolBar *Tools; GTableLayout *VarsTbl; GScriptVmDebuggerPriv() { RunLoop = false; OwnVm = false; CurrentAddr = -1; Main = NULL; Tabs = NULL; Log = NULL; Text = NULL; Locals = NULL; Globals = NULL; Registers = NULL; Stack = NULL; Tools = NULL; SourceLst = NULL; Callback = NULL; VarsTbl = NULL; } }; GDebugView::GDebugView(GScriptVmDebuggerPriv *priv) : GTextView3(IDC_TEXT, 0, 0, 100, 100) { d = priv; ErrorLine = -1; SetWrapType(TEXTED_WRAP_NONE); GetCss(true)->PaddingLeft(GCss::Len(GCss::LenPx, 18)); } GDebugView::~GDebugView() { } void GDebugView::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 GDebugView::GetAddr() { ssize_t Index; GTextLine *t = GetTextLine(Cursor, &Index); if (!t) return -1; int Addr = IsAddr(Text + t->Start); return Addr; } void GDebugView::ScrollToCurLine() { SetLine(CurLine); } bool GDebugView::Breakpoint(int Addr) { if (BreakPts.HasItem(Addr)) { BreakPts.Delete(Addr); Invalidate(); return false; } else { BreakPts.Add(Addr); Invalidate(); return true; } } void GDebugView::OnPaintLeftMargin(GSurface *pDC, GRect &r, GColour &colour) { GTextView3::OnPaintLeftMargin(pDC, r, colour); pDC->Colour(GColour(192, 0, 0)); GFont *f = GetFont(); f->Colour(LC_LOW, LC_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); for (GTextLine *i = Line[Start]; i && Ln <= Start + Page; i = Line.Next(), Ln++) { int OffY = (Ln - Start) * f->GetHeight(); /* GString Num; Num.Printf("%i", Ln); GDisplayString 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(LC_TEXT, LC_WORKSPACE); } void GDebugView::OnPaint(GSurface *pDC) { GTextView3::OnPaint(pDC); if (Error) { GTextLine *Ln = Line[ErrorLine]; GFont *f = GetFont(); GRect c = GetClient(); int Pad = 3; GDisplayString Ds(f, Error); GRect r(0, 0, Ds.X()-1, Ds.Y()-1); r.Size(-Pad, -Pad); r.Offset(c.X()-r.X(), Ln ? Ln->r.y1 - ScrollYPixel(): 0); f->Transparent(false); f->Colour(GColour::White, GColour::Red); Ds.Draw(pDC, r.x1 + Pad, r.y1 + Pad, &r); } } void GDebugView::PourText(size_t Start, ssize_t Len) { GTextView3::PourText(Start, Len); CurLine = -1; for (unsigned i=0; iBlocks.Length(); i++) { CodeBlock &b = d->Blocks[i]; for (unsigned n=0; nCurrentAddr >= b.AsmAddr[n]) { CurLine = b.ViewLine + b.SrcLines + n - 1; } } } unsigned Idx = 0; for (GTextLine *l=Line.First(); l; l=Line.Next(), Idx++) { // char16 *t = Text + l->Start; // char16 *e = t + l->Len; if (CurLine == Idx) { l->c.Rgb(0, 0, 0); l->Back.Set(LC_DEBUG_CURRENT_LINE, 24); } 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); } } } } GVmDebuggerWnd::GVmDebuggerWnd(GView *Parent, GVmDebuggerCallback *Callback, GVirtualMachine *Vm, GCompiledCode *Code, const char *Assembly) { d = new GScriptVmDebuggerPriv; d->Parent = Parent; d->AcceptNotify = false; if (Vm) d->Vm.Reset(new GVirtualMachine(Vm)); d->Callback = Callback; if (Code) d->Script = Code->GetSource(); d->Assembly = Assembly; GRect r(0, 0, 1000, 900); SetPos(r); if (Parent) MoveSameScreen(Parent); else MoveToCenter(); Name("Script Debugger"); if (Attach(NULL)) { if ((Menu = new GMenu)) { Menu->Attach(this); GSubMenu *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 GToolBar); uint16 *Px = (uint16*) DbgIcons.Data; GImageList *il = new GImageList(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 GBox(IDC_BOX)); d->Main->SetVertical(true); d->Main->AddView(d->Sub = new GBox(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(GCss::Len("200px")); d->SourceLst->AddColumn("Source", 200); d->Sub->AddView(d->Text = new GDebugView(d)); d->Main->AddView(d->Tabs = new GTabView(IDC_TABS)); d->Tabs->GetCss(true)->Height(GCss::Len("250px")); GTabPage *p = d->Tabs->Append("Variables"); p->Append(d->VarsTbl = new GTableLayout(IDC_VARS_TBL)); int x = 0, y = 0; GLayoutCell *c = d->VarsTbl->GetCell(x++, y); c->Add(new GTextLabel(IDC_STATIC, 0, 0, -1, -1, "Globals:")); c = d->VarsTbl->GetCell(x++, y); c->Add(new GTextLabel(IDC_STATIC, 0, 0, -1, -1, "Locals:")); c = d->VarsTbl->GetCell(x++, y); c->Add(new GTextLabel(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 GTextLog(IDC_LOG)); AttachChildren(); Visible(true); { char p[MAX_PATH]; LgiGetExePath(p, sizeof(p)); LgiMakePath(p, sizeof(p), p, "../Scripts"); GDirectory 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 (Code && Code->GetFileName()) { if (_stricmp(p, Code->GetFileName()) == 0) Match = it; } d->SourceLst->Insert(it); } } } if (!Match && Code) { LListItem *it = new LListItem; if (it) { it->SetText(LgiGetLeaf(Code->GetFileName()), 0); it->SetText(Code->GetFileName(), 1); d->SourceLst->Insert(it); it->Select(true); } } } } d->AcceptNotify = true; } GVmDebuggerWnd::~GVmDebuggerWnd() { LgiAssert(d->RunLoop == false); } bool GVmDebuggerWnd::OnRequestClose(bool OsShuttingDown) { if (!d->RunLoop) return GWindow::OnRequestClose(OsShuttingDown); d->RunLoop = false; return false; // Wait for Run() to exit in it's own time. } void GVmDebuggerWnd::Run() { // This is to allow objects on the application's stack to // still be valid while the debugger UI is shown. d->RunLoop = true; while (d->RunLoop && Visible()) { LgiApp->Run(false); LgiSleep(1); } Quit(); } GStream *GVmDebuggerWnd::GetLog() { return d->Log; } void GVmDebuggerWnd::OwnVm(bool Own) { d->OwnVm = Own; } void GVmDebuggerWnd::OwnCompiledCode(GAutoPtr Cc) { d->Obj = Cc; } GCompiledCode *GVmDebuggerWnd::GetCode() { return d->Obj; } void GVmDebuggerWnd::SetSource(const char *Mixed) { #if 1 GStringPipe Glob(256); GStringPipe Tmp(256); d->Blocks.Length(0); CodeBlock *Cur = &d->Blocks.New(); // Parse the mixed source GToken t(Mixed, "\n", 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(); GStringPipe Txt; GToken Src(d->Script, "\n", 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]); 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; } GAutoString a(Txt.NewStr()); d->Text->Name(a); #else d->Text->Name(Mixed); #endif } void GVmDebuggerWnd::UpdateVariables(LList *Lst, GVariant *Arr, ssize_t Len, char Prefix) { if (!d->Vm || !Lst || !Arr) return; List all; Lst->GetAll(all); LListItem *it; for (int i=0; iVm->d->DumpVariant(&p, *v); GAutoString a(p.NewStr()); char nm[32]; sprintf_s(nm, sizeof(nm), "%c%i", Prefix, i); if (i >= all.Length()) { it = new LListItem; all.Insert(it); Lst->Insert(it); } it = i < all.Length() ? all[i] : NULL; if (it) { it->SetText(nm, 0); it->SetText(a, 1); } } Lst->ResizeColumnsToContent(); } void GVmDebuggerWnd::OnAddress(size_t Addr) { d->CurrentAddr = Addr; if (d->Text) { ssize_t Sz = d->Text->GetSize(); d->Text->PourText(0, Sz); d->Text->ScrollToCurLine(); d->Text->Invalidate(); } OnNotify(d->Tabs, 0); } void GVmDebuggerWnd::OnError(const char *Msg) { if (Msg) d->Text->SetError(Msg); } void GVmDebuggerWnd::OnRun(bool Running) { } void GVmDebuggerWnd::LoadFile(const char *File) { if (!d->Vm || !d->Callback) { LgiAssert(0); return; } GFile 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)) { GCompiledCode *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 GVmDebuggerWnd::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: { d->Vm.Reset(); if (d->RunLoop) d->RunLoop = false; else Quit(); break; } case IDC_RESTART: { if (d->Vm && d->Obj) { GCompiledCode *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) { LgiAssert(0); break; } GMenuItem *i = Menu->FindItem(IDC_BREAK_CPP); if (!i) { LgiAssert(0); break; } bool b = !i->Checked(); i->Checked(b); d->Vm->SetBreakCpp(b); break; } } return GWindow::OnCommand(Cmd, Event, Wnd); } int GVmDebuggerWnd::OnNotify(GViewI *Ctrl, int Flags) { 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()) { GVirtualMachinePriv::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(); GArray &Frames = d->Vm->d->Frames; for (int i=(int)Frames.Length()-1; i>=0; i--) { GVirtualMachinePriv::StackFrame &Sf = Frames[i]; LListItem *li = new LListItem; GString 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 (Flags == GNotifyItem_Select) { LListItem *it = d->SourceLst->GetSelected(); if (!it) break; char *full = it->GetText(1); if (!FileExists(full)) break; LoadFile(full); } break; } } return GWindow::OnNotify(Ctrl, Flags); } GMessage::Param GVmDebuggerWnd::OnEvent(GMessage *Msg) { return GWindow::OnEvent(Msg); } ///////////////////////////////////////////////////////////////////////////// bool LScriptArguments::Throw(const char *File, int Line, const char *Msg, ...) { if (!Vm || !Vm->d) return false; va_list Arg; va_start(Arg, Msg); GString s; s.Printf(Arg, Msg); va_end(Arg); Vm->d->OnException(File, Line, 0, s); return true; } diff --git a/src/common/Coding/GScriptingPriv.h b/src/common/Coding/GScriptingPriv.h --- a/src/common/Coding/GScriptingPriv.h +++ b/src/common/Coding/GScriptingPriv.h @@ -1,546 +1,546 @@ #ifndef _GSCRIPTING_PRIV_H_ #define _GSCRIPTING_PRIV_H_ #include #include "GScripting.h" #include "GRefCount.h" // Instructions #define _i(name, opcode, desc) \ name = opcode, #define AllInstructions \ _i(INop, 0, "Nop") \ _i(IAssign, OpAssign, "OpAssign") \ _i(IPlus, OpPlus, "OpPlus") \ _i(IUnaryPlus, OpUnaryPlus, "OpUnaryPlus") \ _i(IMinus, OpMinus, "OpMinus") \ _i(IUnaryMinus, OpUnaryMinus, "OpUnaryMinus") \ _i(IMul, OpMul, "OpMul") \ _i(IDiv, OpDiv, "OpDiv") \ _i(IMod, OpMod, "OpMod") \ _i(ILessThan, OpLessThan, "OpLessThan") \ _i(ILessThanEqual, OpLessThanEqual, "OpLessThanEqual") \ _i(IGreaterThan, OpGreaterThan, "OpGreaterThan") \ _i(IGreaterThanEqual, OpGreaterThanEqual, "OpGreaterThanEqual") \ _i(IEquals, OpEquals, "OpEquals") \ _i(INotEquals, OpNotEquals, "OpNotEquals") \ _i(IPlusEquals, OpPlusEquals, "OpPlusEquals") \ _i(IMinusEquals, OpMinusEquals, "OpMinusEquals") \ _i(IMulEquals, OpMulEquals, "OpMulEquals") \ _i(IDivEquals, OpDivEquals, "OpDivEquals") \ _i(IPostInc, OpPostInc, "OpPostInc") \ _i(IPostDec, OpPostDec, "OpPostDec") \ _i(IPreInc, OpPreInc, "OpPreInc") \ _i(IPreDec, OpPreDec, "OpPreDec") \ _i(IAnd, OpAnd, "OpAnd") \ _i(IOr, OpOr, "OpOr") \ _i(INot, OpNot, "OpNot") \ \ /** Calls a another part of the script */ \ _i(ICallScript, 64, "CallScript") \ /** Calls a method defined by the script context */ \ _i(ICallMethod, 65, "CallMethod") \ _i(IDomGet, 67, "DomGet") \ _i(IDomSet, 68, "DomSet") \ _i(IPush, 69, "Push") \ _i(IPop, 70, "Pop") \ _i(IJump, 71, "Jump") \ _i(IJumpZero, 72, "JumpZ") \ _i(IArrayGet, 73, "ArrayGet") \ _i(IArraySet, 74, "ArraySet") \ _i(IRet, 75, "Return") \ _i(IDomCall, 76, "DomCall") \ /* Stop in the VM at instruction */ \ _i(IBreakPoint, 77, "BreakPoint") \ _i(ICast, 78, "Cast") \ /* Open the debugger */ \ _i(IDebug, 79, "Debug") \ enum GInstruction { AllInstructions }; enum OperatorType { OpPrefix, OpInfix, OpPostfix, }; extern char16 sChar[]; extern char16 sInt[]; extern char16 sUInt[]; extern char16 sInt32[]; extern char16 sUInt32[]; extern char16 sInt64[]; extern char16 sHWND[]; extern char16 sDWORD[]; extern char16 sLPTSTR[]; extern char16 sLPCTSTR[]; extern char16 sElse[]; extern char16 sIf[]; extern char16 sFunction[]; extern char16 sExtern[]; extern char16 sFor[]; extern char16 sWhile[]; extern char16 sReturn[]; extern char16 sInclude[]; extern char16 sDefine[]; extern char16 sStruct[]; extern char16 sTrue[]; extern char16 sFalse[]; extern char16 sNull[]; extern char16 sOutParam[]; extern char16 sHash[]; extern char16 sPeriod[]; extern char16 sComma[]; extern char16 sSemiColon[]; extern char16 sStartRdBracket[]; extern char16 sEndRdBracket[]; extern char16 sStartSqBracket[]; extern char16 sEndSqBracket[]; extern char16 sStartCurlyBracket[]; extern char16 sEndCurlyBracket[]; extern const char *InstToString(GInstruction i); /* Variable Reference: Can either be a: - stack variable - global variable - register (0 .. MAX_REGISTER - 1) Thus a variable reference encodes 2 pieces of information, first the scope of the reference (as above) and secondly the index into that scope. Scopes are stored as arrays of variables. The scope is one byte, the index is 3 bytes following, totally 4 bytes per ref. */ #define MAX_REGISTER 8 /// 32bit variable reference, used to track where a variable is during compilation. struct GVarRef { /// \sa #SCOPE_REGISTER, #SCOPE_LOCAL or #SCOPE_GLOBAL unsigned Scope : 8; /// Index into scope int Index : 24; bool Valid() { return Index >= 0; } void Empty() { Scope = 0; Index = -1; } bool IsReg() { return Scope == SCOPE_REGISTER && Index >= 0 && Index < MAX_REGISTER; } void SetReg(int i) { Scope = SCOPE_REGISTER; Index = i; } bool operator ==(GVarRef &r) { return r.Scope == Scope && r.Index == Index; } bool operator !=(GVarRef &r) { return r.Scope != Scope || r.Index != Index; } const char *GetStr() { if (Index < 0) { LgiAssert(!"Invalid reference"); return "NoRef"; } #define GETSTR_BUF_SIZE 16 static char Buf[4][GETSTR_BUF_SIZE]; static int Cur = 0; static char Names[] = {'R', 'L', 'G'}; char *b = Buf[Cur++]; if (Cur >= 4) Cur = 0; LgiAssert(Scope <= SCOPE_GLOBAL); sprintf_s(b, GETSTR_BUF_SIZE, "%c%i", Names[Scope], Index); return b; } }; union GPtr { - uint8 *u8; + uint8_t *u8; uint16 *u16; - uint32 *u32; + uint32_t *u32; int8 *i8; int16 *i16; int32 *i32; double *dbl; float *flt; GVarRef *r; GFunc **fn; }; class SystemFunctions; class GCompileTools { protected: OperatorType OpType(GOperator o) { switch (o) { case OpUnaryPlus: case OpUnaryMinus: case OpPreInc: case OpPreDec: case OpNot: return OpPrefix; case OpPostInc: case OpPostDec: return OpPostfix; default: return OpInfix; } } int GetPrecedence(GOperator o) { // Taken from: // http://www.cppreference.com/operator_precedence.html switch (o) { case OpAssign: case OpMinusEquals: case OpPlusEquals: case OpMulEquals: case OpDivEquals: return 16; case OpAnd: return 13; case OpOr: return 14; case OpEquals: case OpNotEquals: return 9; case OpLessThan: case OpLessThanEqual: case OpGreaterThan: case OpGreaterThanEqual: return 8; case OpPlus: case OpMinus: return 6; case OpMul: case OpDiv: case OpMod: return 5; case OpUnaryPlus: case OpUnaryMinus: case OpPreInc: case OpPreDec: case OpNot: return 3; case OpPostInc: case OpPostDec: return 2; case OpNull: return 0; default: LgiAssert(!"Really?"); break; } return -1; } GOperator IsOp(char16 *s, int PrevIsOp) { if (!s) return OpNull; if (s[0] != 0 && !s[1]) { // One character operator switch (*s) { case '=': return OpAssign; case '*': return OpMul; case '/': return OpDiv; case '<': return OpLessThan; case '>': return OpGreaterThan; case '%': return OpMod; case '!': return OpNot; case '+': { if (PrevIsOp == 0) return OpPlus; return OpUnaryPlus; } case '-': { if (PrevIsOp == 0) return OpMinus; return OpUnaryMinus; } } } else if (s[0] != 0 && s[1] == '=' && !s[2]) { // 2 chars, "something" equals operator switch (*s) { case '!': return OpNotEquals; case '=': return OpEquals; case '<': return OpLessThanEqual; case '>': return OpGreaterThanEqual; case '+': return OpPlusEquals; case '-': return OpMinusEquals; case '*': return OpMulEquals; case '/': return OpDivEquals; } } else if (s[0] == '+' && s[1] == '+' && !s[2]) { if (PrevIsOp == 0) return OpPostInc; return OpPreInc; } else if (s[0] == '-' && s[1] == '-' && !s[2]) { if (PrevIsOp == 0) return OpPostDec; return OpPreDec; } else if (s[0] == '&' && s[1] == '&' && !s[2]) { return OpAnd; } else if (s[0] == '|' && s[1] == '|' && !s[2]) { return OpOr; } return OpNull; } }; /// This class compiles the source down to byte code class GCompiler : public GScriptUtils { class GCompilerPriv *d; public: /// Constructor GCompiler(); ~GCompiler(); /// Compile the source into byte code. bool Compile ( GAutoPtr &Code, GScriptContext *SysContext, GScriptContext *UserContext, const char *FileName, const char *Script, GDom *Args ); }; /// This class is the VM for the byte language class GVirtualMachine : public GScriptUtils { friend class GVmDebuggerWnd; friend class LScriptArguments; class GVirtualMachinePriv *d; public: GVirtualMachine(GVmDebuggerCallback *callback = NULL); GVirtualMachine(GVirtualMachine *vm); ~GVirtualMachine(); /// Executes the whole script starting at the top GExecutionStatus Execute ( /// [In] The code to execute GCompiledCode *Code, /// [In] The instruction to start at... [defaults to the start of script) - uint32 StartOffset = 0, + uint32_t StartOffset = 0, /// [Optional] Log file for execution GStream *Log = NULL, /// Start the script execution straight away? bool StartImmediately = true, /// Optional return value GVariant *Return = NULL ); /// Execute just one method and return GExecutionStatus ExecuteFunction ( /// [In] The code to execute GCompiledCode *Code, /// [In] The function to execute GFunctionInfo *Func, /// [In/Out] The function's arguments LScriptArguments &Args, /// [Optional] Log file for execution GStream *Log = NULL, /// [Optional] Copy arguments back to this array LScriptArguments *ArgsOut = NULL ); // Debugging commands GVmDebugger *OpenDebugger(GCompiledCode *Code = NULL, const char *Assembly = NULL); bool StepInstruction(); bool StepLine(); bool StepOut(); bool BreakExecution(); bool Continue(); bool BreakPoint(const char *File, int Line, bool Add); bool BreakPoint(int Addr, bool Add); void SetBreakCpp(bool Brk); // Properties void SetTempPath(const char *Path); }; /// Scripting engine system functions class SystemFunctions : public GScriptContext { GScriptEngine *Engine; GStream *Log; #ifdef WINNATIVE HANDLE Brk; #endif GView *CastGView(GVariant &v); public: SystemFunctions(); ~SystemFunctions(); GStream *GetLog(); bool SetLog(GStream *log); void SetEngine(GScriptEngine *Eng); char *GetIncludeFile(char *FileName) { return NULL; } GHostFunc *GetCommands(); // String bool LoadString(LScriptArguments &Args); /// Formats a string bool Sprintf(LScriptArguments &Args); /// Formats a file size bool FormatSize(LScriptArguments &Args); /// Prints items to the console bool Print(LScriptArguments &Args); /// Converts args to string bool ToString(LScriptArguments &Args); // Object creation/deletion bool New(LScriptArguments &Args); bool Delete(LScriptArguments &Args); // File /// Reads a text file into a variable bool ReadTextFile(LScriptArguments &Args); /// Writes a text file from a variable bool WriteTextFile(LScriptArguments &Args); /// \brief Opens a file open dialog to select files. /// /// Args: GView *Parent, char *Patterns, /// char *InitFolder, bool Multiselect bool SelectFiles(LScriptArguments &Args); /// Lists file in folder /// /// Args; char *Path, [optional] char *Pattern /// Returns: List of DOM objects with the following fields: /// Name - File/dir name /// Size - Size of entry /// Folder - bool, true if folder /// Modified - LDateTime, modified time bool ListFiles(LScriptArguments &Args); /// Deletes a file bool DeleteFile(LScriptArguments &Args); /// Gets the current script path. bool CurrentScript(LScriptArguments &Args); /// Finds out if a path exists. bool PathExists(LScriptArguments &Args); /// Joins path segments together. bool PathJoin(LScriptArguments &Args); /// Returns the current OS path separator. bool PathSep(LScriptArguments &Args); // Time /// Sleeps a number of milliseconds bool Sleep(LScriptArguments &Args); /// Get the current tick count bool ClockTick(LScriptArguments &Args); /// Get the date time bool Now(LScriptArguments &Args); // Bitmaps /// Creates a memory context bool CreateSurface(LScriptArguments &Args); // User interface /// Standard alert message box bool MessageDlg(LScriptArguments &Args); /// Gets an input string from the user /// String GetInputDlg(Window Parent, String InitialValue, String Question, String Title[, bool IsPassword]); bool GetInputDlg(LScriptArguments &Args); /// Gets a view by id bool GetViewById(LScriptArguments &Args); // System /// Executes a command, waits for it to finish, then returns it's output: /// String Execute(String Application, String CmdLine); bool Execute(LScriptArguments &Args); /// Executes a command and doesn't wait for it to return: /// Bool System(String Application, String CmdLine); bool System(LScriptArguments &Args); /// Gets the operating system name. bool OsName(LScriptArguments &Args); /// Gets the operating system version. bool OsVersion(LScriptArguments &Args); }; #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,1832 +1,1832 @@ /* This file is included in both the GVirtualMachinePriv::Execute and GVirtualMachinePriv::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 GResolveRef(nm) GVariant *nm = #else #define Resolve() c.r++ #define GResolveRef(nm) // GVarRef * #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 GResolveRef(Var) Resolve(); - uint8 Type = *c.u8++; + uint8_t Type = *c.u8++; #if VM_DECOMP if (Log) Log->Print(" to %s\n", GVariant::TypeToString((GVariantType)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: { if (Log) Log->Print( "%s ICast warning: unknown type %i/%s\n", Code->AddrToSourceRef(CurrentScriptAddress), Var->Type, GVariant::TypeToString(Var->Type)); 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 GResolveRef(Dst) Resolve(); GResolveRef(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 GResolveRef(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 GResolveRef(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 GResolveRef(Dst) Resolve(); GResolveRef(Src) Resolve(); #ifdef VM_EXECUTE if (Dst->Str()) { size_t dlen = strlen(Dst->Str()); char *ss; GVariant 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 GResolveRef(Dst) Resolve(); GResolveRef(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 GResolveRef(Dst) Resolve(); GResolveRef(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 GResolveRef(Dst) Resolve(); GResolveRef(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 GResolveRef(Dst) Resolve(); GResolveRef(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 GResolveRef(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 GResolveRef(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 GResolveRef(Dst) Resolve(); GResolveRef(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 GResolveRef(Dst) Resolve(); GResolveRef(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 GResolveRef(Dst) Resolve(); GResolveRef(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 GResolveRef(Dst) Resolve(); GResolveRef(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 GResolveRef(Dst) Resolve(); GResolveRef(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 GResolveRef(Dst) Resolve(); GResolveRef(Src) Resolve(); #ifdef VM_EXECUTE *Dst = CompareVariants(Dst, Src) >= 0; #endif break; } case ICallMethod: { GFunc *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 GResolveRef(Ret) Resolve(); uint16 Args = *c.u16++; #ifdef VM_EXECUTE LScriptArguments Arg(Vm, Ret); #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 GHostFunc *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; } } #endif break; } case ICallScript: { int32 FuncAddr = *c.i32++; - if (FuncAddr < 0 || (uint32)FuncAddr >= Code->ByteCode.Length()) + if (FuncAddr < 0 || (uint32_t)FuncAddr >= Code->ByteCode.Length()) { 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 GArray Arg; #else GResolveRef(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[LocalsBase]; #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 GResolveRef(ReturnValue) Resolve(); #ifdef VM_EXECUTE if (Frames.Length() > 0) { StackFrame Sf = Frames[Frames.Length()-1]; GVarRef &Ret = Sf.ReturnValue; GVariant *RetVar = &Scope[Ret.Scope][Ret.Index]; // LgiTrace("IRet to %i:%i\n", Ret.Scope, Ret.Index); if (Ret.Scope == SCOPE_LOCAL) LgiAssert(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 GResolveRef(Dst) Resolve(); GResolveRef(Var) Resolve(); GResolveRef(Idx) Resolve(); #ifdef VM_EXECUTE switch (Var->Type) { case GV_LIST: { CheckParam(Var->Value.Lst); GVariant *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); GVariant *t = (GVariant*)Var->Value.Hash->Find(Idx->CastString()); if (t) *Dst = *t; else Dst->Empty(); break; } case GV_CUSTOM: { GCustomType *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; } default: { if (Log) Log->Print( "%s IArrayGet warning: Can't array deref variant type %i\n", Code->AddrToSourceRef(CurrentScriptAddress), Var->Type); 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 GResolveRef(Var) Resolve(); GResolveRef(Idx) Resolve(); GResolveRef(Val) Resolve(); #ifdef VM_EXECUTE switch (Var->Type) { case GV_LIST: { CheckParam(Var->Value.Lst); (*Var->Value.Lst).Insert(new GVariant(*Val), Idx->CastInt32()); break; } case GV_HASHTABLE: { CheckParam(Var->Value.Hash); GVariant *Old = (GVariant*)Var->Value.Hash->Find(Idx->CastString()); DeleteObj(Old); Var->Value.Hash->Add(Idx->CastString(), new GVariant(*Val)); break; } default: { if (Log) Log->Print( "%s IArraySet warning: Can't dereference type '%s'\n", Code->AddrToSourceRef(CurrentScriptAddress), GVariant::TypeToString(Var->Type)); 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 GResolveRef(Dst) Resolve(); GResolveRef(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 GResolveRef(Dst) Resolve(); GResolveRef(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 GResolveRef(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 GResolveRef(Dst) Resolve(); GResolveRef(Dom) Resolve(); GResolveRef(Name) Resolve(); GResolveRef(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_GSURFACE: { GDom *dom = Dom->CastDom(); CheckParam(dom != NULL); char *sName = Name->Str(); CheckParam(sName); bool Ret = dom->GetVariant(sName, *Dst, CastArrayIndex(Arr)); if (!Ret) { Dst->Empty(); if (Log) Log->Print("%s IDomGet warning: Unexpected %s member '%s'.\n", Code->AddrToSourceRef(CurrentScriptAddress), GVariant::TypeToString(Dom->Type), sName); 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(); if (Log) Log->Print("%s IDomGet warning: Unexpected %s member '%s'.\n", Code->AddrToSourceRef(CurrentScriptAddress), GVariant::TypeToString(Dom->Type), sName); Status = ScriptWarning; } break; } case GV_CUSTOM: { GCustomType *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); GDomProperty p = LgiStringToDomProp(sName); if (p == ObjLength) (*Dst) = (int)Dom->Value.Lst->Length(); break; } case GV_HASHTABLE: { CheckParam(Dom->Value.Hash); char *sName = Name->Str(); CheckParam(sName); GDomProperty p = LgiStringToDomProp(sName); if (p == ObjLength) (*Dst) = (int)Dom->Value.Hash->Length(); break; } case GV_BINARY: { char *sName = Name->Str(); CheckParam(sName); GDomProperty p = LgiStringToDomProp(sName); if (p == ObjLength) (*Dst) = Dom->Value.Binary.Length; break; } case GV_STRING: { char *sName = Name->Str(); CheckParam(sName); GDomProperty p = LgiStringToDomProp(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(); if (Log) Log->Print("%s IDomGet warning: Unexpected string member '%s'.\n", Code->AddrToSourceRef(CurrentScriptAddress), sName); Status = ScriptWarning; break; } } break; } case GV_NULL: { #if 1 if (Log) Log->Print("%s IDomGet warning: Can't deref NULL object.\n", Code->AddrToSourceRef(CurrentScriptAddress)); #else // If you want exceptions on NULL deref OnException(_FL, CurrentScriptAddress-1, "NULL Dom Ptr"); #endif Status = ScriptWarning; break; } default: { if (Log) Log->Print("%s IDomGet warning: Unexpected type %s (Src=%s:%i IP=0x%x).\n", Code->AddrToSourceRef(CurrentScriptAddress), GVariant::TypeToString(Dom->Type), _FL, CurrentScriptAddress); 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 GResolveRef(Dom) Resolve(); GResolveRef(Name) Resolve(); GResolveRef(Arr) Resolve(); GResolveRef(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_GSURFACE: { GDom *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), GVariant::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), GVariant::TypeToString(Dom->Type), sName); Status = ScriptWarning; } break; } case GV_CUSTOM: { GCustomType *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: { GDomProperty p = LgiStringToDomProp(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), GVariant::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 GVarRef DstRef = *c.r; #endif GResolveRef(Dst) Resolve(); GResolveRef(Dom) Resolve(); GResolveRef(Name) Resolve(); #ifdef VM_EXECUTE GResolveRef(Args) Resolve(); int ArgCount = Args->CastInt32(); char *sName = Name->Str(); CheckParam(sName) if (Dom->Type == GV_CUSTOM) { #define DEBUG_CUSTOM_METHOD_CALL 1 GCustomType *t = Dom->Value.Custom.Dom; CheckParam(t); GCustomType::Method *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 GString 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; } GArray Arg; Arg.Length(ArgCount); for (int i=0; iType); } else switch (Dom->Type) { case GV_DOM: case GV_STREAM: case GV_GSURFACE: { GDom *dom = Dom->CastDom(); CheckParam(dom); bool Ret = dom->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_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; GVariant *v = new GVariant; *v = *Arg[0]; Dom->Value.Lst->Insert(v, Index); } break; } case ContainerDelete: { for (unsigned i=0; iCastInt32(); GVariant *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: { GVariant *Param = Arg.Length() > 0 ? Arg[0] : NULL; Dom->Value.Lst->Sort(GVariantCmp, (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) { GVariant *v = new GVariant; *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) { GVariant *v = (GVariant*) 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: { GStringPipe p(256); List *Lst = Arg[0]->Value.Lst; const char *Sep = Dom->CastString(); GVariant *v = Lst->First(); if (v) { GVariant Tmp = *v; p.Print("%s", Tmp.CastString()); while ((v = Lst->Next())) { 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; } GVariant 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; GVariant *v = new GVariant; v->OwnStr(NewStr(c, next - c)); Dst->Value.Lst->Insert(v); c = next + SepLen; } if (c && *c) { GVariant *v = new GVariant; v->OwnStr(NewStr(c)); Dst->Value.Lst->Insert(v); } break; } case StrFind: { const char *s = Dom->Str(); if (!s) { *Dst = -1; break; } ssize_t sLen = strlen(s); const char *sub = Arg[0]->Str(); int start = Arg.Length() > 1 ? Arg[1]->CastInt32() : 0; int 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); const char *sub = Arg[0]->Str(); int start_idx = Arg.Length() > 1 ? Arg[1]->CastInt32() : 0; int end_idx = Arg.Length() > 2 ? Arg[2]->CastInt32() : -1; if (start_idx >= sLen) { *Dst = -1; break; } size_t sublen = strlen(sub); char *cur = (char*)s + start_idx; char *end = end_idx >= 0 ? cur + end_idx : NULL; 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: { char *s = Dom->Str(); if (s) { char *start = s; char *end = s + strlen(s); while (start < end && strchr(WhiteSpace, *start)) start++; while (end > start && strchr(WhiteSpace, end[-1])) end--; Dst->OwnStr(NewStr(start, end - start)); } else Dst->Empty(); break; } case StrSub: { char *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 = GVariant::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; } } #else GVariant *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 GResolveRef \ No newline at end of file diff --git a/src/common/Gdc2/15Bit.cpp b/src/common/Gdc2/15Bit.cpp --- a/src/common/Gdc2/15Bit.cpp +++ b/src/common/Gdc2/15Bit.cpp @@ -1,221 +1,221 @@ /** \file \author Matthew Allen \date 30/11/1999 \brief 15 bit primitives */ #include #include #include #include #include "Gdc2.h" #include "GPalette.h" /// 15 bit rgb applicators template class GdcApp15 : public GApplicator { protected: union { - uint8 *u8; + uint8_t *u8; uint16 *u16; Pixel *p; }; public: GdcApp15() { u8 = NULL; } const char *GetClass() { return "GdcApp15"; } bool SetSurface(GBmpMem *d, GPalette *p, GBmpMem *a) { if (d && d->Cs == Cs) { Dest = d; Pal = p; u8 = d->Base; Alpha = 0; return true; } else { LgiAssert(0); } return false; } void SetPtr(int x, int y) { LgiAssert(Dest && Dest->Base); u8 = Dest->Base + ((y * Dest->Line) + x + x); } void IncX() { p++; } void IncY() { u8 += Dest->Line; } void IncPtr(int X, int Y) { p += X; u8 += Y * Dest->Line; } void Set() { p->r = R15(c); p->g = G15(c); p->b = B15(c); } COLOUR Get() { return *u16; } }; template class GdcApp15Set : public GdcApp15 { public: const char *GetClass() { return "GdcApp15Set"; } #define InitSet15() \ REG union { \ Pixel px; \ uint16 upx; \ }; \ REG union { \ - uint8 *d8; \ + uint8_t *d8; \ uint16 *d16; \ }; \ px.r = R15(this->c); \ px.g = G15(this->c); \ px.b = B15(this->c); \ REG ssize_t line = this->Dest->Line; \ d8 = this->u8; void VLine(int height) { InitSet15(); while (height--) { *d16 = upx; d8 += line; } this->u8 = d8; } void Rectangle(int x, int y) { InitSet15(); while (y--) { REG uint16 *d = d16; REG uint16 *e = d + x; while (d < e) { *d++ = upx; } d8 += line; } this->u8 = d8; } bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha) { if (!Src) return false; switch (Src->Cs) { case CsIndex8: { Pixel map[256]; ZeroObj(map); for (int i=0; i<256; i++) { GdcRGB *p = SPal ? (*SPal)[i] : NULL; if (p) { map[i].r = G8bitTo5bit(p->r); map[i].g = G8bitTo5bit(p->g); map[i].b = G8bitTo5bit(p->b); } else { map[i].r = G8bitTo5bit(i); map[i].g = G8bitTo5bit(i); map[i].b = G8bitTo5bit(i); } } for (int y=0; yy; y++) { uchar *s = ((uchar*)Src->Base) + (y * Src->Line); Pixel *d = this->p; for (int x=0; xx; x++) { *d++ = map[*s++]; } this->u8 += this->Dest->Line; } break; } default: { GBmpMem Dst; Dst.Base = this->u8; Dst.x = Src->x; Dst.y = Src->y; Dst.Cs = this->Dest->Cs; Dst.Line = this->Dest->Line; if (!LgiRopUniversal(&Dst, Src, false)) { return false; } break; } } return true; } }; GApplicator *GApp15::Create(GColourSpace Cs, int Op) { if (Op == GDC_SET) { switch (Cs) { case CsRgb15: return new GdcApp15Set; case CsBgr15: return new GdcApp15Set; case CsArgb15: return new GdcApp15Set; case CsAbgr15: return new GdcApp15Set; default: break; } } return NULL; } diff --git a/src/common/Gdc2/16Bit.cpp b/src/common/Gdc2/16Bit.cpp --- a/src/common/Gdc2/16Bit.cpp +++ b/src/common/Gdc2/16Bit.cpp @@ -1,389 +1,389 @@ /** \file \author Matthew Allen \date 30/11/1999 \brief 16 bit primitives */ #include #include #include #include #include "Gdc2.h" #include "GPalette.h" /// 16 bit rgb applicators template class GdcApp16 : public GApplicator { protected: union { - uint8 *u8; + uint8_t *u8; uint16 *u16; Pixel *p; } Ptr; public: GdcApp16() { Ptr.u8 = NULL; } const char *GetClass() { return "GdcApp16"; } bool SetSurface(GBmpMem *d, GPalette *p, GBmpMem *a) { if (d && d->Cs == Cs) { Dest = d; Pal = p; Ptr.u8 = d->Base; Alpha = 0; return true; } else LgiAssert(0); return false; } void SetPtr(int x, int y) { LgiAssert(Dest && Dest->Base); Ptr.u8 = Dest->Base + ((y * Dest->Line) + x + x); } void IncX() { Ptr.p++; } void IncY() { Ptr.u8 += Dest->Line; } void IncPtr(int X, int Y) { Ptr.u16 += X; Ptr.u8 += Y * Dest->Line; } COLOUR Get() { return *Ptr.u16; } }; template class GdcApp16Set : public GdcApp16 { public: const char *GetClass() { return "GdcApp16Set"; } void Set() { register union { Pixel Px; uint16 u; }; Px.r = R16(this->c); Px.g = G16(this->c); Px.b = B16(this->c); *this->Ptr.u16 = u; } void VLine(int height) { register union { Pixel Px; uint16 u; }; Px.r = R16(this->c); Px.g = G16(this->c); Px.b = B16(this->c); while (height--) { *this->Ptr.u16 = u; this->Ptr.u8 += this->Dest->Line; } } void Rectangle(int x, int y) { register union { Pixel Px; uint16 u; }; Px.r = R16(this->c); Px.g = G16(this->c); Px.b = B16(this->c); while (y--) { uint16 *d = this->Ptr.u16; uint16 *e = d + x; while (d < e) *d++ = u; this->Ptr.u8 += this->Dest->Line; } } bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha) { if (!Src) return false; switch (Src->Cs) { case CsIndex8: { Pixel map[256]; if (SPal) { GdcRGB *p = (*SPal)[0]; for (int i=0; i<256; i++, p++) { map[i].r = G8bitTo5bit(p->r); map[i].g = G8bitTo6bit(p->g); map[i].b = G8bitTo5bit(p->b); } } else { for (int i=0; i<256; i++) { map[i].r = G8bitTo5bit(i); map[i].g = G8bitTo6bit(i); map[i].b = G8bitTo5bit(i); } } for (int y=0; yy; y++) { uchar *s = ((uchar*)Src->Base) + (y * Src->Line); Pixel *d = this->Ptr.p; for (int x=0; xx; x++) { *d++ = map[*s++]; } this->Ptr.u8 += this->Dest->Line; } break; } default: { GBmpMem Dst; Dst.Base = this->Ptr.u8; Dst.x = Src->x; Dst.y = Src->y; Dst.Cs = this->Dest->Cs; Dst.Line = this->Dest->Line; if (!LgiRopUniversal(&Dst, Src, false)) { return false; } break; } } return true; } }; template class GdcApp16And : public GdcApp16 { public: const char *GetClass() { return "GdcApp16And"; } void Set(); void VLine(int height); void Rectangle(int x, int y); bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha); }; template class GdcApp16Or : public GdcApp16 { public: const char *GetClass() { return "GdcApp16Or"; } void Set(); void VLine(int height); void Rectangle(int x, int y); bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha); }; template class GdcApp16Xor : public GdcApp16 { public: const char *GetClass() { return "GdcApp16Xor"; } void Set(); void VLine(int height); void Rectangle(int x, int y); bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha); }; GApplicator *GApp16::Create(GColourSpace Cs, int Op) { if (Cs == CsRgb16) { switch (Op) { case GDC_SET: return new GdcApp16Set; /* case GDC_AND: return new GdcApp16And; case GDC_OR: return new GdcApp16Or; case GDC_XOR: return new GdcApp16Xor; */ } } else if (Cs == CsBgr16) { switch (Op) { case GDC_SET: return new GdcApp16Set; /* case GDC_AND: return new GdcApp16And; case GDC_OR: return new GdcApp16Or; case GDC_XOR: return new GdcApp16Xor; */ } } return NULL; } //////////////////////////////////////////////////////////////////////////////////////// // 16 bit or sub functions /* void GdcApp16Or::Set() { *sPtr |= c; } void GdcApp16Or::VLine(int height) { while (height--) { *sPtr |= c; Ptr += Dest->Line; } } void GdcApp16Or::Rectangle(int x, int y) { while (y--) { for (int n=0; nLine; } } bool GdcApp16Or::Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha) { if (Src && Src->Cs == Dest->Cs) { uchar *s = Src->Base; for (int y=0; yy; y++) { MemOr(Ptr, s, Src->x * 2); s += Src->Line; Ptr += Dest->Line; } } return true; } // 16 bit AND sub functions void GdcApp16And::Set() { *sPtr &= c; } void GdcApp16And::VLine(int height) { while (height--) { *sPtr &= c; Ptr += Dest->Line; } } void GdcApp16And::Rectangle(int x, int y) { while (y--) { for (int n=0; nLine; } } bool GdcApp16And::Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha) { if (Src && Src->Cs == Dest->Cs) { uchar *s = Src->Base; for (int y=0; yy; y++) { MemAnd(Ptr, s, Src->x * 2); s += Src->Line; Ptr += Dest->Line; } } return true; } // 16 bit XOR sub functions void GdcApp16Xor::Set() { *sPtr ^= c; } void GdcApp16Xor::VLine(int height) { while (height--) { *sPtr ^= c; Ptr += Dest->Line; } } void GdcApp16Xor::Rectangle(int x, int y) { while (y--) { for (int n=0; nLine; } } bool GdcApp16Xor::Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha) { if (Src && Src->Cs == Dest->Cs) { uchar *s = Src->Base; for (int y=0; yy; y++) { MemXor(Ptr, s, Src->x * 2); s += Src->Line; Ptr += Dest->Line; } } return true; } */ \ No newline at end of file diff --git a/src/common/Gdc2/24Bit.cpp b/src/common/Gdc2/24Bit.cpp --- a/src/common/Gdc2/24Bit.cpp +++ b/src/common/Gdc2/24Bit.cpp @@ -1,715 +1,715 @@ /** \file \author Matthew Allen \date 21/3/1997 \brief 24 bit primitives */ #include #include #include #include #include "Gdc2.h" #include "GPalette.h" -#define BytePtr ((uint8*&)Ptr) +#define BytePtr ((uint8_t*&)Ptr) #undef NonPreMulOver24 #define NonPreMulOver24(c) d->c = ((s->c * sa) + (DivLut[d->c * 255] * o)) / 255 /// 24 bit rgb applicators class LgiClass GdcApp24 : public GApplicator { protected: union { - uint8 *u8; + uint8_t *u8; System24BitPixel *Ptr; }; public: GdcApp24() { Ptr = NULL; } const char *GetClass() { return "GdcApp24"; } bool SetSurface(GBmpMem *d, GPalette *p, GBmpMem *a); void SetPtr(int x, int y); void IncX(); void IncY(); void IncPtr(int X, int Y); COLOUR Get(); }; class LgiClass GdcApp24Set : public GdcApp24 { public: const char *GetClass() { return "GdcApp24Set"; } void Set(); void VLine(int height); void Rectangle(int x, int y); bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha); }; class LgiClass GdcApp24And : public GdcApp24 { public: const char *GetClass() { return "GdcApp24And"; } void Set(); void VLine(int height); void Rectangle(int x, int y); bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha); }; class LgiClass GdcApp24Or : public GdcApp24 { public: const char *GetClass() { return "GdcApp24Or"; } void Set(); void VLine(int height); void Rectangle(int x, int y); bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha); }; class LgiClass GdcApp24Xor : public GdcApp24 { public: const char *GetClass() { return "GdcApp24Xor"; } void Set(); void VLine(int height); void Rectangle(int x, int y); bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha); }; template class App24 : public GApplicator { union { - uint8 *u8; + uint8_t *u8; Pixel *p; }; int ConstAlpha; GPalette *PalAlpha; public: App24() { p = NULL; ConstAlpha = 255; PalAlpha = NULL; } int GetVar(int Var) { LgiAssert(0) ; return 0; } int SetVar(int Var, NativeInt Value) { switch (Var) { case GAPP_ALPHA_A: { ConstAlpha = (int)Value; break; } case GAPP_ALPHA_PAL: { PalAlpha = (GPalette*)Value; break; } } return 0; } bool SetSurface(GBmpMem *d, GPalette *pal = NULL, GBmpMem *a = NULL) { if (d && d->Cs == ColourSpace) { Dest = d; Pal = pal; p = (Pixel*) d->Base; Alpha = 0; return true; } return false; } void SetPtr(int x, int y) { p = (Pixel*) (Dest->Base + (y * Dest->Line) + (x * sizeof(Pixel))); } void IncX() { p++; } void IncY() { u8 += Dest->Line; } void IncPtr(int X, int Y) { p += X; u8 += Y * Dest->Line; } void Set() { p->r = p24.r; p->g = p24.g; p->b = p24.b; } COLOUR Get() { return Rgb24(p->r, p->g, p->b); } void VLine(int height) { Pixel cp; cp.r = p24.r; cp.g = p24.g; cp.b = p24.b; while (height-- > 0) { *p = cp; u8 += Dest->Line; } } void Rectangle(int x, int y) { Pixel cp; cp.r = p24.r; cp.g = p24.g; cp.b = p24.b; while (y-- > 0) { Pixel *i = p, *e = i + x; while (i < e) { *i++ = cp; } u8 += Dest->Line; } } template bool CopyBlt(GBmpMem *Src) { for (int y=0; yy; y++) { Pixel *d = p; T *s = (T*) (Src->Base + (y * Src->Line)); T *e = s + Src->x; while (s < e) { d->r = s->r; d->g = s->g; d->b = s->b; s++; d++; } u8 += Dest->Line; } return true; } template bool AlphaBlt(GBmpMem *Src, GBmpMem *SrcAlpha) { uchar *DivLut = Div255Lut; for (int y=0; yy; y++) { Pixel *d = p; T *s = (T*) (Src->Base + (y * Src->Line)); T *e = s + Src->x; - uint8 *a = Src->Base + (y * SrcAlpha->Line); + uint8_t *a = Src->Base + (y * SrcAlpha->Line); while (s < e) { - uint8 sa = *a++; + uint8_t sa = *a++; if (sa == 255) { d->r = s->r; d->g = s->g; d->b = s->b; } else if (sa > 0) { - uint8 o = 255 - sa; + uint8_t o = 255 - sa; NonPreMulOver24(r); NonPreMulOver24(g); NonPreMulOver24(b); } s++; d++; } u8 += Dest->Line; } return true; } bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha = NULL) { if (!Src) return false; if (!SrcAlpha) { if (Src->Cs == CsIndex8) { Pixel map[256]; for (int i=0; i<256; i++) { GdcRGB *rgb = SPal ? (*SPal)[i] : NULL; if (rgb) { map[i].r = rgb->r; map[i].g = rgb->g; map[i].b = rgb->b; } else { map[i].r = i; map[i].g = i; map[i].b = i; } } for (int y=0; yy; y++) { - REG uint8 *s = Src->Base + (y * Src->Line); + REG uint8_t *s = Src->Base + (y * Src->Line); REG Pixel *d = p, *e = d + Src->x; while (d < e) { *d++ = map[*s++]; } u8 += Dest->Line; } } else { GBmpMem Dst; Dst.Base = u8; Dst.x = Src->x; Dst.y = Src->y; Dst.Cs = Dest->Cs; Dst.Line = Dest->Line; if (!LgiRopUniversal(&Dst, Src, false)) { return false; } } } else { switch (Src->Cs) { #define AlphaCase(name) \ case Cs##name: return AlphaBlt(Src, SrcAlpha); AlphaCase(Rgb24); AlphaCase(Bgr24); AlphaCase(Xrgb32); AlphaCase(Xbgr32); AlphaCase(Rgbx32); AlphaCase(Bgrx32); AlphaCase(Argb32); AlphaCase(Abgr32); AlphaCase(Rgba32); AlphaCase(Bgra32); default: LgiAssert(!"Impl me."); break; } } return false; } }; GApplicator *GApp24::Create(GColourSpace Cs, int Op) { if (Cs == System24BitColourSpace) { switch (Op) { case GDC_SET: return new GdcApp24Set; case GDC_AND: return new GdcApp24And; case GDC_OR: return new GdcApp24Or; case GDC_XOR: return new GdcApp24Xor; } } else { if (Op != GDC_SET) return NULL; switch (Cs) { #define Case24(name) \ case Cs##name: \ return new App24(); Case24(Rgb24); Case24(Bgr24); default: break; } } return 0; } ////////////////////////////////////////////////////////////////////////////////////// #define GetRGB(c) \ - REG uint8 R = p24.r; \ - REG uint8 G = p24.g; \ - REG uint8 B = p24.b + REG uint8_t R = p24.r; \ + REG uint8_t G = p24.g; \ + REG uint8_t B = p24.b bool GdcApp24::SetSurface(GBmpMem *d, GPalette *p, GBmpMem *a) { if (d && d->Cs == System24BitColourSpace) { Dest = d; Pal = p; Ptr = (System24BitPixel*) d->Base; Alpha = 0; return true; } return false; } void GdcApp24::SetPtr(int x, int y) { LgiAssert(Dest && Dest->Base); - Ptr = (System24BitPixel*) ((uint8*)Dest->Base + (y * Dest->Line) + (x * 3)); + Ptr = (System24BitPixel*) ((uint8_t*)Dest->Base + (y * Dest->Line) + (x * 3)); } void GdcApp24::IncX() { ((char*&)Ptr) += 3; } void GdcApp24::IncY() { ((char*&)Ptr) += Dest->Line; } void GdcApp24::IncPtr(int X, int Y) { ((char*&)Ptr) += (Y * Dest->Line) + (X * 3); } COLOUR GdcApp24::Get() { return Rgb24(Ptr->r, Ptr->g, Ptr->b); } // 24 bit set sub functions void GdcApp24Set::Set() { GetRGB(c); Ptr->r = R; Ptr->g = G; Ptr->b = B; } void GdcApp24Set::VLine(int height) { GetRGB(c); while (height--) { Ptr->r = R; Ptr->g = G; Ptr->b = B; ((char*&)Ptr) += Dest->Line; } } void GdcApp24Set::Rectangle(int x, int y) { GetRGB(c); while (y--) { System24BitPixel *p = Ptr; System24BitPixel *e = Ptr + x; while (p < e) { p->r = R; p->g = G; p->b = B; p++; } ((char*&)Ptr) += Dest->Line; } } bool GdcApp24Set::Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha) { if (Src) { switch (Src->Cs) { case CsIndex8: { if (SPal) { System24BitPixel c[256]; for (int i=0; i<256; i++) { GdcRGB *p = (*SPal)[i]; if (p) { c[i].r = p->r; c[i].g = p->g; c[i].b = p->b; } else { c[i].r = 0; c[i].g = 0; c[i].b = 0; } } for (int y=0; yy; y++) { System24BitPixel *d = Ptr; uchar *s = Src->Base + (y * Src->Line); uchar *e = s + Src->x; while (s < e) { d->r = c[*s].r; d->g = c[*s].g; d->b = c[*s].b; s++; d++; } - ((uint8*&)Ptr) += Dest->Line; + ((uint8_t*&)Ptr) += Dest->Line; } } else { for (int y=0; yy; y++) { System24BitPixel *d = Ptr; uchar *s = Src->Base + (y * Src->Line); uchar *e = s + Src->x; while (s < e) { d->r = *s; d->g = *s; d->b = *s++; d++; } - ((uint8*&)Ptr) += Dest->Line; + ((uint8_t*&)Ptr) += Dest->Line; } } break; } default: { GBmpMem Dst; Dst.Base = u8; Dst.x = Src->x; Dst.y = Src->y; Dst.Cs = Dest->Cs; Dst.Line = Dest->Line; if (!LgiRopUniversal(&Dst, Src, false)) { return false; } break; } } } return true; } // 24 bit or sub functions void GdcApp24Or::Set() { GetRGB(c); Ptr->b |= B; Ptr->g |= G; Ptr->r |= R; } void GdcApp24Or::VLine(int height) { GetRGB(c); while (height--) { Ptr->b |= B; Ptr->g |= G; Ptr->r |= R; ((char*&)Ptr) += Dest->Line; } } void GdcApp24Or::Rectangle(int x, int y) { GetRGB(c); while (y--) { for (int n=0; nb |= B; Ptr->g |= G; Ptr->r |= R; Ptr++; } BytePtr += Dest->Line - (x * 3); } } bool GdcApp24Or::Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha) { if (Src) { switch (Src->Cs) { default: break; case System24BitColourSpace: { uchar *s = Src->Base; for (int y=0; yy; y++) { MemOr(Ptr, s, Src->x * 3); s += Src->Line; BytePtr += Dest->Line; } break; } } } return true; } // 24 bit AND sub functions void GdcApp24And::Set() { GetRGB(c); Ptr->b &= B; Ptr->g &= G; Ptr->r &= R; } void GdcApp24And::VLine(int height) { GetRGB(c); while (height--) { Ptr->b &= B; Ptr->g &= G; Ptr->r &= R; BytePtr += Dest->Line; } } void GdcApp24And::Rectangle(int x, int y) { GetRGB(c); while (y--) { for (int n=0; nb &= B; Ptr->g &= G; Ptr->b &= R; Ptr++; } BytePtr += Dest->Line - (x * 3); } } bool GdcApp24And::Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha) { if (Src && Src->Cs == Dest->Cs) { uchar *s = Src->Base; for (int y=0; yy; y++) { MemAnd(Ptr, s, Src->x * 3); s += Src->Line; BytePtr += Dest->Line; } } return true; } // 24 bit XOR sub functions void GdcApp24Xor::Set() { GetRGB(c); Ptr->b ^= B; Ptr->g ^= G; Ptr->r ^= R; } void GdcApp24Xor::VLine(int height) { GetRGB(c); while (height--) { Ptr->b ^= B; Ptr->g ^= G; Ptr->r ^= R; BytePtr += Dest->Line; } } void GdcApp24Xor::Rectangle(int x, int y) { GetRGB(c); while (y--) { for (int n=0; nb ^= B; Ptr->g ^= G; Ptr->r ^= R; Ptr++; } BytePtr += Dest->Line - (x * 3); } } bool GdcApp24Xor::Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha) { if (Src && Src->Cs == Dest->Cs) { uchar *s = Src->Base; for (int y=0; yy; y++) { MemXor(Ptr, s, Src->x * 3); s += Src->Line; BytePtr += Dest->Line; } } return true; } diff --git a/src/common/Gdc2/32Bit.cpp b/src/common/Gdc2/32Bit.cpp --- a/src/common/Gdc2/32Bit.cpp +++ b/src/common/Gdc2/32Bit.cpp @@ -1,776 +1,776 @@ /** \file \author Matthew Allen \date 29/8/1997 \brief 32 bit primitives */ #include #include #include #include #include "Gdc2.h" #include "GPalette.h" #include "GPixelRops.h" #undef NonPreMulOver32 #undef NonPreMulAlpha ///////////////////////////////////////////////////////////////////////////////////////// template class App32Base : public GApplicator { protected: union { - uint8 *u8; + uint8_t *u8; Pixel *p; }; GPalette *PalAlpha; public: App32Base() { p = NULL; } int GetVar(int Var) { return 0; } int SetVar(int Var, NativeInt Value) { return 0; } bool SetSurface(GBmpMem *d, GPalette *pal = NULL, GBmpMem *a = NULL) { if (d && d->Cs == ColourSpace) { Dest = d; Pal = pal; u8 = d->Base; Alpha = 0; return true; } return false; } void SetPtr(int x, int y) { u8 = Dest->Base + (y * Dest->Line); p += x; } void IncX() { p++; } void IncY() { u8 += Dest->Line; } void IncPtr(int X, int Y) { p += X; u8 += Y * Dest->Line; } }; template class App32NoAlpha : public App32Base { public: void Set() { this->p->r = this->p32.r; this->p->g = this->p32.g; this->p->b = this->p32.b; } COLOUR Get() { return Rgb32(this->p->r, this->p->g, this->p->b); } void VLine(int height) { REG Pixel cp; cp.r = this->p32.r; cp.g = this->p32.g; cp.b = this->p32.b; while (height-- > 0) { *this->p = cp; this->u8 += this->Dest->Line; } } void Rectangle(int x, int y) { REG Pixel cp; cp.r = this->p32.r; cp.g = this->p32.g; cp.b = this->p32.b; REG int lines = y; REG ssize_t ystep = this->Dest->Line; while (lines-- > 0) { REG Pixel *i = this->p, *e = i + x; while (i < e) { *i++ = cp; } this->u8 += ystep; } } template bool AlphaBlt(GBmpMem *Src, GBmpMem *SrcAlpha) { uchar *DivLut = Div255Lut; for (int y=0; yy; y++) { REG Pixel *d = this->p; REG T *s = (T*) (Src->Base + (y * Src->Line)); REG T *e = s + Src->x; - REG uint8 *a = Src->Base + (y * SrcAlpha->Line); + REG uint8_t *a = Src->Base + (y * SrcAlpha->Line); while (s < e) { - uint8 sa = *a++; + uint8_t sa = *a++; if (sa == 255) { d->r = s->r; d->g = s->g; d->b = s->b; } else if (sa > 0) { - uint8 o = 255 - sa; + uint8_t o = 255 - sa; // NonPreMulAlpha; #define NonPreMulOver32NoAlpha(c) d->c = ((s->c * sa) + (DivLut[d->c * 255] * o)) / 255 NonPreMulOver32NoAlpha(r); NonPreMulOver32NoAlpha(g); NonPreMulOver32NoAlpha(b); } s++; d++; } this->u8 += this->Dest->Line; } return true; } bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha = NULL) { if (!Src) return false; if (!SrcAlpha) { if (this->Dest->Cs == Src->Cs) { REG uchar *s = Src->Base; for (REG int y=0; yy; y++) { MemCpy(this->p, s, Src->x * sizeof(Pixel)); s += Src->Line; this->u8 += this->Dest->Line; } } else if (Src->Cs == CsIndex8) { Pixel map[256]; for (int i=0; i<256; i++) { GdcRGB *rgb = SPal ? (*SPal)[i] : NULL; if (rgb) { map[i].r = rgb->r; map[i].g = rgb->g; map[i].b = rgb->b; } else { map[i].r = i; map[i].g = i; map[i].b = i; } } for (int y=0; yy; y++) { - REG uint8 *s = Src->Base + (y * Src->Line); + REG uint8_t *s = Src->Base + (y * Src->Line); REG Pixel *d = this->p, *e = d + Src->x; while (d < e) { *d++ = map[*s++]; } this->u8 += this->Dest->Line; } } else { GBmpMem Dst; Dst.Base = this->u8; Dst.x = Src->x; Dst.y = Src->y; Dst.Cs = this->Dest->Cs; Dst.Line = this->Dest->Line; if (!LgiRopUniversal(&Dst, Src, false)) { return false; } } } else { switch (Src->Cs) { #define AlphaCase(name) \ case Cs##name: return AlphaBlt(Src, SrcAlpha); AlphaCase(Rgb24); AlphaCase(Bgr24); AlphaCase(Xrgb32); AlphaCase(Xbgr32); AlphaCase(Rgbx32); AlphaCase(Bgrx32); AlphaCase(Argb32); AlphaCase(Abgr32); AlphaCase(Rgba32); AlphaCase(Bgra32); #undef AlphaCase default: LgiAssert(!"Impl me."); break; } } return false; } }; template class App32Alpha : public App32Base { public: #define InitColour() \ REG Pixel cp; \ if (this->Dest->PreMul()) \ { \ cp.r = ((int)this->p32.r * this->p32.a) / 255; \ cp.g = ((int)this->p32.g * this->p32.a) / 255; \ cp.b = ((int)this->p32.b * this->p32.a) / 255; \ cp.a = this->p32.a; \ } \ else \ { \ cp.r = this->p32.r; \ cp.g = this->p32.g; \ cp.b = this->p32.b; \ cp.a = this->p32.a; \ } void Set() { InitColour(); *this->p = cp; } COLOUR Get() { return Rgba32(this->p->r, this->p->g, this->p->b, this->p->a); } void VLine(int height) { InitColour(); while (height-- > 0) { *this->p = cp; this->u8 += this->Dest->Line; } } void Rectangle(int x, int y) { InitColour(); REG int lines = y; REG ssize_t ystep = this->Dest->Line; while (lines-- > 0) { REG Pixel *i = this->p, *e = i + x; while (i < e) { *i++ = cp; } this->u8 += ystep; } } template bool CopyBlt24(GBmpMem *Src) { for (int y=0; yy; y++) { REG Pixel *d = this->p; REG T *s = (T*) (Src->Base + (y * Src->Line)); REG T *e = s + Src->x; while (s < e) { d->r = s->r; d->g = s->g; d->b = s->b; d->a = 255; s++; d++; } this->u8 += this->Dest->Line; } return true; } template bool CopyBlt32(GBmpMem *Src) { for (int y=0; yy; y++) { REG Pixel *d = this->p; REG T *s = (T*) (Src->Base + (y * Src->Line)); REG T *e = s + Src->x; while (s < e) { d->r = s->r; d->g = s->g; d->b = s->b; d->a = s->a; s++; d++; } this->u8 += this->Dest->Line; } return true; } template bool AlphaBlt24(GBmpMem *Src, GBmpMem *SrcAlpha) { uchar *DivLut = Div255Lut; for (int y=0; yy; y++) { REG Pixel *d = this->p; REG T *s = (T*) (Src->Base + (y * Src->Line)); REG T *e = s + Src->x; - REG uint8 *a = Src->Base + (y * SrcAlpha->Line); + REG uint8_t *a = Src->Base + (y * SrcAlpha->Line); while (s < e) { - uint8 sa = *a++; + uint8_t sa = *a++; if (sa == 255) { d->r = s->r; d->g = s->g; d->b = s->b; d->a = 255; } else if (sa > 0) { OverNpm24toNpm32(s, d, sa); } s++; d++; } this->u8 += this->Dest->Line; } return true; } template bool AlphaBlt32(GBmpMem *Src, GBmpMem *SrcAlpha) { REG uchar *DivLut = Div255Lut; for (int y=0; yy; y++) { REG Pixel *d = this->p; REG T *s = (T*) (Src->Base + (y * Src->Line)); REG T *e = s + Src->x; - REG uint8 *a = Src->Base + (y * SrcAlpha->Line); + REG uint8_t *a = Src->Base + (y * SrcAlpha->Line); while (s < e) { - uint8 sa = *a++; + uint8_t sa = *a++; if (sa == 255) { d->r = s->r; d->g = s->g; d->b = s->b; d->a = 255; } else if (sa > 0) { OverNpm32toNpm32(s, d); } s++; d++; } this->u8 += this->Dest->Line; } return true; } void ConvertPreMul(GBmpMem *m) { REG uchar *DivLut = Div255Lut; for (int y=0; yy; y++) { REG Pixel *d = (Pixel*) (m->Base + (y * m->Line)); REG Pixel *e = d + m->x; while (d < e) { d->r = DivLut[d->r * d->a]; d->g = DivLut[d->g * d->a]; d->b = DivLut[d->b * d->a]; d++; } } } bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha = NULL) { if (!Src) return false; if (Src->Cs == CsIndex8) { Pixel map[256]; for (int i=0; i<256; i++) { GdcRGB *p = SPal ? (*SPal)[i] : NULL; if (p) { map[i].r = p->r; map[i].g = p->g; map[i].b = p->b; } else { map[i].r = map[i].g = map[i].b = i; } map[i].a = 255; } - uint8 *in = Src->Base; + uint8_t *in = Src->Base; REG uchar *DivLut = Div255Lut; for (int y=0; yy; y++) { - REG uint8 *i = in; + REG uint8_t *i = in; REG Pixel *o = this->p, *e = this->p + Src->x; if (SrcAlpha) { - REG uint8 *alpha = SrcAlpha->Base + (y * SrcAlpha->Line); + REG uint8_t *alpha = SrcAlpha->Base + (y * SrcAlpha->Line); while (o < e) { REG Pixel *src = map + *i++; src->a = *alpha++; if (src->a == 255) { o->r = src->r; o->g = src->g; o->b = src->b; o->a = 255; } else if (src->a > 0) { OverNpm32toNpm32(src, o); } o++; } if (this->Dest->PreMul()) { o = this->p; while (o < e) { o->r = DivLut[o->r * o->a]; o->g = DivLut[o->g * o->a]; o->b = DivLut[o->b * o->a]; o++; } } } else { while (o < e) { *o++ = map[*i++]; } } in += Src->Line; this->u8 += this->Dest->Line; } } else if (!SrcAlpha) { if (this->Dest->Cs == Src->Cs) { REG uchar *s = Src->Base; for (REG int y=0; yy; y++) { MemCpy(this->p, s, Src->x * sizeof(Pixel)); s += Src->Line; this->u8 += this->Dest->Line; } } else { GBmpMem Dst; Dst.Base = this->u8; Dst.x = Src->x; Dst.y = Src->y; Dst.Cs = this->Dest->Cs; Dst.Line = this->Dest->Line; Dst.PreMul(this->Dest->PreMul()); if (!LgiRopUniversal(&Dst, Src, false)) { return false; } if (this->Dest->PreMul() && !Src->PreMul()) { ConvertPreMul(&Dst); } } } else { switch (Src->Cs) { #define AlphaCase(name, sz) \ case Cs##name: return AlphaBlt##sz(Src, SrcAlpha); AlphaCase(Rgb24, 24); AlphaCase(Bgr24, 24); AlphaCase(Xrgb32, 24); AlphaCase(Xbgr32, 24); AlphaCase(Rgbx32, 24); AlphaCase(Bgrx32, 24); AlphaCase(Argb32, 32); AlphaCase(Abgr32, 32); AlphaCase(Rgba32, 32); AlphaCase(Bgra32, 32); #undef AlphaCase default: LgiAssert(!"Impl me."); break; } } return false; } }; ///////////////////////////////////////////////////////////////////////////////////////// //, a = this->p32.a #define CreateOpNoAlpha(name, opcode, alpha) \ template \ class App32##name##alpha : public App32Base \ { \ public: \ void Set() { this->p->r opcode this->p32.r; this->p->g opcode this->p32.g; \ this->p->b opcode this->p32.b; } \ COLOUR Get() { return Rgb32(this->p->r, this->p->g, this->p->b); } \ void VLine(int height) \ { \ - REG uint8 r = this->p32.r, g = this->p32.g, b = this->p32.b; \ + REG uint8_t r = this->p32.r, g = this->p32.g, b = this->p32.b; \ while (height-- > 0) \ { \ this->p->r opcode r; this->p->g opcode g; this->p->b opcode b; \ this->u8 += this->Dest->Line; \ } \ } \ void Rectangle(int x, int y) \ { \ - REG uint8 r = this->p32.r, g = this->p32.g, b = this->p32.b; \ + REG uint8_t r = this->p32.r, g = this->p32.g, b = this->p32.b; \ REG int lines = y; \ REG ssize_t ystep = this->Dest->Line; \ while (lines-- > 0) \ { \ REG Pixel *i = this->p, *e = i + x; \ while (i < e) \ { \ i->r opcode r; i->g opcode g; i->b opcode b; \ i++; \ } \ this->u8 += ystep; \ } \ } \ bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha = 0) { return false; } \ }; #define CreateOpAlpha(name, opcode, alpha) \ template \ class App32##name##alpha : public App32Base \ { \ public: \ void Set() { this->p->r opcode this->p32.r; this->p->g opcode this->p32.g; \ this->p->b opcode this->p32.b; this->p->a opcode this->p32.a; } \ COLOUR Get() { return Rgba32(this->p->r, this->p->g, this->p->b, this->p->a); } \ void VLine(int height) \ { \ - REG uint8 r = this->p32.r, g = this->p32.g, b = this->p32.b, a = this->p32.a; \ + REG uint8_t r = this->p32.r, g = this->p32.g, b = this->p32.b, a = this->p32.a; \ while (height-- > 0) \ { \ this->p->r opcode r; this->p->g opcode g; this->p->b opcode b; \ this->p->a opcode a; \ this->u8 += this->Dest->Line; \ } \ } \ void Rectangle(int x, int y) \ { \ - REG uint8 r = this->p32.r, g = this->p32.g, b = this->p32.b, a = this->p32.a; \ + REG uint8_t r = this->p32.r, g = this->p32.g, b = this->p32.b, a = this->p32.a; \ REG int lines = y; \ REG ssize_t ystep = this->Dest->Line; \ while (lines-- > 0) \ { \ REG Pixel *i = this->p, *e = i + x; \ while (i < e) \ { \ i->r opcode r; i->g opcode g; i->b opcode b; \ i->a opcode a; \ i++; \ } \ this->u8 += ystep; \ } \ } \ bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha = 0) { return false; } \ }; CreateOpNoAlpha(And, &=, NoAlpha) CreateOpAlpha(And, &=, Alpha) CreateOpNoAlpha(Or, |=, NoAlpha) CreateOpAlpha(Or, |=, Alpha) CreateOpNoAlpha(Xor, ^=, NoAlpha) CreateOpAlpha(Xor, ^=, Alpha) ///////////////////////////////////////////////////////////////////////////////////////// GApplicator *GApp32::Create(GColourSpace Cs, int Op) { if (Op == GDC_SET) { switch (Cs) { #define AppCase(name, alpha) \ case Cs##name: return new App32##alpha(); AppCase(Rgba32, Alpha); AppCase(Bgra32, Alpha); AppCase(Argb32, Alpha); AppCase(Abgr32, Alpha); AppCase(Xrgb32, NoAlpha); AppCase(Rgbx32, NoAlpha); AppCase(Xbgr32, NoAlpha); AppCase(Bgrx32, NoAlpha); default: break; #undef AppCase } } else if (Op == GDC_OR) { switch (Cs) { #define AppCase(name, alpha) \ case Cs##name: return new App32Or##alpha(); AppCase(Rgba32, Alpha); AppCase(Bgra32, Alpha); AppCase(Argb32, Alpha); AppCase(Abgr32, Alpha); AppCase(Xrgb32, NoAlpha); AppCase(Rgbx32, NoAlpha); AppCase(Xbgr32, NoAlpha); AppCase(Bgrx32, NoAlpha); default: break; #undef AppCase } } else if (Op == GDC_AND) { switch (Cs) { #define AppCase(name, alpha) \ case Cs##name: return new App32And##alpha(); AppCase(Rgba32, Alpha); AppCase(Bgra32, Alpha); AppCase(Argb32, Alpha); AppCase(Abgr32, Alpha); AppCase(Xrgb32, NoAlpha); AppCase(Rgbx32, NoAlpha); AppCase(Xbgr32, NoAlpha); AppCase(Bgrx32, NoAlpha); default: break; #undef AppCase } } else if (Op == GDC_XOR) { switch (Cs) { #define AppCase(name, alpha) \ case Cs##name: return new App32Xor##alpha(); AppCase(Rgba32, Alpha); AppCase(Bgra32, Alpha); AppCase(Argb32, Alpha); AppCase(Abgr32, Alpha); AppCase(Xrgb32, NoAlpha); AppCase(Rgbx32, NoAlpha); AppCase(Xbgr32, NoAlpha); AppCase(Bgrx32, NoAlpha); default: break; #undef AppCase } } return NULL; } diff --git a/src/common/Gdc2/48Bit.cpp b/src/common/Gdc2/48Bit.cpp --- a/src/common/Gdc2/48Bit.cpp +++ b/src/common/Gdc2/48Bit.cpp @@ -1,319 +1,319 @@ /** \file \author Matthew Allen \date 24/2/2014 \brief 48 bit primitives */ #include #include #include #include #include "Gdc2.h" #include "GPalette.h" #define BytePtr ((uint8*&)Ptr) #undef NonPreMulOver48 #define NonPreMulOver48(c) d->c = ((s->c * sa) + ((d->c * 0xffff) / 0xffff * o)) / 0xffff template class App48 : public GApplicator { union { - uint8 *u8; + uint8_t *u8; Pixel *p; }; int Op; int ConstAlpha; GPalette *PalAlpha; public: App48(int op) { Op = op; p = NULL; ConstAlpha = 0xffff; PalAlpha = NULL; } int GetVar(int Var) { switch (Var) { case GAPP_ALPHA_A: { return ConstAlpha; break; } default: { LgiAssert(!"impl me."); break; } } return 0; } int SetVar(int Var, NativeInt Value) { switch (Var) { case GAPP_ALPHA_A: { ConstAlpha = (int)Value; break; } case GAPP_ALPHA_PAL: { PalAlpha = (GPalette*)Value; break; } default: { LgiAssert(!"impl me."); break; } } return 0; } bool SetSurface(GBmpMem *d, GPalette *pal = NULL, GBmpMem *a = NULL) { if (d && d->Cs == ColourSpace) { Dest = d; Pal = pal; p = (Pixel*) d->Base; Alpha = 0; return true; } return false; } void SetPtr(int x, int y) { p = (Pixel*) (Dest->Base + (y * Dest->Line) + (x * sizeof(Pixel))); } void IncX() { p++; } void IncY() { u8 += Dest->Line; } void IncPtr(int X, int Y) { p += X; u8 += Y * Dest->Line; } void Set() { p->r = ((uint16)p24.r << 8) | p24.r; p->g = ((uint16)p24.g << 8) | p24.g; p->b = ((uint16)p24.b << 8) | p24.b; } COLOUR Get() { return Rgb24(p->r >> 8, p->g >> 8, p->b >> 8); } void VLine(int height) { Pixel cp; cp.r = ((uint16)p24.r << 8) | p24.r; cp.g = ((uint16)p24.g << 8) | p24.g; cp.b = ((uint16)p24.b << 8) | p24.b; while (height-- > 0) { *p = cp; u8 += Dest->Line; } } void Rectangle(int x, int y) { Pixel cp; cp.r = ((uint16)p24.r << 8) | p24.r; cp.g = ((uint16)p24.g << 8) | p24.g; cp.b = ((uint16)p24.b << 8) | p24.b; while (y-- > 0) { Pixel *i = p, *e = i + x; while (i < e) { *i++ = cp; } u8 += Dest->Line; } } template bool CopyBlt(GBmpMem *Src) { for (int y=0; yy; y++) { Pixel *d = p; T *s = (T*) (Src->Base + (y * Src->Line)); T *e = s + Src->x; while (s < e) { d->r = s->r; d->g = s->g; d->b = s->b; s++; d++; } u8 += Dest->Line; } return true; } template bool AlphaBlt(GBmpMem *Src, GBmpMem *SrcAlpha) { for (int y=0; yy; y++) { Pixel *d = p; T *s = (T*) (Src->Base + (y * Src->Line)); T *e = s + Src->x; uint16 *a = (uint16*) (Src->Base + (y * SrcAlpha->Line)); while (s < e) { uint16 sa = *a++; if (sa == 255) { d->r = s->r; d->g = s->g; d->b = s->b; } else if (sa > 0) { uint16 o = 0xffff - sa; d->r = ((s->r * sa) + (d->r * o)) / 0xffff; d->g = ((s->g * sa) + (d->g * o)) / 0xffff; d->b = ((s->b * sa) + (d->b * o)) / 0xffff; } s++; d++; } u8 += Dest->Line; } return true; } bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha = NULL) { if (!Src) return false; if (!SrcAlpha) { if (Src->Cs == CsIndex8) { Pixel map[256]; for (int i=0; i<256; i++) { GdcRGB *rgb = SPal ? (*SPal)[i] : NULL; if (rgb) { map[i].r = G8bitTo16bit(rgb->r); map[i].g = G8bitTo16bit(rgb->g); map[i].b = G8bitTo16bit(rgb->b); } else { map[i].r = G8bitTo16bit(i); map[i].g = G8bitTo16bit(i); map[i].b = G8bitTo16bit(i); } } for (int y=0; yy; y++) { - REG uint8 *s = Src->Base + (y * Src->Line); + REG uint8_t *s = Src->Base + (y * Src->Line); REG Pixel *d = p, *e = d + Src->x; while (d < e) { *d++ = map[*s++]; } u8 += Dest->Line; } } else { GBmpMem Dst; Dst.Base = u8; Dst.x = Src->x; Dst.y = Src->y; Dst.Cs = Dest->Cs; Dst.Line = Dest->Line; if (!LgiRopUniversal(&Dst, Src, Op==GDC_ALPHA)) { return false; } } } else { switch (Src->Cs) { #define AlphaCase(name) \ case Cs##name: return AlphaBlt(Src, SrcAlpha); AlphaCase(Rgb24); AlphaCase(Bgr24); AlphaCase(Xrgb32); AlphaCase(Xbgr32); AlphaCase(Rgbx32); AlphaCase(Bgrx32); AlphaCase(Argb32); AlphaCase(Abgr32); AlphaCase(Rgba32); AlphaCase(Bgra32); default: LgiAssert(!"Impl me."); break; } } return false; } }; class G48BitFactory : public GApplicatorFactory { public: GApplicator *Create(GColourSpace Cs, int Op) { switch (Cs) { #define Case48(name) \ case Cs##name: \ return new App48(Op); Case48(Rgb48); Case48(Bgr48); default: break; } return 0; } } App48Factory; diff --git a/src/common/Gdc2/64Bit.cpp b/src/common/Gdc2/64Bit.cpp --- a/src/common/Gdc2/64Bit.cpp +++ b/src/common/Gdc2/64Bit.cpp @@ -1,403 +1,403 @@ /** \file \author Matthew Allen \date 24/2/2014 \brief 64 bit primitives (RGBA 16 bit) */ #include #include #include #include #include "Gdc2.h" #include "GPalette.h" #define BytePtr ((uint8*&)Ptr) #undef NonPreMulOver64 #define NonPreMulOver64(c) d->c = ((s->c * sa) + ((d->c * 0xffff) / 0xffff * o)) / 0xffff template class App64 : public GApplicator { union { - uint8 *u8; + uint8_t *u8; Pixel *p; }; int Op; int ConstAlpha; GPalette *PalAlpha; public: App64(int op) { Op = op; p = NULL; ConstAlpha = 0xffff; PalAlpha = NULL; } int GetVar(int Var) { switch (Var) { case GAPP_ALPHA_A: { return ConstAlpha; break; } default: { LgiAssert(!"impl me."); break; } } return 0; } int SetVar(int Var, NativeInt Value) { switch (Var) { case GAPP_ALPHA_A: { ConstAlpha = (int)Value; break; } case GAPP_ALPHA_PAL: { PalAlpha = (GPalette*)Value; break; } } return 0; } bool SetSurface(GBmpMem *d, GPalette *pal = NULL, GBmpMem *a = NULL) { if (d && d->Cs == ColourSpace) { Dest = d; Pal = pal; p = (Pixel*) d->Base; Alpha = 0; return true; } return false; } void SetPtr(int x, int y) { p = (Pixel*) (Dest->Base + (y * Dest->Line) + (x * sizeof(Pixel))); } void IncX() { p++; } void IncY() { u8 += Dest->Line; } void IncPtr(int X, int Y) { p += X; u8 += Y * Dest->Line; } void Set() { if (p32.a == 255) { p->r = G8bitTo16bit(p32.r); p->g = G8bitTo16bit(p32.g); p->b = G8bitTo16bit(p32.b); p->a = -1; } else if (p32.a) { LgiAssert(0); } } COLOUR Get() { return Rgb24(p->r >> 8, p->g >> 8, p->b >> 8); } void VLine(int height) { Pixel cp; cp.r = G8bitTo16bit(p32.r); cp.g = G8bitTo16bit(p32.g); cp.b = G8bitTo16bit(p32.b); cp.a = G8bitTo16bit(p32.a); while (height-- > 0) { *p = cp; u8 += Dest->Line; } } void Rectangle(int x, int y) { Pixel cp; cp.r = G8bitTo16bit(p32.r); cp.g = G8bitTo16bit(p32.g); cp.b = G8bitTo16bit(p32.b); cp.a = G8bitTo16bit(p32.a); while (y-- > 0) { REG Pixel *i = p, *e = i + x; while (i < e) { *i++ = cp; } u8 += Dest->Line; } } template bool CopyBltNoAlphaUpScale(GBmpMem *Src) { for (int y=0; yy; y++) { Pixel *d = p; T *s = (T*) (Src->Base + (y * Src->Line)); T *e = s + Src->x; while (s < e) { d->r = G8bitTo16Bit(s->r); d->g = G8bitTo16Bit(s->g); d->b = G8bitTo16Bit(s->b); d->a = 0xffff; s++; d++; } u8 += Dest->Line; } return true; } template bool CopyBltWithAlphaUpScale(GBmpMem *Src) { for (int y=0; yy; y++) { Pixel *d = p; T *s = (T*) (Src->Base + (y * Src->Line)); T *e = s + Src->x; while (s < e) { d->r = G8bitTo16Bit(s->r); d->g = G8bitTo16Bit(s->g); d->b = G8bitTo16Bit(s->b); d->a = G8bitTo16Bit(s->a); s++; d++; } u8 += Dest->Line; } return true; } template bool CopyBltNoAlpha(GBmpMem *Src) { for (int y=0; yy; y++) { Pixel *d = p; T *s = (T*) (Src->Base + (y * Src->Line)); T *e = s + Src->x; while (s < e) { d->r = s->r; d->g = s->g; d->b = s->b; d->a = 0xffff; s++; d++; } u8 += Dest->Line; } return true; } template bool CopyBltWithAlpha(GBmpMem *Src) { for (int y=0; yy; y++) { Pixel *d = p; T *s = (T*) (Src->Base + (y * Src->Line)); T *e = s + Src->x; while (s < e) { d->r = s->r; d->g = s->g; d->b = s->b; d->a = s->a; s++; d++; } u8 += Dest->Line; } return true; } template bool AlphaBlt(GBmpMem *Src, GBmpMem *SrcAlpha) { for (int y=0; yy; y++) { Pixel *d = p; T *s = (T*) (Src->Base + (y * Src->Line)); T *e = s + Src->x; uint16 *a = (uint16*) (Src->Base + (y * SrcAlpha->Line)); while (s < e) { uint16 sa = *a++; if (sa == 255) { d->r = s->r; d->g = s->g; d->b = s->b; } else if (sa > 0) { uint16 o = 0xffff - sa; d->r = ((s->r * sa) + (d->r * o)) / 0xffff; d->g = ((s->g * sa) + (d->g * o)) / 0xffff; d->b = ((s->b * sa) + (d->b * o)) / 0xffff; } s++; d++; } u8 += Dest->Line; } return true; } bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha = NULL) { if (!Src) return false; if (!SrcAlpha) { if (Src->Cs == CsIndex8) { Pixel map[256]; for (int i=0; i<256; i++) { GdcRGB *rgb = SPal ? (*SPal)[i] : NULL; if (rgb) { map[i].r = G8bitTo16bit(rgb->r); map[i].g = G8bitTo16bit(rgb->g); map[i].b = G8bitTo16bit(rgb->b); } else { map[i].r = G8bitTo16bit(i); map[i].g = G8bitTo16bit(i); map[i].b = G8bitTo16bit(i); } map[i].a = 0xffff; } for (int y=0; yy; y++) { - REG uint8 *s = Src->Base + (y * Src->Line); + REG uint8_t *s = Src->Base + (y * Src->Line); REG Pixel *d = p, *e = d + Src->x; while (d < e) { *d++ = map[*s++]; } u8 += Dest->Line; } } else { GBmpMem Dst; Dst.Base = u8; Dst.x = Src->x; Dst.y = Src->y; Dst.Cs = Dest->Cs; Dst.Line = Dest->Line; if (!LgiRopUniversal(&Dst, Src, Op==GDC_ALPHA)) { return false; } } } else { switch (Src->Cs) { #define AlphaCase(name) \ case Cs##name: return AlphaBlt(Src, SrcAlpha); AlphaCase(Rgb24); AlphaCase(Bgr24); AlphaCase(Xrgb32); AlphaCase(Xbgr32); AlphaCase(Rgbx32); AlphaCase(Bgrx32); AlphaCase(Argb32); AlphaCase(Abgr32); AlphaCase(Rgba32); AlphaCase(Bgra32); default: LgiAssert(!"Impl me."); break; } } return false; } }; class G64BitFactory : public GApplicatorFactory { public: GApplicator *Create(GColourSpace Cs, int Op) { switch (Cs) { #define Case64(name) \ case Cs##name: \ return new App64(Op); Case64(Rgba64); Case64(Bgra64); Case64(Argb64); Case64(Abgr64); default: break; } return 0; } } App64Factory; diff --git a/src/common/Gdc2/8Bit.cpp b/src/common/Gdc2/8Bit.cpp --- a/src/common/Gdc2/8Bit.cpp +++ b/src/common/Gdc2/8Bit.cpp @@ -1,885 +1,885 @@ /** \file \author Matthew Allen \date 20/3/1997 \brief 8 bit primitives */ #include #include #include #include #include "Gdc2.h" #include "GPalette.h" /// 8 bit paletted applicators class LgiClass GdcApp8 : public GApplicator { protected: uchar *Ptr; uchar *APtr; public: GdcApp8() { Ptr = 0; APtr = 0; } const char *GetClass() { return "GdcApp8"; } bool SetSurface(GBmpMem *d, GPalette *p, GBmpMem *a); void SetPtr(int x, int y); void IncX(); void IncY(); void IncPtr(int X, int Y); COLOUR Get(); }; class LgiClass GdcApp8Set : public GdcApp8 { public: const char *GetClass() { return "GdcApp8Set"; } void Set(); void VLine(int height); void Rectangle(int x, int y); bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha); }; class LgiClass GdcApp8And : public GdcApp8 { public: const char *GetClass() { return "GdcApp8And"; } void Set(); void VLine(int height); void Rectangle(int x, int y); bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha); }; class LgiClass GdcApp8Or : public GdcApp8 { public: const char *GetClass() { return "GdcApp8Or"; } void Set(); void VLine(int height); void Rectangle(int x, int y); bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha); }; class LgiClass GdcApp8Xor : public GdcApp8 { public: const char *GetClass() { return "GdcApp8Xor"; } void Set(); void VLine(int height); void Rectangle(int x, int y); bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha); }; GApplicator *GApp8::Create(GColourSpace Cs, int Op) { if (Cs == CsIndex8) { switch (Op) { case GDC_SET: return new GdcApp8Set; case GDC_AND: return new GdcApp8And; case GDC_OR: return new GdcApp8Or; case GDC_XOR: return new GdcApp8Xor; } } return 0; } ////////////////////////////////////////////////////////////////////////////////////////// bool GdcApp8::SetSurface(GBmpMem *d, GPalette *p, GBmpMem *a) { if (d && d->Cs == CsIndex8) { Dest = d; Pal = p; Ptr = d->Base; Alpha = a; APtr = Alpha ? Alpha->Base : 0; return true; } return false; } void GdcApp8::SetPtr(int x, int y) { LgiAssert(Dest && Dest->Base); Ptr = Dest->Base + ((y * Dest->Line) + x); if (Alpha) APtr = Alpha->Base + ((y * Alpha->Line) + x); } void GdcApp8::IncX() { Ptr++; if (APtr) APtr++; } void GdcApp8::IncY() { Ptr += Dest->Line; if (APtr) APtr += Alpha->Line; } void GdcApp8::IncPtr(int X, int Y) { Ptr += (Y * Dest->Line) + X; if (APtr) { APtr += (Y * Dest->Line) + X; } } COLOUR GdcApp8::Get() { return *Ptr; } // 8 bit set sub functions void GdcApp8Set::Set() { *Ptr = c; if (APtr) { *APtr = 0xff; } } void GdcApp8Set::VLine(int height) { while (height--) { *Ptr = c; Ptr += Dest->Line; if (APtr) { *APtr = 0xff; APtr += Dest->Line; } } } void GdcApp8Set::Rectangle(int x, int y) { COLOUR fill = c & 0xFF; fill = fill | (fill<<8) | (fill<<16) | (fill<<24); #if 0 // defined(GDC_USE_ASM) && defined(_MSC_VER) // this duplicates the colour twice in eax to allow us to fill // four pixels per write. this means we are using the whole // 32-bit bandwidth to the video card :) uchar *p = Ptr; int Line = Dest->Line; if (x > 0 && y > 0) { _asm { mov esi, p mov eax, fill mov edx, Line LoopY: mov edi, esi add esi, edx mov ecx, x shr ecx, 2 jz Odd LoopX: mov [edi], eax add edi, 4 dec ecx jnz LoopX Odd: mov ecx, x and ecx, 3 jz Next LoopO: mov [edi], al inc edi dec ecx jnz LoopO Next: dec y jnz LoopY } } #else int x4, n; - uint32 *p; + uint32_t *p; uchar *cp; while (y--) { x4 = x >> 2; - p = (uint32*) Ptr; + p = (uint32_t*) Ptr; for (n=0; nLine; if (APtr) { for (n=0; nLine; } } #endif } uchar Div51[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5}; uchar Mod51[256] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 0}; uchar DitherIndex8[64] = { 0, 38, 9, 47, 2, 40, 11, 50, 25, 12, 35, 22, 27, 15, 37, 24, 6, 44, 3, 41, 8, 47, 5, 43, 31, 19, 28, 15, 34, 21, 31, 18, 1, 39, 11, 49, 0, 39, 10, 48, 27, 14, 36, 23, 26, 13, 35, 23, 7, 46, 4, 43, 7, 45, 3, 42, 33, 20, 30, 17, 32, 19, 29, 16}; uchar Mul8[8] = {0, 8, 16, 24, 32, 40, 48, 56}; uchar Mul6[6] = {0, 6, 12, 18, 24, 30}; uchar Mul36[6] = {0, 36, 72, 108, 144, 180}; template void GConvertIndexed(OutPx *out, InPx *in, int len, GColourSpace inCs, GPalette *pal) { switch (inCs) { case CsIndex8: { OutPx p[256]; for (int i=0; i<256; i++) { GdcRGB *rgb = pal ? (*pal)[i] : NULL; if (rgb) { p[i].r = rgb->r; p[i].g = rgb->g; p[i].b = rgb->b; } else { p[i].r = i; p[i].g = i; p[i].b = i; } } // Indexed colour InPx *end = in + len; while (in < end) { *out++ = p[*in++]; } break; } default: { LgiAssert(0); break; } } } template void GConvertRgb24(OutPx *out, InPx *in, int len, GColourSpace inCs, GPalette *pal) { switch (inCs) { case CsRgb15: case CsBgr15: { // All 15 bit types InPx *end = in + len; while (in < end) { out->r = G5bitTo8bit(in->r); out->g = G5bitTo8bit(in->g); out->b = G5bitTo8bit(in->b); in++; out++; } break; } case CsRgb16: case CsBgr16: { // All 16 bit types InPx *end = in + len; while (in < end) { out->r = G5bitTo8bit(in->r); out->g = G6bitTo8bit(in->g); out->b = G5bitTo8bit(in->b); in++; out++; } break; } default: { // All straight RGB types fall into here InPx *end = in + len; while (in < end) { out->r = in->r; out->g = in->g; out->b = in->b; in++; out++; } break; } } } void Convert(System24BitPixel *Dst, GBmpMem *Src, int Line, GPalette *SPal) { uchar *In = Src->Base + (Line * Src->Line); switch (Src->Cs) { #define ConvertCase(type) \ case Cs##type: \ GConvertRgb24(Dst, (G##type*) In, Src->x, Src->Cs, NULL); \ break case CsIndex8: - GConvertIndexed(Dst, In, Src->x, Src->Cs, SPal); + GConvertIndexed(Dst, In, Src->x, Src->Cs, SPal); break; ConvertCase(Rgb15); ConvertCase(Bgr15); ConvertCase(Rgb16); ConvertCase(Bgr16); ConvertCase(Rgb24); ConvertCase(Bgr24); ConvertCase(Rgba32); ConvertCase(Bgra32); ConvertCase(Argb32); ConvertCase(Abgr32); default: LgiAssert(0); break; } } template -void BltNearest16(uchar *Lookup, uint8 *out, Pixel *p, int x) +void BltNearest16(uchar *Lookup, uint8_t *out, Pixel *p, int x) { REG Pixel *src = p, *e = src + x; while (src < e) { *out++ = Lookup[Rgb15(src->r<<3, src->g<<2, src->b<<3)]; src++; } } template -void BltNearest24(uchar *Lookup, uint8 *out, Pixel *p, int x) +void BltNearest24(uchar *Lookup, uint8_t *out, Pixel *p, int x) { REG Pixel *src = p, *e = src + x; while (src < e) { *out++ = Lookup[Rgb15(src->r, src->g, src->b)]; src++; } } template -void BltNearest48(uchar *Lookup, uint8 *out, Pixel *p, int x) +void BltNearest48(uchar *Lookup, uint8_t *out, Pixel *p, int x) { REG Pixel *src = p, *e = src + x; while (src < e) { *out++ = Lookup[Rgb15(src->r >> 8, src->g >> 8, src->b >> 8)]; src++; } } #define LookupIdx5bit(r, g, b) ( ((int)(r)<<10) | ((int)(g)<<5) | (b) ) bool GdcApp8Set::Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha) { Dest->Flags &= ~GDC_UPDATED_PALETTE; if (Src) { switch (Src->Cs) { case CsIndex8: { uchar *s = Src->Base; for (int y=0; yy; y++) { MemCpy(Ptr, s, Src->x); s += Src->Line; Ptr += Dest->Line; } break; } default: { switch (GdcD->GetOption(GDC_REDUCE_TYPE)) { default: case REDUCE_NEAREST: { if (Pal) { // Create colour lookup table int LookupSize = 32 << 10; GAutoPtr Mem(new uchar[LookupSize]); if (Mem) { uchar *Lookup = Mem; for (int i=0; iMatchRgb(Rgb24(r, g, b)); } // loop through all pixels and convert colour for (int y=0; yy; y++) { uchar *d = Ptr; Ptr += Dest->Line; switch (Src->Cs) { default: { LgiAssert(!"Not impl."); break; } case CsRgb15: { ushort *s = (ushort*) (Src->Base + (y * Src->Line)); for (int x=0; xx; x++) { *d++ = Lookup[*s++ & 0x7fff]; } break; } case CsBgr15: { GBgr15 *s = (GBgr15*) (Src->Base + (y * Src->Line)); for (int x=0; xx; x++) { uint16 u = LookupIdx5bit(s->r, s->g, s->b); *d++ = Lookup[u]; s++; } break; } #define Case(Px, Sz) \ case Cs##Px: \ BltNearest##Sz(Lookup, d, (G##Px*) (Src->Base + (y * Src->Line)), Src->x); \ break Case(Rgb16, 16); Case(Bgr16, 16); Case(Rgb24, 24); Case(Bgr24, 24); Case(Rgbx32, 24); Case(Bgrx32, 24); Case(Xrgb32, 24); Case(Xbgr32, 24); Case(Rgba32, 24); Case(Bgra32, 24); Case(Argb32, 24); Case(Abgr32, 24); Case(Rgb48, 48); Case(Bgr48, 48); Case(Rgba64, 48); Case(Bgra64, 48); Case(Argb64, 48); Case(Abgr64, 48); #undef Case } } } } break; } case REDUCE_HALFTONE: { for (int y=0; yy; y++) { uchar *D = Ptr; int ym = y % 8; Ptr += Dest->Line; switch (Src->Cs) { default: { LgiAssert(!"Not impl."); break; } case CsRgb15: { ushort *S = (ushort*) (Src->Base + (y * Src->Line)); for (int x=0; xx; x++) { int n = DitherIndex8[Mul8[x&7] + ym]; int r = (*S >> 7) & 0xF8; int g = (*S >> 2) & 0xF8; int b = (*S << 3) & 0xF8; S++; *D++ = (Div51[b] + (Mod51[b] > n)) + Mul6[Div51[g] + (Mod51[g] > n)] + Mul36[Div51[r] + (Mod51[r] > n)]; } break; } case CsRgb16: { ushort *S = (ushort*) (Src->Base + (y * Src->Line)); for (int x=0; xx; x++) { int n = DitherIndex8[Mul8[x&7] + ym]; int r = (*S >> 8) & 0xF8; int g = (*S >> 3) & 0xFC; int b = (*S << 3) & 0xF8; S++; *D++ = (Div51[b] + (Mod51[b] > n)) + Mul6[Div51[g] + (Mod51[g] > n)] + Mul36[Div51[r] + (Mod51[r] > n)]; } break; } case System24BitColourSpace: { System24BitPixel *s = (System24BitPixel*) (Src->Base + (y * Src->Line)); for (int x=0; xx; x++, s++) { int n = DitherIndex8[Mul8[x&7] + ym]; *D++ = (Div51[s->b] + (Mod51[s->b] > n)) + Mul6[Div51[s->g] + (Mod51[s->g] > n)] + Mul36[Div51[s->r] + (Mod51[s->r] > n)]; } break; } case System32BitColourSpace: { System32BitPixel *s = (System32BitPixel*) (Src->Base + (y * Src->Line)); for (int x=0; xx; x++, s++) { int n = DitherIndex8[Mul8[x&7] + ym]; *D++ = (Div51[s->b] + (Mod51[s->b] > n)) + Mul6[Div51[s->g] + (Mod51[s->g] > n)] + Mul36[Div51[s->r] + (Mod51[s->r] > n)]; } break; } } } break; } case REDUCE_ERROR_DIFFUSION: { // dist = sqrt(0.299*SQR(r2-r1) + 0.587*SQR(g2-g1) + 0.114*SQR(b2-b1)) // Floyd - Steinberg error diffusion... roughly anyway if (!Pal) break; GSurface *pBuf = new GMemDC; if (pBuf && pBuf->Create(Src->x+2, 2, System24BitColourSpace)) { // Clear buffer pBuf->Colour(0, 24); pBuf->Rectangle(); // Create lookup table for converting RGB -> palette int LookupSize = 32 << 10; // 32KB uchar *Lookup = new uchar[LookupSize]; if (Lookup) { int i; for (i=0; iMatchRgb(Rgb24(r, g, b)); } // Error diffusion buffer System24BitPixel *Buffer[2]; Buffer[0] = ((System24BitPixel*) (*pBuf)[0]) + 1; Buffer[1] = ((System24BitPixel*) (*pBuf)[0]) + 1; // Depth converter function // Convert the first scan line Convert(Buffer[0], Src, 0, SPal); // Loop through pixels for (int y=0; yy; y++) { System24BitPixel *src = Buffer[0]; System24BitPixel *next = Buffer[1]; uchar *dst = Ptr; if (y < Src->y - 1) { // Convert the next scanline Convert(Buffer[1], Src, y + 1, SPal); } for (int x=0; xx; x++, dst++, src++, next++) { *dst = Lookup[Rgb15(src->r, src->g, src->b)]; // cube nearest colour calc // *D = (IndexLookup[S[2]]*36) + (IndexLookup[S[1]]*6) + IndexLookup[S[0]]; GdcRGB *P = (*Pal)[*dst]; #define Diffuse(c) \ { \ int nError = src->c - P->c; \ if (nError != 0) \ { \ int nRemainder = 0; \ int n; \ \ /* next pixel: 7/16th's */ \ n = src[1].c + ((nError * 7) >> 4); \ n = limit(n, 0, 255); \ nRemainder += n - src[1].c; \ src[1].c = n; \ \ /* below and to the left: 3/16th's */ \ n = next[-1].c + ((nError * 3) >> 4); \ n = limit(n, 0, 255); \ nRemainder += n - next[-1].c; \ next[-1].c = n; \ \ /* below: 5/16th's */ \ n = next[0].c + ((nError * 5) >> 4); \ n = limit(n, 0, 255); \ nRemainder += n - next[0].c; \ next[0].c = n; \ \ /* below and to the right: 1/16th */ \ n = next[1].c + (nError - nRemainder); \ n = limit(n, 0, 255); \ next[1].c = n; \ } \ } Diffuse(r) Diffuse(g) Diffuse(b) } // Copy the next scanline into the current one memcpy(Buffer[0]-1, Buffer[1]-1, (Src->x + 2) * sizeof(*Buffer[0])); // Move output ptr to the next scanline Ptr += Dest->Line; } } DeleteObj(pBuf); } break; } } break; } } } return true; } // 8 bit or sub functions void GdcApp8Or::Set() { *Ptr |= c; } void GdcApp8Or::VLine(int height) { while (height--) { *Ptr |= c; Ptr += Dest->Line; } } void GdcApp8Or::Rectangle(int x, int y) { while (y--) { for (int n=0; nLine; } } bool GdcApp8Or::Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha) { if (Src) { switch (Src->Cs) { default: { LgiAssert(!"Not impl."); break; } case CsIndex8: { uchar *s = Src->Base; for (int y=0; yy; y++) { MemOr(Ptr, s, Src->x); s += Src->Line; Ptr += Dest->Line; } break; } } } return true; } // 8 bit AND sub functions void GdcApp8And::Set() { *Ptr &= c; } void GdcApp8And::VLine(int height) { while (height--) { *Ptr &= c; Ptr += Dest->Line; } } void GdcApp8And::Rectangle(int x, int y) { while (y--) { for (int n=0; nLine; } } bool GdcApp8And::Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha) { if (Src && Src->Cs == Dest->Cs) { uchar *s = Src->Base; for (int y=0; yy; y++) { MemAnd(Ptr, s, Src->x); s += Src->Line; Ptr += Dest->Line; } } return true; } // 8 bit XOR sub functions void GdcApp8Xor::Set() { *Ptr ^= c; } void GdcApp8Xor::VLine(int height) { while (height--) { *Ptr ^= c; Ptr += Dest->Line; } } void GdcApp8Xor::Rectangle(int x, int y) { while (y--) { for (int n=0; nLine; } } bool GdcApp8Xor::Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha) { if (Src && Src->Cs == Dest->Cs) { uchar *s = Src->Base; for (int y=0; yy; y++) { MemXor(Ptr, s, Src->x); s += Src->Line; Ptr += Dest->Line; } } return true; } diff --git a/src/common/Gdc2/Alpha.cpp b/src/common/Gdc2/Alpha.cpp --- a/src/common/Gdc2/Alpha.cpp +++ b/src/common/Gdc2/Alpha.cpp @@ -1,1820 +1,1820 @@ /** \file \author Matthew Allen \date 4/3/1998 \brief Draw mode effects */ #include #include #include #include #include "Gdc2.h" #include "GPath.h" #include "GPixelRops.h" #include "GPalette.h" // #define Div255(a) DivLut[a] #define Div255(a) ((a)/255) template void CreatePaletteLut(T *c, GPalette *Pal, int Scale = 255) { if (Scale < 255) { uchar *DivLut = Div255Lut; for (int i=0; i<256; i++) { GdcRGB *p = (Pal) ? (*Pal)[i] : 0; if (p) { c[i].r = DivLut[p->r * Scale]; c[i].g = DivLut[p->g * Scale]; c[i].b = DivLut[p->b * Scale]; } else { c[i].r = DivLut[i * Scale]; c[i].g = c[i].r; c[i].b = c[i].r; } } } else if (Scale) { for (int i=0; i<256; i++) { GdcRGB *p = (Pal) ? (*Pal)[i] : 0; if (p) { c[i].r = p->r; c[i].g = p->g; c[i].b = p->b; } else { c[i].r = i; c[i].g = i; c[i].b = i; } } } else { memset(c, 0, sizeof(*c) * 256); } } /// Alpha blending applicators class GAlphaApp : public GApplicator { protected: uchar alpha, oma; int Bits, Bytes; uchar *Ptr; uchar *APtr; const char *GetClass() { return "GAlphaApp"; } public: GAlphaApp() { Op = GDC_ALPHA; alpha = 0xFF; oma = 0; Bits = 8; Bytes = 1; Ptr = 0; APtr = 0; } int GetVar(int Var) { switch (Var) { case GAPP_ALPHA_A: return alpha; } return 0; } int SetVar(int Var, NativeInt Value) { switch (Var) { case GAPP_ALPHA_A: { int Old = alpha; alpha = (uchar)Value; oma = 0xFF - alpha; return Old; } case GAPP_ALPHA_PAL: { } } return 0; } bool SetSurface(GBmpMem *d, GPalette *p, GBmpMem *a) { if (d && d->GetBits() == Bits) { Dest = d; Pal = p; Ptr = d->Base; Alpha = a; APtr = Alpha ? Alpha->Base : 0; return true; } return false; } void SetPtr(int x, int y) { LgiAssert(Dest && Dest->Base); Ptr = Dest->Base + ((y * Dest->Line) + (x * Bytes)); if (APtr) APtr = Alpha->Base + ((y * Alpha->Line) + x); } void IncX() { Ptr += Bytes; if (APtr) APtr++; } void IncY() { Ptr += Dest->Line; if (APtr) APtr += Alpha->Line; } void IncPtr(int X, int Y) { Ptr += (Y * Dest->Line) + (X * Bytes); if (APtr) APtr += (Y * Dest->Line) + X; } COLOUR Get() { switch (Bytes) { case 1: - return *((uint8*)Ptr); + return *((uint8_t*)Ptr); case 2: return *((uint16*)Ptr); case 3: return Rgb24(Ptr[2], Ptr[1], Ptr[0]); case 4: - return *((uint32*)Ptr); + return *((uint32_t*)Ptr); } return 0; } }; class GdcApp8Alpha : public GAlphaApp { char Remap[256]; uchar *DivLut; public: GdcApp8Alpha(); int SetVar(int Var, NativeInt Value); void Set(); void VLine(int height); void Rectangle(int x, int y); bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha); template void AlphaBlt15(GBmpMem *Src, GPalette *DPal, uchar *Lut) { System24BitPixel dc[256]; CreatePaletteLut(dc, DPal, oma); if (!Lut) Lut = DPal->MakeLut(15); for (int y=0; yy; y++) { Pixel *s = (Pixel*) (Src->Base + (y * Src->Line)); uchar *d = Ptr; uchar *e = d + Src->x; while (d < e) { System24BitPixel *dst = dc + *d; int r = dst->r + DivLut[G5bitTo8bit(s->r) * alpha]; int g = dst->g + DivLut[G5bitTo8bit(s->g) * alpha]; int b = dst->b + DivLut[G5bitTo8bit(s->b) * alpha]; *d++ = Lut[Rgb15(r, g, b)]; s++; } Ptr += Dest->Line; } } template void AlphaBlt16(GBmpMem *Src, GPalette *DPal, uchar *Lut) { System24BitPixel dc[256]; CreatePaletteLut(dc, DPal, oma); if (!Lut) Lut = DPal->MakeLut(15); for (int y=0; yy; y++) { Pixel *s = (Pixel*) (Src->Base + (y * Src->Line)); uchar *d = Ptr; uchar *e = d + Src->x; while (d < e) { System24BitPixel *dst = dc + *d; int r = dst->r + DivLut[G5bitTo8bit(s->r) * alpha]; int g = dst->g + DivLut[G6bitTo8bit(s->g) * alpha]; int b = dst->b + DivLut[G5bitTo8bit(s->b) * alpha]; *d++ = Lut[Rgb15(r, g, b)]; s++; } Ptr += Dest->Line; } } template void AlphaBlt24(GBmpMem *Src, GPalette *DPal, uchar *Lut) { System24BitPixel dc[256]; CreatePaletteLut(dc, DPal, oma); if (!Lut) Lut = DPal->MakeLut(15); for (int y=0; yy; y++) { Pixel *s = (Pixel*) (Src->Base + (y * Src->Line)); uchar *d = Ptr; uchar *e = d + Src->x; while (d < e) { System24BitPixel *dst = dc + *d; int r = dst->r + DivLut[s->r * alpha]; int g = dst->g + DivLut[s->g * alpha]; int b = dst->b + DivLut[s->b * alpha]; *d++ = Lut[Rgb15(r, g, b)]; s++; } Ptr += Dest->Line; } } template void AlphaBlt32(GBmpMem *Src, GPalette *DPal, uchar *Lut) { GdcRGB *dc = (*DPal)[0]; if (!Lut) Lut = DPal->MakeLut(15); for (int y=0; yy; y++) { Pixel *s = (Pixel*) (Src->Base + (y * Src->Line)); uchar *d = Ptr; uchar *e = d + Src->x; while (d < e) { GdcRGB dst = dc[*d]; OverNpm32toNpm24(s, &dst); *d++ = Lut[Rgb15(dst.r, dst.g, dst.b)]; s++; } Ptr += Dest->Line; } } template void AlphaBlt48(GBmpMem *Src, GPalette *DPal, uchar *Lut) { System24BitPixel dc[256]; CreatePaletteLut(dc, DPal, oma); if (!Lut) Lut = DPal->MakeLut(15); for (int y=0; yy; y++) { Pixel *s = (Pixel*) (Src->Base + (y * Src->Line)); uchar *d = Ptr; uchar *e = d + Src->x; while (d < e) { System24BitPixel dst = dc[*d]; GRgba32 src = { - (uint8)(s->r >> 8), - (uint8)(s->g >> 8), - (uint8)(s->b >> 8), + (uint8_t)(s->r >> 8), + (uint8_t)(s->g >> 8), + (uint8_t)(s->b >> 8), alpha }; OverNpm32toNpm24(&src, &dst); *d++ = Lut[Rgb15(dst.r, dst.g, dst.b)]; s++; } Ptr += Dest->Line; } } template void AlphaBlt64(GBmpMem *Src, GPalette *DPal, uchar *Lut) { System24BitPixel dc[256]; CreatePaletteLut(dc, DPal, 0xff); if (!Lut) Lut = DPal->MakeLut(15); for (int y=0; yy; y++) { Pixel *s = (Pixel*) (Src->Base + (y * Src->Line)); uchar *d = Ptr; uchar *e = d + Src->x; while (d < e) { System24BitPixel dst = dc[*d]; OverNpm64toNpm24(s, &dst); *d++ = Lut[Rgb15(dst.r, dst.g, dst.b)]; s++; } Ptr += Dest->Line; } } }; template class GdcAlpha : public GApplicator { protected: union { - uint8 *u8; + uint8_t *u8; uint16 *u16; - uint32 *u32; + uint32_t *u32; Pixel *p; }; - uint8 alpha, one_minus_alpha; + uint8_t alpha, one_minus_alpha; public: GdcAlpha() { p = NULL; alpha = 0xff; one_minus_alpha = 0; } const char *GetClass() { return "GdcAlpha"; } int GetVar(int Var) { switch (Var) { case GAPP_ALPHA_A: return alpha; } return 0; } int SetVar(int Var, NativeInt Value) { switch (Var) { case GAPP_ALPHA_A: { int Old = alpha; alpha = (uchar)Value; one_minus_alpha = 0xFF - alpha; return Old; } } return 0; } bool SetSurface(GBmpMem *d, GPalette *p = 0, GBmpMem *a = 0) { if (d && d->Cs == ColourSpace) { Dest = d; Pal = p; u8 = d->Base; Alpha = a; return true; } else LgiAssert(0); return false; } void SetPtr(int x, int y) { u8 = Dest->Base + (Dest->Line * y); p += x; } void IncX() { p++; } void IncY() { u8 += Dest->Line; } void IncPtr(int X, int Y) { p += X; u8 += Dest->Line * Y; } COLOUR Get() { return Rgb24(p->r, p->g, p->b); } }; template class GdcAlpha15 : public GdcAlpha { public: const char *GetClass() { return "GdcAlpha15"; } #define Div2040(c) ((c) / 2040) #define Setup15() \ - REG uint8 r = Rc15(this->c); \ - REG uint8 g = Gc15(this->c); \ - REG uint8 b = Bc15(this->c); \ - REG uint8 a = this->alpha; \ - REG uint8 oma = 255 - a; \ + REG uint8_t r = Rc15(this->c); \ + REG uint8_t g = Gc15(this->c); \ + REG uint8_t b = Bc15(this->c); \ + REG uint8_t a = this->alpha; \ + REG uint8_t oma = 255 - a; \ REG Pixel *d = this->p #define Comp15() \ d->r = Div2040((G5bitTo8bit(d->r) * oma) + (r * a)); \ d->g = Div2040((G5bitTo8bit(d->g) * oma) + (g * a)); \ d->b = Div2040((G5bitTo8bit(d->b) * oma) + (b * a)) void Set() { Setup15(); Comp15(); } void VLine(int height) { Setup15(); REG ssize_t line = this->Dest->Line; while (height--) { Comp15(); - d = (Pixel*)(((uint8*)d) + line); + d = (Pixel*)(((uint8_t*)d) + line); } this->p = d; } void Rectangle(int x, int y) { Setup15(); REG ssize_t line = this->Dest->Line; while (y--) { d = this->p; REG Pixel *e = d + x; while (d < e) { Comp15(); d++; } this->u8 += line; } } bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha) { if (!Src) return false; if (Src->Cs == CsIndex8) { Pixel map[256]; if (SPal) { GdcRGB *p = (*SPal)[0]; for (int i=0; i<256; i++, p++) { map[i].r = G8bitTo5bit(p->r); map[i].g = G8bitTo5bit(p->g); map[i].b = G8bitTo5bit(p->b); } } else { for (int i=0; i<256; i++) { map[i].r = G8bitTo5bit(i); map[i].g = G8bitTo5bit(i); map[i].b = G8bitTo5bit(i); } } for (int y=0; yy; y++) { uchar *s = ((uchar*)Src->Base) + (y * Src->Line); Pixel *d = this->p; for (int x=0; xx; x++) { *d++ = map[*s++]; } this->u8 += this->Dest->Line; } return true; } else { GBmpMem Dst; Dst.Base = this->u8; Dst.x = Src->x; Dst.y = Src->y; Dst.Cs = this->Dest->Cs; Dst.Line = this->Dest->Line; return LgiRopUniversal(&Dst, Src, true); } } }; template class GdcAlpha16 : public GdcAlpha { public: const char *GetClass() { return "GdcAlpha16"; } #define Div2040(c) ((c) / 2040) #define Div1020(c) ((c) / 1020) #define Setup16() \ - REG uint8 r = Rc16(this->c); \ - REG uint8 g = Gc16(this->c); \ - REG uint8 b = Bc16(this->c); \ - REG uint8 a = this->alpha; \ - REG uint8 oma = 255 - a; \ + REG uint8_t r = Rc16(this->c); \ + REG uint8_t g = Gc16(this->c); \ + REG uint8_t b = Bc16(this->c); \ + REG uint8_t a = this->alpha; \ + REG uint8_t oma = 255 - a; \ REG Pixel *d = this->p #define Comp16() \ d->r = Div2040((G5bitTo8bit(d->r) * oma) + (r * a)); \ d->g = Div1020((G6bitTo8bit(d->g) * oma) + (g * a)); \ d->b = Div2040((G5bitTo8bit(d->b) * oma) + (b * a)) void Set() { Setup16(); Comp16(); } void VLine(int height) { Setup16(); REG ssize_t line = this->Dest->Line; while (height--) { Comp16(); - d = (Pixel*)(((uint8*)d) + line); + d = (Pixel*)(((uint8_t*)d) + line); } this->p = d; } void Rectangle(int x, int y) { Setup16(); REG ssize_t line = this->Dest->Line; while (y--) { d = this->p; REG Pixel *e = d + x; while (d < e) { Comp16(); d++; } this->u8 += line; } } bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha) { if (!Src) return false; if (Src->Cs == CsIndex8) { Pixel map[256]; if (SPal) { GdcRGB *p = (*SPal)[0]; for (int i=0; i<256; i++, p++) { map[i].r = G8bitTo5bit(p->r); map[i].g = G8bitTo6bit(p->g); map[i].b = G8bitTo5bit(p->b); } } else { for (int i=0; i<256; i++) { map[i].r = G8bitTo5bit(i); map[i].g = G8bitTo6bit(i); map[i].b = G8bitTo5bit(i); } } for (int y=0; yy; y++) { uchar *s = ((uchar*)Src->Base) + (y * Src->Line); Pixel *d = this->p; for (int x=0; xx; x++) { *d++ = map[*s++]; } this->u8 += this->Dest->Line; } return true; } else { GBmpMem Dst; Dst.Base = this->u8; Dst.x = Src->x; Dst.y = Src->y; Dst.Cs = this->Dest->Cs; Dst.Line = this->Dest->Line; return LgiRopUniversal(&Dst, Src, true); } } }; template class GdcAlpha24 : public GdcAlpha { public: const char *GetClass() { return "GdcAlpha24"; } #define InitComposite24() \ uchar *DivLut = Div255Lut; \ - REG uint8 a = this->alpha; \ - REG uint8 oma = this->one_minus_alpha; \ + REG uint8_t a = this->alpha; \ + REG uint8_t oma = this->one_minus_alpha; \ REG int r = this->p24.r * a; \ REG int g = this->p24.g * a; \ REG int b = this->p24.b * a #define InitFlat24() \ Pixel px; \ px.r = this->p24.r; \ px.g = this->p24.g; \ px.b = this->p24.b #define Composite24(ptr) \ ptr->r = DivLut[(oma * ptr->r) + r]; \ ptr->g = DivLut[(oma * ptr->g) + g]; \ ptr->b = DivLut[(oma * ptr->b) + b] void Set() { InitComposite24(); Composite24(this->p); } void VLine(int height) { if (this->alpha == 255) { InitFlat24(); while (height-- > 0) { *this->p = px; this->u8 += this->Dest->Line; } } else if (this->alpha > 0) { InitComposite24(); while (height-- > 0) { Composite24(this->p); this->u8 += this->Dest->Line; } } } void Rectangle(int x, int y) { if (this->alpha == 0xff) { InitFlat24(); while (y-- > 0) { REG Pixel *s = this->p; REG Pixel *e = s + x; while (s < e) { *this->p = px; s++; } this->u8 += this->Dest->Line; } } else if (this->alpha > 0) { InitComposite24(); while (y-- > 0) { REG Pixel *s = this->p; REG Pixel *e = s + x; while (s < e) { Composite24(s); s++; } this->u8 += this->Dest->Line; } } } template void CompositeBlt24(GBmpMem *Src) { uchar *Lut = Div255Lut; - REG uint8 a = this->alpha; - REG uint8 oma = this->one_minus_alpha; + REG uint8_t a = this->alpha; + REG uint8_t oma = this->one_minus_alpha; for (int y=0; yy; y++) { Pixel *dst = this->p; Pixel *dst_end = dst + Src->x; SrcPx *src = (SrcPx*)(Src->Base + (y * Src->Line)); if (a == 0xff) { while (dst < dst_end) { // No source alpha, just copy blt dst->r = src->r; dst->g = src->g; dst->b = src->b; dst++; src++; } } else if (a > 0) { while (dst < dst_end) { // No source alpha, but apply our local alpha dst->r = Lut[(dst->r * oma) + (src->r * a)]; dst->g = Lut[(dst->g * oma) + (src->g * a)]; dst->b = Lut[(dst->b * oma) + (src->b * a)]; dst++; src++; } } this->u8 += this->Dest->Line; } } template void CompositeBlt32(GBmpMem *Src) { uchar *Lut = Div255Lut; - REG uint8 a = this->alpha; + REG uint8_t a = this->alpha; if (a == 0xff) { // Apply the source alpha only for (int y=0; yy; y++) { Pixel *dst = this->p; Pixel *dst_end = dst + Src->x; SrcPx *src = (SrcPx*)(Src->Base + (y * Src->Line)); while (dst < dst_end) { - REG uint8 sa = src->a; - REG uint8 soma = 0xff - sa; + REG uint8_t sa = src->a; + REG uint8_t soma = 0xff - sa; dst->r = Lut[(dst->r * soma) + (src->r * sa)]; dst->g = Lut[(dst->g * soma) + (src->g * sa)]; dst->b = Lut[(dst->b * soma) + (src->b * sa)]; dst++; src++; } this->u8 += this->Dest->Line; } } else if (a > 0) { // Apply source alpha AND our local alpha for (int y=0; yy; y++) { Pixel *dst = this->p; Pixel *dst_end = dst + Src->x; SrcPx *src = (SrcPx*)(Src->Base + (y * Src->Line)); while (dst < dst_end) { - REG uint8 sa = Lut[a * src->a]; - REG uint8 soma = 0xff - sa; + REG uint8_t sa = Lut[a * src->a]; + REG uint8_t soma = 0xff - sa; dst->r = Lut[(dst->r * soma) + (src->r * sa)]; dst->g = Lut[(dst->g * soma) + (src->g * sa)]; dst->b = Lut[(dst->b * soma) + (src->b * sa)]; dst++; src++; } this->u8 += this->Dest->Line; } } } bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha = 0) { if (!Src) return false; if (SrcAlpha) { LgiAssert(!"Impl me."); } else { switch (Src->Cs) { case CsIndex8: { Pixel map[256]; for (int i=0; i<256; i++) { GdcRGB *p = (SPal) ? (*SPal)[i] : NULL; if (p) { map[i].r = p->r; map[i].g = p->g; map[i].b = p->b; } else { map[i].r = i; map[i].g = i; map[i].b = i; } } for (int y=0; yy; y++) { uchar *s = ((uchar*)Src->Base) + (y * Src->Line); Pixel *d = this->p; for (int x=0; xx; x++) { *d++ = map[*s++]; } this->u8 += this->Dest->Line; } return true; } #define Blt24Case(name, size) \ case Cs##name: \ CompositeBlt##size(Src); \ break Blt24Case(Rgb24, 24); Blt24Case(Bgr24, 24); Blt24Case(Rgbx32, 24); Blt24Case(Bgrx32, 24); Blt24Case(Xrgb32, 24); Blt24Case(Xbgr32, 24); Blt24Case(Rgba32, 32); Blt24Case(Bgra32, 32); Blt24Case(Argb32, 32); Blt24Case(Abgr32, 32); #undef Blt24Case default: { GBmpMem Dst; Dst.Base = this->u8; Dst.x = Src->x; Dst.y = Src->y; Dst.Cs = this->Dest->Cs; Dst.Line = this->Dest->Line; return LgiRopUniversal(&Dst, Src, true); break; } } } return false; } }; template class GdcAlpha32 : public GdcAlpha { public: #define InitComposite32() \ uchar *DivLut = Div255Lut; \ REG int a = DivLut[this->alpha * this->p32.a]; \ REG int r = this->p32.r * a; \ REG int g = this->p32.g * a; \ REG int b = this->p32.b * a; \ - REG uint8 oma = 0xff - a + REG uint8_t oma = 0xff - a #define InitFlat32() \ Pixel px; \ px.r = this->p32.r; \ px.g = this->p32.g; \ px.b = this->p32.b; \ px.a = this->p32.a #define Composite32(ptr) \ ptr->r = DivLut[(oma * ptr->r) + r]; \ ptr->g = DivLut[(oma * ptr->g) + g]; \ ptr->b = DivLut[(oma * ptr->b) + b]; \ ptr->a = (a + ptr->a) - DivLut[a * ptr->a] const char *GetClass() { return "GdcAlpha32"; } void Set() { InitComposite32(); Composite32(this->p); } void VLine(int height) { int sa = Div255Lut[this->alpha * this->p32.a]; if (sa == 0xff) { InitFlat32(); while (height-- > 0) { *this->p = px; this->u8 += this->Dest->Line; } } else if (sa > 0) { InitComposite32(); while (height-- > 0) { Composite32(this->p); this->u8 += this->Dest->Line; } } } void Rectangle(int x, int y) { int sa = Div255Lut[this->alpha * this->p32.a]; if (sa == 0xff) { // Fully opaque InitFlat32(); while (y--) { Pixel *d = this->p; Pixel *e = d + x; while (d < e) { *d++ = px; } this->u8 += this->Dest->Line; } } else if (sa > 0) { // Translucent InitComposite32(); while (y--) { Pixel *d = this->p; Pixel *e = d + x; while (d < e) { Composite32(d); d++; } this->u8 += this->Dest->Line; } } } template void PmBlt32(GBmpMem *Src) { REG uchar *DivLut = Div255Lut; for (int y=0; yy; y++) { REG Pixel *d = this->p, *e = d + Src->x; REG SrcPx *s = (SrcPx*)(Src->Base + (Src->Line * y)); while (d < e) { OverPm32toPm32(s, d); d++; s++; } this->u8 += this->Dest->Line; } } bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha = 0) { if (!Src) return 0; REG uchar *DivLut = Div255Lut; uchar lookup[256]; - REG uint8 a = this->alpha; - REG uint8 oma = this->one_minus_alpha; + REG uint8_t a = this->alpha; + REG uint8_t oma = this->one_minus_alpha; for (int i=0; i<256; i++) { lookup[i] = DivLut[i * this->alpha]; } if (SrcAlpha) { switch (Src->Cs) { case CsIndex8: { System24BitPixel c[256]; CreatePaletteLut(c, SPal); for (int y=0; yy; y++) { uchar *s = (uchar*) (Src->Base + (y * Src->Line)); uchar *sa = (uchar*) (SrcAlpha->Base + (y * SrcAlpha->Line)); System24BitPixel *sc; Pixel *d = this->p; uchar a, o; for (int x=0; xx; x++) { a = lookup[*sa++]; if (a == 255) { sc = c + *s; d->r = sc->r; d->g = sc->g; d->b = sc->b; d->a = 255; } else if (a) { sc = c + *s; o = 0xff - a; d->r = DivLut[(d->r * o) + (sc->r * a)]; d->g = DivLut[(d->g * o) + (sc->g * a)]; d->b = DivLut[(d->b * o) + (sc->b * a)]; d->a = (a + d->a) - DivLut[a * d->a]; } s++; d++; } this->u8 += this->Dest->Line; } break; } case System15BitColourSpace: { for (int y=0; yy; y++) { ushort *s = (ushort*) (Src->Base + (y * Src->Line)); uchar *sa = SrcAlpha->Base + (y * SrcAlpha->Line); Pixel *d = this->p; for (int x=0; xx; x++) { uchar a = lookup[*sa++]; if (a == 255) { d->r = Rc15(*s); d->g = Gc15(*s); d->b = Bc15(*s); } else if (a) { uchar o = 255 - a; d->r = DivLut[(a * Rc15(*s)) + (o * d->r)]; d->g = DivLut[(a * Gc15(*s)) + (o * d->g)]; d->b = DivLut[(a * Bc15(*s)) + (o * d->b)]; } s++; d++; } this->u8 += this->Dest->Line; } break; } case System16BitColourSpace: { for (int y=0; yy; y++) { ushort *s = (ushort*) (Src->Base + (y * Src->Line)); uchar *sa = SrcAlpha->Base + (y * SrcAlpha->Line); Pixel *d = this->p; for (int x=0; xx; x++) { uchar a = lookup[*sa++]; if (a == 255) { d->r = Rc16(*s); d->g = Gc16(*s); d->b = Bc16(*s); } else if (a) { uchar o = 255 - a; d->r = DivLut[(a * Rc16(*s)) + (o * d->r)]; d->g = DivLut[(a * Gc16(*s)) + (o * d->g)]; d->b = DivLut[(a * Bc16(*s)) + (o * d->b)]; } s++; d++; } this->u8 += this->Dest->Line; } break; } case System24BitColourSpace: { for (int y=0; yy; y++) { uchar *sa = SrcAlpha->Base + (y * SrcAlpha->Line); Pixel *d = this->p; System24BitPixel *s = (System24BitPixel*) (Src->Base + (y * Src->Line)); for (int x=0; xx; x++) { uchar a = lookup[*sa++]; if (a == 255) { d->r = s->r; d->g = s->g; d->b = s->b; } else if (a) { uchar o = 255 - a; d->r = DivLut[(a * s->r) + (o * d->r)]; d->g = DivLut[(a * s->g) + (o * d->g)]; d->b = DivLut[(a * s->b) + (o * d->b)]; } d++; s++; } this->u8 += this->Dest->Line; } break; } case System32BitColourSpace: { for (int y=0; yy; y++) { Pixel *d = this->p; Pixel *s = (Pixel*) (Src->Base + (y * Src->Line)); uchar *sa = SrcAlpha->Base + (y * SrcAlpha->Line); for (int x=0; xx; x++) { uchar a = lookup[*sa++]; if (a == 255) { d->r = s->r; d->g = s->g; d->b = s->b; d->a = 255; } else if (a) { uchar o = 255 - a; d->r = DivLut[(a * s->r) + (o * d->r)]; d->g = DivLut[(a * s->g) + (o * d->g)]; d->b = DivLut[(a * s->b) + (o * d->b)]; d->a = (s->a + d->a) - DivLut[s->a * d->a]; } d++; s++; } this->u8 += this->Dest->Line; } break; } default: return false; } return true; } if (this->Dest->PreMul() || Src->PreMul()) { switch (Src->Cs) { case CsRgba32: PmBlt32(Src); return true; case CsBgra32: PmBlt32(Src); return true; case CsArgb32: PmBlt32(Src); return true; case CsAbgr32: PmBlt32(Src); return true; default: break; } } switch (Src->Cs) { default: { GBmpMem Dst; Dst.Base = this->u8; Dst.x = Src->x; Dst.y = Src->y; Dst.Cs = this->Dest->Cs; Dst.Line = this->Dest->Line; if (!LgiRopUniversal(&Dst, Src, true)) { return false; } break; } case CsIndex8: { System24BitPixel c[256]; if (SPal && SPal->GetSize() == 0) return false; CreatePaletteLut(c, SPal, this->alpha); for (int y=0; yy; y++) { uchar *s = (uchar*) (Src->Base + (y * Src->Line)); System24BitPixel *sc; Pixel *d = this->p; if (this->alpha == 255) { for (int x=0; xx; x++) { sc = c + *s++; d->r = sc->r; d->g = sc->g; d->b = sc->b; d->a = 255; d++; } } else if (this->alpha) { for (int x=0; xx; x++) { sc = c + *s++; d->r = sc->r + DivLut[d->r * oma]; d->g = sc->g + DivLut[d->g * oma]; d->b = sc->b + DivLut[d->b * oma]; d->a = (a + d->a) - DivLut[a * d->a]; d++; } } this->u8 += this->Dest->Line; } break; } } return true; } }; GApplicator *GAlphaFactory::Create(GColourSpace Cs, int Op) { if (Op != GDC_ALPHA) return NULL; switch (Cs) { #define Case(name, px) \ case Cs##name: \ return new GdcAlpha##px() Case(Rgb15, 15); Case(Bgr15, 15); Case(Rgb16, 16); Case(Bgr16, 16); Case(Rgb24, 24); Case(Bgr24, 24); Case(Rgbx32, 24); Case(Bgrx32, 24); Case(Xrgb32, 24); Case(Xbgr32, 24); Case(Rgba32, 32); Case(Bgra32, 32); Case(Argb32, 32); Case(Abgr32, 32); #undef Case case CsIndex8: return new GdcApp8Alpha; default: LgiTrace("%s:%i - Unknown colour space: 0x%x %s\n", _FL, Cs, GColourSpaceToString(Cs)); // LgiAssert(0); break; } return 0; } GdcApp8Alpha::GdcApp8Alpha() { Bits = 8; Bytes = 1; Op = GDC_ALPHA; ZeroObj(Remap); DivLut = Div255Lut; } int GdcApp8Alpha::SetVar(int Var, NativeInt Value) { int Status = GAlphaApp::SetVar(Var, Value); switch (Var) { case GAPP_ALPHA_PAL: { GPalette *Pal = (GPalette*)Value; if (Pal && alpha < 255) { GdcRGB *p = (*Pal)[0]; GdcRGB *Col = (*Pal)[c&0xFF]; for (int i=0; iGetSize(); i++) { COLOUR Rgb = Rgb24( Div255((oma * p[i].r) + (alpha * Col->r)), Div255((oma * p[i].g) + (alpha * Col->g)), Div255((oma * p[i].b) + (alpha * Col->b))); Remap[i] = Pal->MatchRgb(Rgb); } } else { for (int i=0; i<256; i++) { Remap[i] = c; } } break; } } return Status; } void GdcApp8Alpha::Set() { *Ptr = Remap[*Ptr]; if (APtr) { *APtr += DivLut[(255 - *APtr) * alpha]; } } void GdcApp8Alpha::VLine(int y) { while (y--) { *Ptr = Remap[*Ptr]; Ptr += Dest->Line; if (APtr) { *APtr += DivLut[(255 - *APtr) * alpha]; APtr += Alpha->Line; } } } void GdcApp8Alpha::Rectangle(int x, int y) { while (y--) { uchar *p = Ptr; uchar *e = Ptr + x; while (p < e) { *p = Remap[*p]; p++; } Ptr += Dest->Line; if (APtr) { uchar *a = APtr; e = a + x; while (a < e) { *a += DivLut[(255 - *a) * alpha]; a++; } APtr += Alpha->Line; } } } bool GdcApp8Alpha::Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha) { if (!Src) return false; if (!SPal) SPal = Pal; GPalette Grey; GPalette *DPal; if (Pal) { DPal = Pal; } else { Grey.CreateGreyScale(); DPal = &Grey; } uchar *DivLut = Div255Lut; uchar *Lut = 0; uchar lookup[256]; for (int i=0; i<256; i++) { lookup[i] = (i * (int)alpha) / 255; } if (SrcAlpha) { // Per pixel source alpha GdcRGB *SRgb = (*SPal)[0]; GdcRGB *DRgb = (*DPal)[0]; if (!SRgb || !DRgb) return false; switch (Src->Cs) { default: { LgiAssert(!"Not impl."); break; } case CsIndex8: { System24BitPixel sc[256]; CreatePaletteLut(sc, SPal); System24BitPixel dc[256]; CreatePaletteLut(dc, DPal); for (int y=0; yy; y++) { uchar *s = Src->Base + (y * Src->Line); uchar *sa = SrcAlpha->Base + (y * SrcAlpha->Line); uchar *d = Ptr; int r = 0, g = 0, b = 0; Lut = DPal->MakeLut(15); for (int x=0; xx; x++) { uchar a = lookup[*sa]; System24BitPixel *src = sc + *s; if (a == 255) { r = src->r; g = src->g; b = src->b; } else if (a) { uchar o = 0xff - a; System24BitPixel *dst = dc + *d; r = DivLut[(dst->r * o) + (src->r * a)]; g = DivLut[(dst->g * o) + (src->g * a)]; b = DivLut[(dst->b * o) + (src->b * a)]; } *d++ = Lut[Rgb15(r, g, b)]; sa++; s++; } Ptr += Dest->Line; } break; } case System15BitColourSpace: { System24BitPixel dc[256]; CreatePaletteLut(dc, DPal); if (!Lut) Lut = DPal->MakeLut(15); for (int y=0; yy; y++) { ushort *s = (ushort*) (Src->Base + (y * Src->Line)); uchar *sa = SrcAlpha->Base + (y * SrcAlpha->Line); uchar *d = Ptr; uchar *end = d + Src->x; while (d < end) { uchar a = lookup[*sa++]; if (a == 255) { *d = Lut[*s]; } else if (a) { uchar o = 255 - a; System24BitPixel *dst = dc + *d; int r = DivLut[(dst->r * o) + (Rc15(*s) * a)]; int g = DivLut[(dst->g * o) + (Gc15(*s) * a)]; int b = DivLut[(dst->b * o) + (Bc15(*s) * a)]; *d = Lut[Rgb15(r, g, b)]; } d++; s++; } Ptr += Dest->Line; } break; } case System16BitColourSpace: { System24BitPixel dc[256]; CreatePaletteLut(dc, DPal); if (!Lut) Lut = DPal->MakeLut(15); for (int y=0; yy; y++) { ushort *s = (ushort*) (Src->Base + (y * Src->Line)); uchar *sa = SrcAlpha->Base + (y * SrcAlpha->Line); uchar *d = Ptr; uchar *end = d + Src->x; while (d < end) { uchar a = lookup[*sa++]; if (a == 255) { *d = Lut[Rgb16To15(*s)]; } else if (a) { uchar o = 255 - a; System24BitPixel *dst = dc + *d; int r = DivLut[(dst->r * o) + (Rc16(*s) * a)]; int g = DivLut[(dst->g * o) + (Gc16(*s) * a)]; int b = DivLut[(dst->b * o) + (Bc16(*s) * a)]; *d = Lut[Rgb15(r, g, b)]; } d++; s++; } Ptr += Dest->Line; } break; } case CsBgr24: { GBgr24 dc[256]; CreatePaletteLut(dc, DPal, 255); if (!Lut) Lut = DPal->MakeLut(15); for (int y=0; yy; y++) { GBgr24 *s = (GBgr24*) (Src->Base + (y * Src->Line)); uchar *sa = SrcAlpha->Base + (y * SrcAlpha->Line); uchar *d = Ptr; for (int x=0; xx; x++) { uchar a = lookup[*sa++]; if (a == 255) { *d = Lut[Rgb15(s->r, s->g, s->b)]; } else if (a) { uchar o = 255 - a; GBgr24 *dst = dc + *d; int r = DivLut[(dst->r * o) + (s->r * a)]; int g = DivLut[(dst->g * o) + (s->g * a)]; int b = DivLut[(dst->b * o) + (s->b * a)]; *d = Lut[Rgb15(r, g, b)]; } d++; s++; } Ptr += Dest->Line; } break; } case System32BitColourSpace: { System24BitPixel dc[256]; CreatePaletteLut(dc, DPal, 255); if (!Lut) Lut = DPal->MakeLut(15); for (int y=0; yy; y++) { System32BitPixel *s = (System32BitPixel*) (Src->Base + (y * Src->Line)); uchar *sa = SrcAlpha->Base + (y * SrcAlpha->Line); uchar *d = Ptr; for (int x=0; xx; x++) { uchar a = lookup[*sa++]; if (a == 255) { *d = Lut[Rgb15(s->r, s->g, s->b)]; } else if (a) { uchar o = 255 - a; System24BitPixel *dst = dc + *d; int r = DivLut[(dst->r * o) + (s->r * a)]; int g = DivLut[(dst->g * o) + (s->g * a)]; int b = DivLut[(dst->b * o) + (s->b * a)]; *d = Lut[Rgb15(r, g, b)]; } d++; s++; } Ptr += Dest->Line; } break; } } } else { // Global alpha level GdcRGB *SRgb = (*SPal)[0]; GdcRGB *DRgb = (*DPal)[0]; if (!SRgb || !DRgb) return false; switch (Src->Cs) { default: { LgiTrace("%s:%i - Not impl.\n", _FL); break; } case CsIndex8: { if (alpha == 255) { // do a straight blt for (int y=0; yy; y++) { uchar *s = Src->Base + (y * Src->Line); uchar *d = Ptr; memcpy(d, s, Src->x); Ptr += Dest->Line; } } else if (alpha) { System24BitPixel sc[256]; CreatePaletteLut(sc, SPal, alpha); System24BitPixel dc[256]; CreatePaletteLut(dc, DPal, oma); if (!Lut) Lut = DPal->MakeLut(15); for (int y=0; yy; y++) { uchar *s = Src->Base + (y * Src->Line); uchar *d = Ptr; for (int x=0; xx; x++, s++, d++) { System24BitPixel *src = sc + *s; System24BitPixel *dst = dc + *d; int r = src->r + dst->r; int g = src->g + dst->g; int b = src->b + dst->b; *d = Lut[Rgb15(r, g, b)]; } Ptr += Dest->Line; } } break; } #define Case(Px, Sz) \ case Cs##Px: \ AlphaBlt##Sz(Src, DPal, Lut); \ break Case(Rgb15, 15); Case(Bgr15, 15); Case(Rgb16, 16); Case(Bgr16, 16); Case(Rgb24, 24); Case(Bgr24, 24); Case(Rgbx32, 24); Case(Bgrx32, 24); Case(Xrgb32, 24); Case(Xbgr32, 24); Case(Rgba32, 32); Case(Bgra32, 32); Case(Argb32, 32); Case(Abgr32, 32); Case(Rgb48, 48); Case(Bgr48, 48); Case(Rgba64, 64); Case(Bgra64, 64); Case(Argb64, 64); Case(Abgr64, 64); #undef Case } } return false; } diff --git a/src/common/Gdc2/Filters/GFilter.cpp b/src/common/Gdc2/Filters/GFilter.cpp --- a/src/common/Gdc2/Filters/GFilter.cpp +++ b/src/common/Gdc2/Filters/GFilter.cpp @@ -1,2218 +1,2218 @@ /** \file \author Matthew Allen \date 25/3/97 \brief Graphics file filters */ #if WINDOWS #include #include #include "Lgi.h" #ifdef _MSC_VER #include #endif #else #define BI_RGB 0L #define BI_RLE8 1L #define BI_RLE4 2L #define BI_BITFIELDS 3L #endif #include #include #include #include #include "Lgi.h" #include "GString.h" #include "GVariant.h" #include "GClipBoard.h" #include "GToken.h" #include "GPalette.h" int FindHeader(int Offset, const char *Str, GStream *f) { int i = 0; if (Offset >= 0) { f->SetPos(Offset); } while (true) { char c; if (!f->Read(&c, 1)) break; if (Str[i] == c || Str[i] == '?') { i++; if (!Str[i]) { return true; } } else { i = 0; } } return false; } /// Windows and OS/2 BMP file filter class GdcBmp : public GFilter { int ActualBits; int ScanSize; public: int GetCapabilites() { return FILTER_CAP_READ | FILTER_CAP_WRITE; } Format GetFormat() { return FmtBmp; } /// Reads a BMP file IoStatus ReadImage(GSurface *Out, GStream *In); /// Writes a Windows BMP file IoStatus WriteImage(GStream *Out, GSurface *In); bool GetVariant(const char *n, GVariant &v, char *a) { if (!stricmp(n, LGI_FILTER_TYPE)) { v = "Windows or OS/2 Bitmap"; } else if (!stricmp(n, LGI_FILTER_EXTENSIONS)) { v = "BMP"; } else return false; return true; } }; class GdcBmpFactory : public GFilterFactory { bool CheckFile(const char *File, int Access, const uchar *Hint) { if (File) { const char *Ext = LgiGetExtension((char*)File); if (Ext && !_stricmp(Ext, "bmp")) return true; } if (Hint) { if (Hint[0] == 'B' && Hint[1] == 'M') return true; } return false; } GFilter *NewObject() { return new GdcBmp; } } BmpFactory; #define BMP_ID 0x4D42 #ifdef WIN32 #pragma pack(push, before_pack) #pragma pack(1) #endif class BMP_FILE { public: char Type[2]; - uint32 Size; + uint32_t Size; uint16 Reserved1; uint16 Reserved2; - uint32 OffsetToData; + uint32_t OffsetToData; }; class BMP_WININFO { public: // Win2 hdr (12 bytes) int32 Size; int32 Sx; int32 Sy; uint16 Planes; uint16 Bits; // Win3 hdr (40 bytes) - uint32 Compression; - uint32 DataSize; + uint32_t Compression; + uint32_t DataSize; int32 XPels; int32 YPels; - uint32 ColoursUsed; - uint32 ColourImportant; + uint32_t ColoursUsed; + uint32_t ColourImportant; // Win4 hdr (108 bytes) - uint32 RedMask; /* Mask identifying bits of red component */ - uint32 GreenMask; /* Mask identifying bits of green component */ - uint32 BlueMask; /* Mask identifying bits of blue component */ - uint32 AlphaMask; /* Mask identifying bits of alpha component */ - uint32 CSType; /* Color space type */ - uint32 RedX; /* X coordinate of red endpoint */ - uint32 RedY; /* Y coordinate of red endpoint */ - uint32 RedZ; /* Z coordinate of red endpoint */ - uint32 GreenX; /* X coordinate of green endpoint */ - uint32 GreenY; /* Y coordinate of green endpoint */ - uint32 GreenZ; /* Z coordinate of green endpoint */ - uint32 BlueX; /* X coordinate of blue endpoint */ - uint32 BlueY; /* Y coordinate of blue endpoint */ - uint32 BlueZ; /* Z coordinate of blue endpoint */ - uint32 GammaRed; /* Gamma red coordinate scale value */ - uint32 GammaGreen; /* Gamma green coordinate scale value */ - uint32 GammaBlue; /* Gamma blue coordinate scale value */ + uint32_t RedMask; /* Mask identifying bits of red component */ + uint32_t GreenMask; /* Mask identifying bits of green component */ + uint32_t BlueMask; /* Mask identifying bits of blue component */ + uint32_t AlphaMask; /* Mask identifying bits of alpha component */ + uint32_t CSType; /* Color space type */ + uint32_t RedX; /* X coordinate of red endpoint */ + uint32_t RedY; /* Y coordinate of red endpoint */ + uint32_t RedZ; /* Z coordinate of red endpoint */ + uint32_t GreenX; /* X coordinate of green endpoint */ + uint32_t GreenY; /* Y coordinate of green endpoint */ + uint32_t GreenZ; /* Z coordinate of green endpoint */ + uint32_t BlueX; /* X coordinate of blue endpoint */ + uint32_t BlueY; /* Y coordinate of blue endpoint */ + uint32_t BlueZ; /* Z coordinate of blue endpoint */ + uint32_t GammaRed; /* Gamma red coordinate scale value */ + uint32_t GammaGreen; /* Gamma green coordinate scale value */ + uint32_t GammaBlue; /* Gamma blue coordinate scale value */ bool Read(GStream &f) { ZeroObj(*this); int64 Start = f.GetPos(); #define Rd(var) if (f.Read(&var, sizeof(var)) != sizeof(var)) \ { LgiTrace("Bmp.Read(%i) failed\n", (int)sizeof(var)); return false; } Rd(Size); // 4 Rd(Sx); // 4 Rd(Sy); // 4 Rd(Planes); // 2 Rd(Bits); // 2 // = 16 if (Size >= 40) { Rd(Compression); // 4 Rd(DataSize); // 4 Rd(XPels); // 4 Rd(YPels); // 4 Rd(ColoursUsed); // 4 Rd(ColourImportant); // 4 // = 24 + 16 = 40 } if (Size >= 52) { Rd(RedMask); // 4 Rd(GreenMask); // 4 Rd(BlueMask); // 4 // 12 + 40 = 52 } if (Size >= 56) { Rd(AlphaMask); // 4 // = 4 + 52 = 56 } if (Size >= 108) { Rd(CSType); // 4 Rd(RedX); // 4 Rd(RedY); // 4 Rd(RedZ); // 4 Rd(GreenX); // 4 Rd(GreenY); // 4 Rd(GreenZ); // 4 Rd(BlueX); // 4 Rd(BlueY); // 4 Rd(BlueZ); // 4 Rd(GammaRed); // 4 Rd(GammaGreen); // 4 Rd(GammaBlue); // 4 // = 52 + 56 = 108 } int64 End = f.GetPos(); int64 Bytes = End - Start; return Bytes >= 12; } }; #ifdef WIN32 #pragma pack(pop, before_pack) #endif -static int CountSetBits(uint32 b) +static int CountSetBits(uint32_t b) { int Count = 0; for (int i=0; iMask < a->Mask ? 1 : -1; } int DownSort(MaskComp *a, MaskComp *b) { return b->Mask > a->Mask ? 1 : -1; } GFilter::IoStatus GdcBmp::ReadImage(GSurface *pDC, GStream *In) { if (!pDC || !In) return GFilter::IoError; ActualBits = 0; ScanSize = 0; BMP_FILE File; BMP_WININFO Info; GRect Cr; Read(In, &File.Type, sizeof(File.Type)); Read(In, &File.Size, sizeof(File.Size)); Read(In, &File.Reserved1, sizeof(File.Reserved1)); Read(In, &File.Reserved2, sizeof(File.Reserved2)); Read(In, &File.OffsetToData, sizeof(File.OffsetToData)); if (!Info.Read(*In)) { LgiTrace("%s:%i - BmpHdr read failed.\n", _FL); return GFilter::IoError; } if (File.Type[0] != 'B' || File.Type[1] != 'M') { LgiTrace("%s:%i - Bmp file id failed: '%.2s'.\n", _FL, File.Type); return GFilter::IoUnsupportedFormat; } ActualBits = Info.Bits; ScanSize = BMPWIDTH(Info.Sx * Info.Bits); int MemBits = MAX(Info.Bits, 8); if (!pDC->Create(Info.Sx, Info.Sy, GBitsToColourSpace(MemBits), ScanSize)) { LgiTrace("%s:%i - MemDC(%i,%i,%i) failed.\n", _FL, Info.Sx, Info.Sy, MAX(Info.Bits, 8)); return GFilter::IoError; } if (pDC->GetBits() <= 8) { int Colours = 1 << ActualBits; GPalette *Palette = new GPalette; if (Palette) { Palette->SetSize(Colours); GdcRGB *Start = (*Palette)[0]; if (Start) { In->Read(Start, sizeof(GdcRGB)*Colours); Palette->SwapRAndB(); } Palette->Update(); pDC->Palette(Palette); } } GBmpMem *pMem = GetSurface(pDC); In->SetPos(File.OffsetToData); GFilter::IoStatus Status = IoSuccess; if (Info.Compression == BI_RLE8) { // 8 bit RLE compressed image int64 Remaining = In->GetSize() - In->GetPos(); uchar *Data = new uchar[Remaining]; if (Data) { if (In->Read(Data, (int)Remaining) == Remaining) { int x=0, y=pDC->Y()-1; uchar *p = Data; bool Done = false; while (!Done && p < Data + Remaining) { uchar Length = *p++; uchar Colour = *p++; if (Length == 0) { switch (Colour) { case 0: { x = 0; y--; break; } case 1: { Done = true; break; } case 2: { x += *p++; y -= *p++; break; } default: { // absolute mode uchar *Pixel = (*pDC)[y]; if (Pixel && y >= 0 && y < pDC->Y()) { int Len = MIN(Colour, pDC->X() - x); if (Len > 0) { memcpy(Pixel + x, p, Len); x += Colour; p += Colour; } } else { p += Colour; } if ((NativeInt) p & 1) p++; break; } } } else { // encoded mode uchar *Pixel = (*pDC)[y]; if (Pixel && y >= 0 && y < pDC->Y()) { int Len = MIN(Length, pDC->X() - x); if (Len > 0) { memset(Pixel + x, Colour, Len); x += Length; } } } } } } } else if (Info.Compression == BI_RLE4) { // 4 bit RLE compressed image // not implemented LgiTrace("%s:%i - BI_RLE4 not implemented.\n", _FL); } else { // Progress if (Meter) { Meter->SetDescription("scanlines"); Meter->SetLimits(0, pMem->y-1); } GColourSpace SrcCs = CsNone; switch (ActualBits) { case 8: SrcCs = CsIndex8; break; case 15: SrcCs = CsRgb15; break; case 16: SrcCs = CsRgb16; break; case 24: SrcCs = CsBgr24; break; case 32: SrcCs = CsBgra32; break; } #if 1 if (Info.Compression == BI_BITFIELDS) { // Should we try and create a colour space from these fields? GArray Comps; Comps.New().Set(CtRed, Info.RedMask); Comps.New().Set(CtGreen, Info.GreenMask); Comps.New().Set(CtBlue, Info.BlueMask); if (Info.AlphaMask) Comps.New().Set(CtAlpha, Info.AlphaMask); Comps.Sort(ActualBits == 16 ? DownSort : UpSort); GColourSpaceBits Cs; Cs.All = 0; for (int i=0; iy-1; y>=0; y--) { if (In->Read(Buffer, ScanSize) == ScanSize) { uchar Mask = 0x80; uchar *d = Buffer; for (int x=0; xX(); x++) { pDC->Colour((*d & Mask) ? 1 : 0); pDC->Set(x, y); Mask >>= 1; if (!Mask) { Mask = 0x80; d++; } } } else { Status = IoError; break; } if (Meter) Meter->Value(pMem->y-1-y); } DeleteArray(Buffer); } break; } case 4: { uchar *Buffer = new uchar[ScanSize]; if (Buffer) { for (int y=pMem->y-1; y>=0; y--) { if (In->Read(Buffer, ScanSize) != ScanSize) { Status = IoError; break; } uchar *d = Buffer; for (int x=0; xX(); x++) { if (x & 1) { pDC->Colour(*d & 0xf); d++; } else { pDC->Colour(*d >> 4); } pDC->Set(x, y); } if (Meter) Meter->Value(pMem->y-1-y); } DeleteArray(Buffer); } break; } default: { GColourSpace DstCs = pDC->GetColourSpace(); for (int i=pMem->y-1; i>=0; i--) { - uint8 *Ptr = pMem->Base + (pMem->Line * i); + uint8_t *Ptr = pMem->Base + (pMem->Line * i); ssize_t r = In->Read(Ptr, ScanSize); if (r != ScanSize) { Status = IoError; LgiTrace("%s:%i - Bmp read err, wanted %i, got %i.\n", _FL, ScanSize, r); break; } if (DstCs != SrcCs) { if (!LgiRopRgb(Ptr, DstCs, Ptr, SrcCs, pMem->x, false)) { Status = IoUnsupportedFormat; LgiTrace("%s:%i - Bmp had unsupported bit depth.\n", _FL); break; } } if (Meter) Meter->Value(pMem->y-1-i); } break; } } } Cr.ZOff(pDC->X()-1, pDC->Y()-1); pDC->ClipRgn(&Cr); pDC->Update(GDC_BITS_CHANGE|GDC_PAL_CHANGE); return Status; } template ssize_t SwapWrite(GStream *Out, T v) { #if 0 // __ORDER_BIG_ENDIAN__ uint8 *s = (uint8*)&v; uint8 *e = s + sizeof(v) - 1; while (s < e) { uint8 tmp = *s; *s++ = *e; *e-- = tmp; } #endif return Out->Write(&v, sizeof(v)); } GFilter::IoStatus GdcBmp::WriteImage(GStream *Out, GSurface *pDC) { GFilter::IoStatus Status = IoError; if (!pDC || !Out) return GFilter::IoError; BMP_FILE File; BMP_WININFO Info; GBmpMem *pMem = GetSurface(pDC); int Colours = (pMem->Cs == CsIndex8) ? 1 << 8 : 0; int UsedBits = GColourSpaceToBits(pMem->Cs); GPalette *Palette = pDC->Palette(); if (pMem->Cs == CsIndex8 && Palette) { int Size = Palette->GetSize(); int Bits = 8; for (int c=256; c; c>>=1, Bits--) { if (c & Size) { Colours = c; UsedBits = Bits; break; } } } if (!pMem || !pMem->x || !pMem->y) { return GFilter::IoError; } Out->SetSize(0); #define Wr(var) SwapWrite(Out, var) Info.Compression = UsedBits == 16 || UsedBits == 32 ? BI_BITFIELDS : BI_RGB; int BitFieldSize = Info.Compression == BI_BITFIELDS ? 16 : 0; Info.Size = 40 + BitFieldSize; File.Type[0] = 'B'; File.Type[1] = 'M'; File.OffsetToData = 14 + Info.Size; File.Size = File.OffsetToData + (int)(ABS(pMem->Line) * pMem->y); File.Reserved1 = 0; File.Reserved2 = 0; Info.Sx = pMem->x; Info.Sy = pMem->y; Info.Planes = 1; Info.Bits = UsedBits; Info.DataSize = 0; Info.XPels = 3000; Info.YPels = 3000; Info.ColoursUsed = Colours; Info.ColourImportant = Colours; bool Written = Out->Write(File.Type, 2) && Wr(File.Size) && Wr(File.Reserved1) && Wr(File.Reserved2) && Wr(File.OffsetToData); Written = Wr(Info.Size) && Wr(Info.Sx) && Wr(Info.Sy) && Wr(Info.Planes) && Wr(Info.Bits) && Wr(Info.Compression) && Wr(Info.DataSize) && Wr(Info.XPels) && Wr(Info.YPels) && Wr(Info.ColoursUsed) && Wr(Info.ColourImportant); if (Written) { if (pMem->Cs == CsIndex8) { int Done = 0; if (Palette) { GdcRGB *Start = (*Palette)[0]; if (Start) { Palette->SwapRAndB(); Out->Write(Start, sizeof(GdcRGB)*Palette->GetSize()); Palette->SwapRAndB(); Done += Palette->GetSize(); } } char Grey[4]; Grey[3] = 0; while (Done < Colours) { int C = Done * 256 / Colours; Grey[0] = C; Grey[1] = C; Grey[2] = C; Out->Write(Grey, 4); Done++; } } // Progress if (Meter) { Meter->SetDescription("scanlines"); Meter->SetLimits(0, pMem->y-1); } int Bytes = BMPWIDTH(pMem->x * UsedBits); Status = GFilter::IoSuccess; switch (UsedBits) { case 1: { uchar *Buffer = new uchar[Bytes]; if (Buffer) { for (int i=pMem->y-1; i>=0; i--) { uchar *Src = pMem->Base + (i * pMem->Line); // assemble the bits memset(Buffer, 0, Bytes); for (int x=0; xx; x++) { Buffer[x>>3] |= (Src[x] & 1) << (x & 3); } // write the bits if (Out->Write(Buffer, Bytes) != Bytes) { Status = IoError; break; } // update status if (Meter) Meter->Value(pMem->y-1-i); } DeleteArray(Buffer); } break; } case 4: { uchar *Buffer = new uchar[Bytes]; if (Buffer) { for (int i=pMem->y-1; i>=0; i--) { uchar *Src = pMem->Base + (i * pMem->Line); // assemble the nibbles for (int x=0; xx; x+=2) { Buffer[x>>1] = (Src[x]<<4) | (Src[x+1]&0x0f); } // write the line if (Out->Write(Buffer, Bytes) != Bytes) { Status = IoError; break; } // update status if (Meter) Meter->Value(pMem->y-1-i); } DeleteArray(Buffer); } break; } default: { if (UsedBits == 16) { System16BitPixel px[8]; ZeroObj(px); px[0].r = 0x1f; px[2].g = 0x3f; px[4].b = 0x1f; Out->Write(px, sizeof(px)); } else if (UsedBits == 32) { System32BitPixel px[4]; ZeroObj(px); px[0].r = 0xff; px[1].g = 0xff; px[2].b = 0xff; px[3].a = 0xff; Out->Write(px, sizeof(px)); } for (int i=pMem->y-1; i>=0; i--) { ssize_t w = Out->Write(pMem->Base + (i * pMem->Line), Bytes); if (w != Bytes) { Status = IoError; break; } if (Meter) Meter->Value(pMem->y-1-i); } break; } } } Out->Close(); return Status; } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // ICO file format class GdcIco : public GFilter { int TruncSize(int i) { if (i >= 64) return 64; if (i >= 32) return 32; return 16; } public: GdcIco(); Format GetFormat() { return FmtIco; } int GetCapabilites() { return FILTER_CAP_READ | FILTER_CAP_WRITE; } int GetImages() { return 1; } IoStatus ReadImage(GSurface *pDC, GStream *In); IoStatus WriteImage(GStream *Out, GSurface *pDC); bool GetVariant(const char *n, GVariant &v, char *a); }; class GdcIcoFactory : public GFilterFactory { bool CheckFile(const char *File, int Access, const uchar *Hint) { return (File) ? stristr(File, ".ico") != 0 : false; } GFilter *NewObject() { return new GdcIco; } } IcoFactory; GdcIco::GdcIco() { } bool GdcIco::GetVariant(const char *n, GVariant &v, char *a) { if (!stricmp(n, LGI_FILTER_TYPE)) { v = "Icon file"; } else if (!stricmp(n, LGI_FILTER_EXTENSIONS)) { v = "ICO"; } else return false; return true; } GFilter::IoStatus GdcIco::ReadImage(GSurface *pDC, GStream *In) { GFilter::IoStatus Status = IoError; int MyBits = 0; int16 Reserved_1; int16 Type; int16 Count; Read(In, &Reserved_1, sizeof(Reserved_1)); Read(In, &Type, sizeof(Type)); Read(In, &Count, sizeof(Count)); for (int Image = 0; Image < Count; Image++) { int8 Width; int8 Height; int8 ColorCount; int8 Reserved_2; int16 Planes; int16 BitCount; int32 BytesInRes; int32 ImageOffset; Read(In, &Width, sizeof(Width)); Read(In, &Height, sizeof(Height)); Read(In, &ColorCount, sizeof(ColorCount)); Read(In, &Reserved_2, sizeof(Reserved_2)); Read(In, &Planes, sizeof(Planes)); Read(In, &BitCount, sizeof(BitCount)); Read(In, &BytesInRes, sizeof(BytesInRes)); Read(In, &ImageOffset, sizeof(ImageOffset)); int64 BytesLeft = BytesInRes; int64 CurrentPos = In->GetPos(); In->SetPos(ImageOffset); BMP_WININFO Header; GdcRGB *Colours; uchar *XorBytes = 0; uchar *AndBytes = 0; int64 StartHdrPos = In->GetPos(); if (!Header.Read(*In)) return GFilter::IoError; BytesLeft -= In->GetPos() - StartHdrPos; if (!Header.Sx) Header.Sx = Width; if (!Header.Sy) Header.Sy = Height; if (!Header.Bits) { if (BitCount) Header.Bits = BitCount; else if (ColorCount) { for (int i=1; i<=8; i++) { if (1 << i >= ColorCount) { BitCount = Header.Bits = i; break; } } } } Colours = new GdcRGB[ColorCount]; if (Colours) { In->Read(Colours, sizeof(GdcRGB) * ColorCount); BytesLeft -= sizeof(GdcRGB) * ColorCount; } Header.Sy >>= 1; int XorSize = BMPWIDTH(Header.Sx * Header.Bits) * Height; int AndSize = BMPWIDTH(Header.Sx) * Height; if (BytesLeft >= XorSize) { XorBytes = new uchar[XorSize]; if (XorBytes) { In->Read(XorBytes, XorSize); BytesLeft -= XorSize; } } if (BytesLeft >= AndSize) { AndBytes = new uchar[AndSize]; if (AndBytes) { In->Read(AndBytes, AndSize); BytesLeft -= AndSize; } } /* LgiTrace("BytesInRes=%i, Xor=%i, And=%i, Pal=%i, BytesLeft=%i\n", BytesInRes, XorSize, AndSize, sizeof(GdcRGB) * Cols, BytesLeft); */ if (Colours && XorBytes && (Header.Bits > MyBits || Width > pDC->X() || Height > pDC->Y()) && pDC->Create(Width, Height, GBitsToColourSpace(MAX(8, Header.Bits)) )) { MyBits = Header.Bits; pDC->Colour(0, 24); pDC->Rectangle(); GPalette *Pal = new GPalette; if (Pal) { Pal->SetSize(ColorCount); memcpy((*Pal)[0], Colours, sizeof(GdcRGB) * ColorCount); Pal->SwapRAndB(); pDC->Palette(Pal, true); } if (AndBytes) { pDC->HasAlpha(true); } GSurface *pAlpha = pDC->AlphaDC(); int XorLine = XorSize / Height; int AndLine = AndSize / Height; for (int y=0; y>3] & (0x80 >> (x&7))) ? 0 : 0xff; } if (Src[x/8] & m) { Dest[x] = 1; } else { Dest[x] = 0; } m >>= 1; if (!m) m = 0x80; } Status = IoSuccess; break; } case 4: { for (int x=0; x>3] & (0x80 >> (x&7))) ? 0 : 0xff; } if (x & 1) { Dest[x] = Src[x>>1] & 0xf; } else { Dest[x] = Src[x>>1] >> 4; } } Status = IoSuccess; break; } case 8: { for (int x=0; x>3] & (0x80 >> (x&7))) ? 0 : 0xff; } Dest[x] = Src[x]; } Status = IoSuccess; break; } } } } else { LgiTrace("%s:%i - Header size error: %i != %i + %i, Img: %ix%i @ %i bits\n", _FL, Header.DataSize, XorSize, AndSize, Header.Sx, Header.Sy, Header.Bits); } DeleteArray(Colours); DeleteArray(XorBytes); DeleteArray(AndBytes); In->SetPos(CurrentPos); } return Status; } GFilter::IoStatus GdcIco::WriteImage(GStream *Out, GSurface *pDC) { GFilter::IoStatus Status = IoError; if (!pDC || pDC->GetBits() > 8 || !Out) return GFilter::IoError; Out->SetSize(0); int ActualBits = pDC->GetBits(); GPalette *Pal = pDC->Palette(); if (Pal) { if (Pal->GetSize() <= 2) { ActualBits = 1; } else if (Pal->GetSize() <= 16) { ActualBits = 4; } } // write file header int Colours = 1 << ActualBits; int16 Reserved_1 = 0; int16 Type = 1; int16 Count = 1; Write(Out, &Reserved_1, sizeof(Reserved_1)); Write(Out, &Type, sizeof(Type)); Write(Out, &Count, sizeof(Count)); // write directory list int8 Width = TruncSize(pDC->X()); int8 Height = TruncSize(pDC->Y()); int8 ColorCount = Colours; int8 Reserved_2 = 0; int16 Planes = 0; int16 BitCount = 0; int Line = (ActualBits * Width) / 8; int MaskLine = BMPWIDTH(Width/8); int32 BytesInRes = sizeof(BMP_WININFO) + (sizeof(GdcRGB) * Colours) + (Line * Height) + (MaskLine * Height); Write(Out, &Width, sizeof(Width)); Write(Out, &Height, sizeof(Height)); Write(Out, &ColorCount, sizeof(ColorCount)); Write(Out, &Reserved_2, sizeof(Reserved_2)); Write(Out, &Planes, sizeof(Planes)); Write(Out, &BitCount, sizeof(BitCount)); Write(Out, &BytesInRes, sizeof(BytesInRes)); int32 ImageOffset = (int32)(Out->GetPos() + sizeof(ImageOffset)); Write(Out, &ImageOffset, sizeof(ImageOffset)); // Write icon itself BMP_WININFO Header; LgiAssert(sizeof(Header) == 40); Header.Size = sizeof(Header); Header.Sx = Width; Header.Sy = Height * 2; Header.Planes = 1; Header.Bits = ActualBits; Header.Compression = 0; // BI_RGB; Header.DataSize = (Line * Height) + (MaskLine * Height); Header.XPels = 0; Header.YPels = 0; Header.ColoursUsed = 0; Header.ColourImportant = 0; Out->Write(&Header, sizeof(Header)); // Write palette if (Pal) { Pal->SwapRAndB(); Out->Write((*Pal)[0], sizeof(GdcRGB) * (int)(1 << ActualBits)); Pal->SwapRAndB(); } // Get background colour COLOUR Back = 0xffffffff; if (Props) { GVariant v; if (Props->GetValue(LGI_FILTER_BACKGROUND, v)) Back = v.CastInt32(); } // Write "Colour" bits int y; for (y=Height-1; y>=0; y--) { uchar *Src = (*pDC)[y]; // xor bits (colour) switch (ActualBits) { case 4: { uchar Dest = 0; for (int x=0; x=0; y--) { uchar *Src = (*pDC)[y]; uchar Dest = 0; uchar Mask = 0x80; int x; for (x = 0; x < Width; x++) { if (Src[x] == Back) Dest |= Mask; Mask >>= 1; if (!Mask) { Write(Out, &Dest, sizeof(Dest)); Dest = 0; Mask = 0x80; } } x = Width / 8; while (x < MaskLine) { uchar c = 0; Write(Out, &c, sizeof(c)); x++; } Status = IoSuccess; } return Status; } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // Run length encoded DC GdcRleDC::GdcRleDC() { Flags = GDC_RLE_COLOUR | GDC_RLE_READONLY; Length = 0; Alloc = 0; Data = 0; ScanLine = 0; Key = 0; pMem = new GBmpMem; if (pMem) { pMem->Base = 0; pMem->x = 0; pMem->y = 0; pMem->Cs = CsNone; pMem->Line = 0; pMem->Flags = 0; } } GdcRleDC::~GdcRleDC() { Empty(); } void GdcRleDC::Empty() { Length = 0; Alloc = 0; DeleteArray(Data); DeleteArray(ScanLine); } bool GdcRleDC::Create(int x, int y, GColourSpace cs, int flags) { bool Status = GMemDC::Create(x, y, cs, flags); if (Status) { Flags &= ~GDC_RLE_READONLY; } return Status; } bool GdcRleDC::CreateInfo(int x, int y, GColourSpace cs) { bool Status = false; DeleteObj(pMem); pMem = new GBmpMem; if (pMem) { pMem->x = x; pMem->y = y; pMem->Cs = cs; pMem->Base = 0; pMem->Line = 0; pMem->Flags = 0; Flags |= GDC_RLE_READONLY; if (cs == CsIndex8) { Flags |= GDC_RLE_MONO; } } return Status; } void GdcRleDC::ReadOnly(bool Read) { if (Read) { Flags |= GDC_RLE_READONLY; } else { if (pMem) { pMem->Base = new uchar[pMem->Line * pMem->y]; } else { // just in case... shouldn't ever happen // am I paranoid? // 'course :) Create(1, 1, System24BitColourSpace); } Flags &= ~GDC_RLE_READONLY; } } bool GdcRleDC::ReadOnly() { return (Flags & GDC_RLE_READONLY) != 0; } void GdcRleDC::Mono(bool Mono) { if (Mono) { Flags |= GDC_RLE_MONO; Flags &= ~GDC_RLE_COLOUR; } else { Flags &= ~GDC_RLE_MONO; Flags |= GDC_RLE_COLOUR; } } bool GdcRleDC::Mono() { return (Flags & GDC_RLE_MONO) != 0; } bool GdcRleDC::SetLength(ssize_t Len) { bool Status = true; if (Len + 1 > Alloc) { int NewAlloc = (Len + 0x4000) & (~0x3FFF); uchar *Temp = new uchar[NewAlloc]; if (Temp) { if (NewAlloc > Length) { memset(Temp + Length, 0, NewAlloc - Length); } memcpy(Temp, Data, Length); DeleteArray(Data); DeleteArray(ScanLine); Data = Temp; Length = Len; Alloc = NewAlloc; } else { Status = false; } } else { // TODO: resize Data to be smaller if theres a // considerable size change Length = Len; } return Status; } void GdcRleDC::Update(int UpdateFlags) { bool Error = false; if ( (UpdateFlags & GDC_BITS_CHANGE) && !(Flags & GDC_RLE_READONLY)) { Empty(); COLOUR Key = Get(0, 0); ssize_t Pos = 0; ulong PixelSize = GetBits() / 8; for (int y=0; !Error && y Length) { Error = true; } } } if (Error) { DeleteArray(ScanLine); } else { Status = true; } } return Status; } void GdcRleDC::Draw(GSurface *Dest, int Ox, int Oy) { int OriX, OriY; Dest->GetOrigin(OriX, OriY); Ox += OriX; Oy += OriY; if (Dest && Data) { bool ReBuildScanInfo = false; if (!ScanLine) ReBuildScanInfo = true; if (ScanLine && Data && ScanLine[0] != Data) ReBuildScanInfo = true; if (ReBuildScanInfo) { // need to rebuild scan line info if (!FindScanLines()) { // couldn't create scan line info so quit return; } } GRect S; S.ZOff(X()-1, Y()-1); GRect D; D = S; D.Offset(Ox, Oy); GRect DClip = D; GRect Temp = Dest->ClipRgn(); D.Bound(&Temp); if (DClip == D && (*Dest)[0]) { // no clipping needed int PixLen = Dest->GetBits() / 8; if (Flags & GDC_RLE_COLOUR) { if (Dest->GetBits() == GetBits()) { for (int y=0; yApplicator(); for (int y=0; ySetPtr(Ox+x, Oy+y); pDApp->Rectangle((int)Pixels, 1); x += Pixels; } } } } else if (DClip.Valid()) { // clip int PixLen = Dest->GetBits() / 8; if (Flags & GDC_RLE_COLOUR) { if (Dest->GetBits() == GetBits()) { for (int y=0; y= Temp.y1 && Fy < Temp.y2) // clip y { uchar *s = ScanLine[y]; uchar *d = (*Dest)[Fy]; for (int x=0; x 0) // clip x { memcpy( d + ((Fx - PreClipPixels) * PixLen), s + (PreClipPixels * PixLen), PixelsLeft * PixLen); } x += Pixels; s += Pixels * PixLen; } } } } } /* else if (Flags & GDC_RLE_MONO) { GApplicator *pDApp = Dest->Applicator(); for (int y=0; ySetPtr(Ox+x, Oy+y); pDApp->Rectangle(Pixels, 1); x += Pixels; } } } */ } } } ssize_t GdcRleDC::SizeOf() { return (sizeof(int) * 5) + Length; } bool GdcRleDC::Read(GFile &F) { Empty(); int32 Len = 0, x = 0, y = 0, bits = 0; int8 Monochrome; F >> Len; if (SetLength(Len)) { F >> Key; F >> x; F >> y; F >> bits; F >> Monochrome; Mono(Monochrome != 0); CreateInfo(x, y, GBitsToColourSpace(bits)); F.Read(Data, Len); return !F.GetStatus(); } return false; } bool GdcRleDC::Write(GFile &F) { if (Data) { char Monochrome = Mono(); F << Length; F << Key; F << X(); F << Y(); F << GetBits(); F << Monochrome; F.Write(Data, Length); return !F.GetStatus(); } return false; } void GdcRleDC::Move(GdcRleDC *pDC) { if (pDC) { Key = pDC->Key; Flags = pDC->Flags; Length = pDC->Length; Alloc = pDC->Alloc; Data = pDC->Data; ScanLine = pDC->ScanLine; pMem = pDC->pMem; pPalette = pDC->pPalette; pDC->pPalette = 0; pDC->pMem = 0; pDC->Length = 0; pDC->Alloc = 0; pDC->Data = 0; pDC->ScanLine = 0; } } //////////////////////////////////////////////////////////////////// GFilterFactory *GFilterFactory::First = 0; GFilterFactory::GFilterFactory() { // add this filter to the global list Next = First; First = this; } GFilterFactory::~GFilterFactory() { // delete from the global list if (First == this) { First = First->Next; } else { GFilterFactory *i = First; while (i->Next && i->Next != this) { i = i->Next; } if (i->Next == this) { i->Next = Next; } else { // we aren't in the list // thats bad LgiAssert(0); } } } GFilter *GFilterFactory::New(const char *File, int Access, const uchar *Hint) { GFilterFactory *i = First; while (i) { if (i->CheckFile(File, Access, Hint)) { return i->NewObject(); } i = i->Next; } return 0; } GFilter *GFilterFactory::NewAt(int n) { int Status = 0; GFilterFactory *i = First; while (i) { if (Status++ == n) { return i->NewObject(); } i = i->Next; } return 0; } int GFilterFactory::GetItems() { int Status = 0; GFilterFactory *i = First; while (i) { Status++; i = i->Next; } return Status; } ////////////////////////////////////////////////////////////////////////// /// Legacy wrapper that calls the new method GSurface *LoadDC(const char *Name, bool UseOSLoader) { return GdcD->Load(Name, UseOSLoader); } GSurface *GdcDevice::Load(const char *Name, bool UseOSLoader) { if (!FileExists(Name)) { // Loading non-file resource... #if WINNATIVE GAutoWString WName(Utf8ToWide(Name)); // a resource... lock and load gentlemen HRSRC hRsrc = FindResource(NULL, WName, RT_BITMAP); if (hRsrc) { int Size = SizeofResource(NULL, hRsrc); HGLOBAL hMem = LoadResource(NULL, hRsrc); if (hMem) { uchar *Ptr = (uchar*) LockResource(hMem); if (Ptr) { GClipBoard Clip(NULL); GSurface *pDC = Clip.ConvertFromPtr(Ptr); GlobalUnlock(hMem); return pDC; } } } #endif return NULL; } GFile File; if (!File.Open(Name, O_READ)) { LgiTrace("%s:%i - Couldn't open '%s' for reading.\n", _FL, Name); return NULL; } return Load(&File, Name, UseOSLoader); } GSurface *GdcDevice::Load(GStream *In, const char *Name, bool UseOSLoader) { if (!In) return NULL; GFilter::IoStatus FilterStatus = GFilter::IoError; int64 Size = In->GetSize(); if (Size <= 0) { return NULL; } GAutoPtr Hint(new uchar[16]); memset(Hint, 0, 16); if (In->Read(Hint, 16) == 0) { Hint.Reset(0); } int64 SeekResult = In->SetPos(0); if (SeekResult != 0) { LgiTrace("%s:%i - Seek failed after reading hint.\n", _FL); return NULL; } GAutoPtr Filter(GFilterFactory::New(Name, FILTER_CAP_READ, Hint)); GAutoPtr pDC; if (Filter && pDC.Reset(new GMemDC)) { FilterStatus = Filter->ReadImage(pDC, In); if (FilterStatus != GFilter::IoSuccess) { pDC.Reset(); LgiTrace("%s:%i - Filter couldn't cope with '%s'.\n", _FL, Name); } } if (UseOSLoader && !pDC) { #if LGI_SDL #elif defined MAC && !defined COCOA CGImageRef Img = NULL; if (FileExists(Name)) { CFURLRef FileUrl = CFURLCreateFromFileSystemRepresentation(0, (const UInt8*)Name, strlen(Name), false); if (!FileUrl) LgiTrace("%s:%i - CFURLCreateFromFileSystemRepresentation failed.\n", _FL); else { CGImageSourceRef Src = CGImageSourceCreateWithURL(FileUrl, 0); if (!Src) LgiTrace("%s:%i - CGImageSourceCreateWithURL failed.\n", _FL); else { Img = CGImageSourceCreateImageAtIndex(Src, 0, 0); if (!Img) LgiTrace("%s:%i - CGImageSourceCreateImageAtIndex failed.\n", _FL); CFRelease(Src); } CFRelease(FileUrl); } } else { GMemStream ms(In, 0, -1); CFDataRef data = CFDataCreate(NULL, (const UInt8 *)ms.GetBasePtr(), ms.GetSize()); CGImageSourceRef Src = CGImageSourceCreateWithData(data, NULL); if (!Src) LgiTrace("%s:%i - CGImageSourceCreateWithURL failed.\n", _FL); else { Img = CGImageSourceCreateImageAtIndex(Src, 0, 0); if (!Img) LgiTrace("%s:%i - CGImageSourceCreateImageAtIndex failed.\n", _FL); CFRelease(Src); } } if (Img) { size_t x = CGImageGetWidth(Img); size_t y = CGImageGetHeight(Img); // size_t bits = CGImageGetBitsPerPixel(Img); if (pDC.Reset(new GMemDC) && pDC->Create(x, y, System32BitColourSpace)) { pDC->Colour(0); pDC->Rectangle(); CGRect r = {{0, 0}, {(CGFloat)x, (CGFloat)y}}; CGContextDrawImage(pDC->Handle(), r, Img); } else { LgiTrace("%s:%i - pMemDC->Create failed.\n", _FL); pDC.Reset(); } CGImageRelease(Img); } #elif WINNATIVE && defined(_MSC_VER) /* char *Ext = LgiGetExtension((char*)Name); if (Ext && stricmp(Ext, "gif") && stricmp(Ext, "png")) */ { IImgCtx *Ctx = 0; HRESULT hr = CoCreateInstance(CLSID_IImgCtx, NULL, CLSCTX_INPROC_SERVER, IID_IImgCtx, (LPVOID*)&Ctx); if (SUCCEEDED(hr)) { GVariant Fn = Name; hr = Ctx->Load(Fn.WStr(), 0); if (SUCCEEDED(hr)) { SIZE Size = { -1, -1 }; ULONG State = 0; int64 Start = LgiCurrentTime(); while (LgiCurrentTime() - Start < 3000) // just in case it gets stuck.... { hr = Ctx->GetStateInfo(&State, &Size, false); if (SUCCEEDED(hr)) { if ((State & IMGLOAD_COMPLETE) || (State & IMGLOAD_STOPPED) || (State & IMGLOAD_ERROR)) { break; } else { LgiSleep(10); } } else break; } if (Size.cx > 0 && Size.cy > 0 && pDC.Reset(new GMemDC)) { if (pDC->Create(Size.cx, Size.cy, System24BitColourSpace)) { HDC hDC = pDC->StartDC(); if (hDC) { RECT rc = { 0, 0, pDC->X(), pDC->Y() }; Ctx->Draw(hDC, &rc); pDC->EndDC(); } if (pDC->GetBits() == 32) { // Make opaque int Op = pDC->Op(GDC_OR); pDC->Colour(Rgba32(0, 0, 0, 255), 32); pDC->Rectangle(); pDC->Op(GDC_SET); } } else pDC.Reset(); } } Ctx->Release(); } } #endif if (pDC) return pDC.Release(); } if (FilterStatus == GFilter::IoComponentMissing) { const char *c = Filter->GetComponentName(); LgiAssert(c != NULL); if (c) { GToken t(c, ","); for (int i=0; iGetOption(GDC_PROMOTE_ON_LOAD); if (PromoteTo > 0 && PromoteTo != pDC->GetBits()) { GAutoPtr pOld;; // GPalette *pPal = pDC->Palette(); pOld = pDC; pDC.Reset(new GMemDC); if (pOld && pDC && pDC->Create(pOld->X(), pOld->Y(), GBitsToColourSpace(PromoteTo))) { pDC->Blt(0, 0, pOld); pOld.Reset(); } } */ #ifdef BEOS else if (pDC->GetBits() == 8) { // Create remap table color_map *Map = (color_map*) system_colors(); GPalette *Palette = pDC->Palette(); if (Map && Palette) { char ReMap[256]; for (int i=0; i<256; i++) { GdcRGB *p = (*Palette)[i]; if (p) { COLOUR c = Rgb15(p->r, p->g, p->b); ReMap[i] = Map->index_map[c]; p->r = Map->color_list[i].red; p->g = Map->color_list[i].green; p->b = Map->color_list[i].blue; } else { ReMap[i] = 0; } } // Remap colours to BeOS palette for (int y=0; yY(); y++) { uchar *d = (*pDC)[y]; if (d) { for (int x=0; xX(); x++) { d[x] = ReMap[d[x]]; } } } } } #endif } #endif return pDC.Release(); } bool WriteDC(const char *Name, GSurface *pDC) { return GdcD->Save(Name, pDC); } bool GdcDevice::Save(const char *Name, GSurface *pDC) { bool Status = false; if (Name && pDC) { GAutoPtr F(GFilterFactory::New(Name, FILTER_CAP_WRITE, 0)); if (F) { GFile File; if (File.Open(Name, O_WRITE)) { Status = F->WriteImage(&File, pDC) == GFilter::IoSuccess; } } else { LgiTrace("No filter to write '%s'\n", Name); } } return Status; } diff --git a/src/common/Gdc2/Filters/Png.cpp b/src/common/Gdc2/Filters/Png.cpp --- a/src/common/Gdc2/Filters/Png.cpp +++ b/src/common/Gdc2/Filters/Png.cpp @@ -1,1548 +1,1548 @@ /*hdr ** FILE: Png.cpp ** AUTHOR: Matthew Allen ** DATE: 8/9/1998 ** DESCRIPTION: Png file filter ** ** Copyright (C) 2002, Matthew Allen ** fret@memecode.com */ // // 'png.h' comes from libpng, which you can get from: // http://www.libpng.org/pub/png/libpng.html // // You will also need zlib, which pnglib requires. zlib // is available here: // http://www.gzip.org/zlib // // If you don't want to build with PNG support then set // the define HAS_LIBPNG_ZLIB to '0' in Lgi.h // #ifndef __CYGWIN__ #include "math.h" #include "png.h" #endif #include "Lgi.h" #include "GPalette.h" #ifdef __CYGWIN__ #include "png.h" #endif #include #include #include #include "GLibraryUtils.h" #ifdef FILTER_UI #include "GTransparentDlg.h" #endif #include "GVariant.h" // Pixel formats -typedef uint8 Png8; +typedef uint8_t Png8; typedef GRgb24 Png24; typedef GRgba32 Png32; typedef GRgb48 Png48; typedef GRgba64 Png64; #if PNG_LIBPNG_VER_MAJOR <= 1 && PNG_LIBPNG_VER_MINOR <= 2 #define png_const_infop png_infop #define png_const_bytep png_bytep #endif #if LIBPNG_SHARED #define LIBPNG Lib-> const char sLibrary[] = #if defined(MAC) "libpng15.15.4.0" #else #if defined(__CYGWIN__) "cygpng12" #else "libpng" #ifdef _MSC_VER _MSC_VER_STR #if defined(WIN64) "x64" #else "x32" #endif #endif #endif #endif ; // Library interface class LibPng : public GLibrary { public: LibPng() : GLibrary(sLibrary) { if (!IsLoaded()) { #if defined(MAC) if (!Load("/opt/local/lib/libpng.dylib")) #elif defined(LINUX) if (!Load("libpng16")) #endif { static bool First = true; if (First) { LgiTrace("%s:%i - Failed to load libpng.\n", _FL); First = false; } } } else { #if 0 char File[256]; GetModuleFileName(Handle(), File, sizeof(File)); LgiTrace("%s:%i - PNG: %s\n", _FL, File); #endif } } DynFunc4( png_structp, png_create_read_struct, png_const_charp, user_png_ver, png_voidp, error_ptr, png_error_ptr, error_fn, png_error_ptr, warn_fn); DynFunc4( png_structp, png_create_write_struct, png_const_charp, user_png_ver, png_voidp, error_ptr, png_error_ptr, error_fn, png_error_ptr, warn_fn); DynFunc1( png_infop, png_create_info_struct, png_structp, png_ptr); DynFunc2( int, png_destroy_info_struct, png_structp, png_ptr, png_infopp, info_ptr); DynFunc3( int, png_destroy_read_struct, png_structpp, png_ptr_ptr, png_infopp, info_ptr_ptr, png_infopp, end_info_ptr_ptr); DynFunc2( int, png_destroy_write_struct, png_structpp, png_ptr_ptr, png_infopp, info_ptr_ptr); DynFunc3( int, png_set_read_fn, png_structp, png_ptr, png_voidp, io_ptr, png_rw_ptr, read_data_fn); DynFunc4( int, png_set_write_fn, png_structp, png_ptr, png_voidp, io_ptr, png_rw_ptr, write_data_fn, png_flush_ptr, output_flush_fn); DynFunc4( int, png_read_png, png_structp, png_ptr, png_infop, info_ptr, int, transforms, png_voidp, params); DynFunc4( int, png_write_png, png_structp, png_ptr, png_infop, info_ptr, int, transforms, png_voidp, params); DynFunc2( png_bytepp, png_get_rows, png_structp, png_ptr, png_infop, info_ptr); DynFunc3( int, png_set_rows, png_structp, png_ptr, png_infop, info_ptr, png_bytepp, row_pointers); DynFunc6( png_uint_32, png_get_iCCP, png_structp, png_ptr, png_const_infop, info_ptr, png_charpp, name, int*, compression_type, png_bytepp, profile, png_uint_32*, proflen); DynFunc6( int, png_set_iCCP, png_structp, png_ptr, png_infop, info_ptr, png_charp, name, int, compression_type, png_const_bytep, profile, png_uint_32, proflen); DynFunc5( png_uint_32, png_get_tRNS, png_structp, png_ptr, png_infop, info_ptr, png_bytep*, trans_alpha, int*, num_trans, png_color_16p*, trans_color); DynFunc3( png_uint_32, png_get_valid, png_structp, png_ptr, png_infop, info_ptr, png_uint_32, flag); DynFunc4( png_uint_32, png_get_PLTE, png_structp, png_ptr, png_infop, info_ptr, png_colorp*, palette, int*, num_palette); DynFunc2( png_uint_32, png_get_image_width, png_structp, png_ptr, png_infop, info_ptr); DynFunc2( png_uint_32, png_get_image_height, png_structp, png_ptr, png_infop, info_ptr); DynFunc2( png_byte, png_get_channels, png_structp, png_ptr, png_infop, info_ptr); #if 1 // PNG_LIBPNG_VER <= 10250 DynFunc2( png_byte, png_get_color_type, png_structp, png_ptr, png_infop, info_ptr); #else DynFunc2( png_byte, png_get_color_type, png_const_structp, png_ptr, png_const_infop, info_ptr); #endif DynFunc2( png_byte, png_get_bit_depth, png_structp, png_ptr, png_infop, info_ptr); DynFunc1( png_voidp, png_get_error_ptr, png_structp, png_ptr); DynFunc1( png_voidp, png_get_io_ptr, png_structp, png_ptr); DynFunc9( int, png_set_IHDR, png_structp, png_ptr, png_infop, info_ptr, png_uint_32, width, png_uint_32, height, int, bit_depth, int, color_type, int, interlace_method, int, compression_method, int, filter_method); DynFunc4( int, png_set_PLTE, png_structp, png_ptr, png_infop, info_ptr, png_colorp, palette, int, num_palette); DynFunc5( int, png_set_tRNS, png_structp, png_ptr, png_infop, info_ptr, png_bytep, trans_alpha, int, num_trans, png_color_16p, trans_color); /* DynFunc2( png_byte, png_get_interlace_type, png_const_structp, png_ptr, png_const_infop, info_ptr); */ }; class InitLibPng : public LMutex { GAutoPtr Png; public: LibPng *Get() { if (Lock(_FL)) { if (!Png) Png.Reset(new LibPng); Unlock(); } return Png; } } CurrentLibPng; #else #define LIBPNG #endif class GdcPng : public GFilter { static char PngSig[]; friend void PNGAPI LibPngError(png_structp Png, png_const_charp Msg); friend void PNGAPI LibPngWarning(png_structp Png, png_const_charp Msg); #if LIBPNG_SHARED LibPng *Lib; #endif int Pos; uchar *PrevScanLine; GSurface *pDC; GMemQueue DataPipe; GView *Parent; jmp_buf Here; public: GdcPng ( #if LIBPNG_SHARED LibPng *lib #endif ); ~GdcPng(); const char *GetComponentName() { return "libpng"; } Format GetFormat() { return FmtPng; } void SetMeter(int i) { if (Meter) Meter->Value(i); } int GetCapabilites() { return FILTER_CAP_READ | FILTER_CAP_WRITE; } IoStatus ReadImage(GSurface *pDC, GStream *In); IoStatus WriteImage(GStream *Out, GSurface *pDC); bool GetVariant(const char *n, GVariant &v, char *a) { if (!_stricmp(n, LGI_FILTER_TYPE)) { v = "Png"; // Portable Network Graphic } else if (!_stricmp(n, LGI_FILTER_EXTENSIONS)) { v = "PNG"; } else return false; return true; } }; // Object Factory class GdcPngFactory : public GFilterFactory { bool CheckFile(const char *File, int Access, const uchar *Hint) { if (Hint) { return Hint[1] == 'P' && Hint[2] == 'N' && Hint[3] == 'G'; } else { return (File) ? stristr(File, ".png") != 0 : false; } } GFilter *NewObject() { return new GdcPng ( #if LIBPNG_SHARED CurrentLibPng.Get() #endif ); } } PngFactory; // Class impl char GdcPng::PngSig[] = { (char)137, 'P', 'N', 'G', '\r', '\n', (char)26, '\n', 0 }; GdcPng::GdcPng( #if LIBPNG_SHARED LibPng *lib #endif ) { #if LIBPNG_SHARED Lib = lib; #endif Parent = 0; Pos = 0; PrevScanLine = 0; } GdcPng::~GdcPng() { DeleteArray(PrevScanLine); } void PNGAPI LibPngError(png_structp Png, png_const_charp Msg) { GdcPng *This = (GdcPng*) #if LIBPNG_SHARED CurrentLibPng.Get()-> #endif png_get_error_ptr(Png); if (This) { printf("Libpng Error Message='%s'\n", Msg); if (This->Props) { GVariant v; This->Props->SetValue(LGI_FILTER_ERROR, v = (char*)Msg); } longjmp(This->Here, -1); } } void PNGAPI LibPngWarning(png_structp Png, png_const_charp Msg) { LgiTrace("LibPng Warning: %s\n", Msg); } void PNGAPI LibPngRead(png_structp Png, png_bytep Ptr, png_size_t Size) { GStream *s = (GStream*) #if LIBPNG_SHARED CurrentLibPng.Get()-> #endif png_get_io_ptr(Png); if (s) { s->Read(Ptr, Size); } else { LgiTrace("%s:%i - No this ptr? (%p)\n", __FILE__, __LINE__, Ptr); LgiAssert(0); } } struct PngWriteInfo { GStream *s; Progress *m; }; void PNGAPI LibPngWrite(png_structp Png, png_bytep Ptr, png_size_t Size) { PngWriteInfo *i = (PngWriteInfo*) #if LIBPNG_SHARED CurrentLibPng.Get()-> #endif png_get_io_ptr(Png); if (i) { i->s->Write(Ptr, Size); /* if (i->m) i->m->Value(Png->flush_rows); */ } else { LgiTrace("%s:%i - No this ptr?\n", __FILE__, __LINE__); LgiAssert(0); } } template void Read32_16(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r >> 3; o->g = i->g >> 2; o->b = i->b >> 3; o++; i++; } } template void Read64_16(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r >> 11; o->g = i->g >> 10; o->b = i->b >> 11; o++; i++; } } template void Read32_24(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r; o->g = i->g; o->b = i->b; o++; i++; } } template void Read64_24(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r >> 8; o->g = i->g >> 8; o->b = i->b >> 8; o++; i++; } } template void Read32_32(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r; o->g = i->g; o->b = i->b; o->a = 255; o++; i++; } } template void Read64_32(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r >> 8; o->g = i->g >> 8; o->b = i->b >> 8; o->a = 255; o++; i++; } } template void ReadAlpha32_16(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r >> 3; o->g = i->g >> 2; o->b = i->b >> 3; o++; i++; } } template void ReadAlpha64_16(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r >> 11; o->g = i->g >> 10; o->b = i->b >> 11; o++; i++; } } template void ReadAlpha32_24(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r; o->g = i->g; o->b = i->b; o++; i++; } } template void ReadAlpha64_24(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r >> 8; o->g = i->g >> 8; o->b = i->b >> 8; o++; i++; } } template void ReadAlpha32_32(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r; o->g = i->g; o->b = i->b; o->a = i->a; o++; i++; } } template void ReadAlpha64_32(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r >> 8; o->g = i->g >> 8; o->b = i->b >> 8; o->a = i->a >> 8; o++; i++; } } GFilter::IoStatus GdcPng::ReadImage(GSurface *pDeviceContext, GStream *In) { GFilter::IoStatus Status = IoError; Pos = 0; pDC = pDeviceContext; DeleteArray(PrevScanLine); GVariant v; if (Props && Props->GetValue(LGI_FILTER_PARENT_WND, v) && v.Type == GV_GVIEW) { Parent = (GView*)v.Value.Ptr; } #if LIBPNG_SHARED if (!Lib->IsLoaded() && !Lib->Load(sLibrary)) { GString s; s.Printf("libpng is missing (%s.%s)", sLibrary, LGI_LIBRARY_EXT); if (Props) Props->SetValue(LGI_FILTER_ERROR, v = s); else LgiTrace("%s:%i - %s\n", _FL, s.Get()); static bool Warn = true; if (Warn) { LgiTrace("%s:%i - Unable to load libpng (%s.%s).\n", _FL, sLibrary, LGI_LIBRARY_EXT); Warn = false; } return GFilter::IoComponentMissing; } #endif png_structp png_ptr = NULL; if (setjmp(Here)) { return Status; } png_ptr = LIBPNG png_create_read_struct( PNG_LIBPNG_VER_STRING, (void*)this, LibPngError, LibPngWarning); if (!png_ptr) { if (Props) Props->SetValue(LGI_FILTER_ERROR, v = "png_create_read_struct failed."); } else { png_infop info_ptr = LIBPNG png_create_info_struct(png_ptr); if (info_ptr) { LIBPNG png_set_read_fn(png_ptr, In, LibPngRead); #if 0 // What was this for again? int off = (char*)&png_ptr->io_ptr - (char*)png_ptr; if (!png_ptr->io_ptr) { printf("io_ptr offset = %i\n", off); LgiAssert(0); CurrentLibPng = 0; return false; } #endif LIBPNG png_read_png(png_ptr, info_ptr, 0, 0); png_bytepp Scan0 = LIBPNG png_get_rows(png_ptr, info_ptr); if (Scan0) { int BitDepth = LIBPNG png_get_bit_depth(png_ptr, info_ptr); int FinalBits = BitDepth == 16 ? 8 : BitDepth; int ColourType = LIBPNG png_get_color_type(png_ptr, info_ptr); int Channels = LIBPNG png_get_channels(png_ptr, info_ptr); int RequestBits = FinalBits * Channels; GColourSpace InCs = ColourType == PNG_COLOR_TYPE_GRAY_ALPHA ? CsIndex8 : GBitsToColourSpace(MAX(RequestBits, 8)); if (!pDC->Create( LIBPNG png_get_image_width(png_ptr, info_ptr), LIBPNG png_get_image_height(png_ptr, info_ptr), InCs, GSurface::SurfaceRequireExactCs)) { printf("%s:%i - GMemDC::Create(%i, %i, %i) failed.\n", _FL, LIBPNG png_get_image_width(png_ptr, info_ptr), LIBPNG png_get_image_height(png_ptr, info_ptr), RequestBits); } else { bool Error = false; #if 1 if (ColourType == PNG_COLOR_TYPE_GRAY_ALPHA) { pDC->HasAlpha(true); // Setup alpha channel } /* printf("PngRead %s->%s\n", GColourSpaceToString(InCs), GColourSpaceToString(pDC->GetColourSpace())); */ #endif // Copy in the scanlines int ActualBits = pDC->GetBits(); int ScanLen = LIBPNG png_get_image_width(png_ptr, info_ptr) * ActualBits / 8; GColourSpace OutCs = pDC->GetColourSpace(); for (int y=0; yY() && !Error; y++) { uchar *Scan = (*pDC)[y]; LgiAssert(Scan != NULL); switch (RequestBits) { case 1: { uchar *o = Scan; uchar *e = Scan + pDC->X(); uchar *i = Scan0[y]; uchar Mask = 0x80; while (o < e) { *o++ = (*i & Mask) ? 1 : 0; Mask >>= 1; if (!Mask) { i++; Mask = 0x80; } } break; } case 4: { uchar *i = Scan0[y]; uchar *o = Scan; for (int x=0; xX(); x++) { if (x & 1) *o++ = *i++ & 0xf; else *o++ = (*i >> 4) & 0xf; } break; } case 8: { memcpy(Scan, Scan0[y], ScanLen); break; } case 16: { if (ColourType == PNG_COLOR_TYPE_GRAY_ALPHA) { - uint8 *grey = Scan; - uint8 *alpha = (*(pDC->AlphaDC()))[y]; + uint8_t *grey = Scan; + uint8_t *alpha = (*(pDC->AlphaDC()))[y]; LgiAssert(grey && alpha); - uint8 *end = grey + pDC->X(); - uint8 *in = Scan0[y]; + uint8_t *end = grey + pDC->X(); + uint8_t *in = Scan0[y]; while (grey < end) { *grey++ = *in++; *alpha++ = *in++; } } else { memcpy(Scan, Scan0[y], ScanLen); } break; } case 24: { switch (OutCs) { #define Read24Case(name, bits) \ case Cs##name: \ { \ if (LIBPNG png_get_bit_depth(png_ptr, info_ptr) == 16) \ Read64_##bits((G##name*)Scan, (Png48*)Scan0[y], pDC->X()); \ else \ Read32_##bits((G##name*)Scan, (Png24*)Scan0[y], pDC->X()); \ break; \ } Read24Case(Rgb16, 16); Read24Case(Bgr16, 16); Read24Case(Rgb24, 24); Read24Case(Bgr24, 24); Read24Case(Xrgb32, 24); Read24Case(Rgbx32, 24); Read24Case(Xbgr32, 24); Read24Case(Bgrx32, 24); Read24Case(Rgba32, 32); Read24Case(Bgra32, 32); Read24Case(Argb32, 32); Read24Case(Abgr32, 32); default: LgiTrace("%s:%i - Unsupported colour space: 0x%x (%s)\n", _FL, pDC->GetColourSpace(), GColourSpaceToString(pDC->GetColourSpace())); LgiAssert(!"Not impl."); break; } break; } case 32: { switch (pDC->GetColourSpace()) { #define Read32Case(name, bits) \ case Cs##name: \ { \ if (LIBPNG png_get_bit_depth(png_ptr, info_ptr) == 16) \ ReadAlpha64_##bits((G##name*)Scan, (Png64*)Scan0[y], pDC->X()); \ else \ ReadAlpha32_##bits((G##name*)Scan, (Png32*)Scan0[y], pDC->X()); \ break; \ } Read32Case(Rgb16, 16); Read32Case(Bgr16, 16); Read32Case(Rgb24, 24); Read32Case(Bgr24, 24); Read32Case(Xrgb32, 24); Read32Case(Rgbx32, 24); Read32Case(Xbgr32, 24); Read32Case(Bgrx32, 24); Read32Case(Rgba32, 32); Read32Case(Bgra32, 32); Read32Case(Argb32, 32); Read32Case(Abgr32, 32); default: LgiTrace("%s:%i - Unsupported colour space: 0x%x (%s)\n", _FL, pDC->GetColourSpace(), GColourSpaceToString(pDC->GetColourSpace())); LgiAssert(!"Not impl."); if (Props) Props->SetValue(LGI_FILTER_ERROR, v = "Missing scan convertor"); Error = true; break; } break; } default: { if (ActualBits == RequestBits) { memcpy(Scan, Scan0[y], ScanLen); } else { LgiAssert(!"Yeah you need to impl a convertor here."); if (Props) Props->SetValue(LGI_FILTER_ERROR, v = "Missing scan convertor"); Error = true; } break; } } } if (RequestBits == 32) { // bool IsPreMul = pDC->IsPreMultipliedAlpha(); pDC->ConvertPreMulAlpha(true); } if (ActualBits <= 8) { // Copy in the palette png_colorp pal; int num_pal = 0; if (LIBPNG png_get_PLTE(png_ptr, info_ptr, &pal, &num_pal) == PNG_INFO_PLTE) { GPalette *Pal = new GPalette(0, num_pal); if (Pal) { for (int i=0; ir = pal[i].red; Rgb->g = pal[i].green; Rgb->b = pal[i].blue; } } pDC->Palette(Pal, true); } } if (LIBPNG png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_bytep trans_alpha = NULL; png_color_16p trans_color; int num_trans; if (LIBPNG png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color)) { pDC->HasAlpha(true); GSurface *Alpha = pDC->AlphaDC(); if (Alpha) { if (trans_alpha) { for (int y=0; yY(); y++) { uchar *a = (*Alpha)[y]; uchar *p = (*pDC)[y]; for (int x=0; xX(); x++) { if (p[x] < num_trans) { a[x] = trans_alpha[p[x]]; } else { a[x] = 0xff; } } } } else if (trans_color) { for (int y=0; yY(); y++) { uchar *a = (*Alpha)[y]; uchar *p = (*pDC)[y]; for (int x=0; xX(); x++) { a[x] = p[x] == trans_color->index ? 0x00 : 0xff; } } } } else { printf("%s:%i - No alpha channel.\n", _FL); } } else { printf("%s:%i - Bad trans ptr.\n", _FL); } } } Status = Error ? IoError : IoSuccess; } } else LgiTrace("%s:%i - png_get_rows failed.\n", _FL); LIBPNG png_destroy_info_struct(png_ptr, &info_ptr); } png_charp ProfName = 0; int CompressionType = 0; png_bytep ColProf = 0; png_uint_32 ColProfLen = 0; if (LIBPNG png_get_iCCP(png_ptr, info_ptr, &ProfName, &CompressionType, &ColProf, &ColProfLen) && Props) { v.SetBinary(ColProfLen, ColProf); Props->SetValue(LGI_FILTER_COLOUR_PROF, v); } LIBPNG png_destroy_read_struct(&png_ptr, 0, 0); } return Status; } GFilter::IoStatus GdcPng::WriteImage(GStream *Out, GSurface *pDC) { GFilter::IoStatus Status = IoError; GVariant Transparent; bool HasTransparency = false; COLOUR Back = 0; GVariant v; if (!pDC) return GFilter::IoError; #if LIBPNG_SHARED if (!Lib->IsLoaded() && !Lib->Load(sLibrary)) { static bool Warn = true; if (Warn) { LgiTrace("%s:%i - Unabled to load libpng.\n", _FL); Warn = false; } return GFilter::IoComponentMissing; } #endif // Work out whether the image has transparency if (pDC->GetBits() == 32) { // Check alpha channel for (int y=0; yY() && !HasTransparency; y++) { System32BitPixel *p = (System32BitPixel*)(*pDC)[y]; if (!p) break; System32BitPixel *e = p + pDC->X(); while (p < e) { if (p->a < 255) { HasTransparency = true; break; } p++; } } } else if (pDC->AlphaDC()) { GSurface *a = pDC->AlphaDC(); if (a) { for (int y=0; yY() && !HasTransparency; y++) { - uint8 *p = (*a)[y]; + uint8_t *p = (*a)[y]; if (!p) break; - uint8 *e = p + a->X(); + uint8_t *e = p + a->X(); while (p < e) { if (*p < 255) { HasTransparency = true; break; } p++; } } } } if (Props) { if (Props->GetValue(LGI_FILTER_PARENT_WND, v) && v.Type == GV_GVIEW) { Parent = (GView*)v.Value.Ptr; } if (Props->GetValue(LGI_FILTER_BACKGROUND, v)) { Back = v.CastInt32(); } Props->GetValue(LGI_FILTER_TRANSPARENT, Transparent); } #ifdef FILTER_UI if (Parent && Transparent.IsNull()) { // put up a dialog to ask about transparent colour GTransparentDlg Dlg(Parent, &Transparent); if (!Dlg.DoModal()) { if (Props) Props->SetValue("Cancel", v = 1); return IoCancel; } } #endif if (setjmp(Here) == 0 && pDC && Out) { GVariant ColProfile; if (Props) { Props->GetValue(LGI_FILTER_COLOUR_PROF, ColProfile); } // setup png_structp png_ptr = LIBPNG png_create_write_struct( PNG_LIBPNG_VER_STRING, (void*)this, LibPngError, LibPngWarning); if (png_ptr) { png_infop info_ptr = LIBPNG png_create_info_struct(png_ptr); if (info_ptr) { Out->SetSize(0); PngWriteInfo WriteInfo; WriteInfo.s = Out; WriteInfo.m = Meter; LIBPNG png_set_write_fn(png_ptr, &WriteInfo, LibPngWrite, 0); // png_set_write_status_fn(png_ptr, write_row_callback); bool KeyAlpha = false; bool ChannelAlpha = false; GMemDC *pTemp = 0; if (pDC->AlphaDC() && HasTransparency) { pTemp = new GMemDC(pDC->X(), pDC->Y(), System32BitColourSpace); if (pTemp) { pTemp->Colour(0); pTemp->Rectangle(); pTemp->Op(GDC_ALPHA); pTemp->Blt(0, 0, pDC); pTemp->Op(GDC_SET); pDC = pTemp; ChannelAlpha = true; } } else { if (Transparent.CastInt32() && Props && Props->GetValue(LGI_FILTER_BACKGROUND, v)) { KeyAlpha = true; } } int Ar = R32(Back); int Ag = G32(Back); int Ab = B32(Back); if (pDC->GetBits() == 32) { if (!ChannelAlpha && !KeyAlpha) { for (int y=0; yY(); y++) { System32BitPixel *s = (System32BitPixel*) (*pDC)[y]; for (int x=0; xX(); x++) { if (s[x].a < 0xff) { ChannelAlpha = true; y = pDC->Y(); break; } } } } } bool ExtraAlphaChannel = ChannelAlpha || (pDC->GetBits() > 8 ? KeyAlpha : 0); int ColourType; if (pDC->GetBits() <= 8) { if (pDC->Palette()) ColourType = PNG_COLOR_TYPE_PALETTE; else ColourType = PNG_COLOR_TYPE_GRAY; } else if (ExtraAlphaChannel) { ColourType = PNG_COLOR_TYPE_RGB_ALPHA; } else { ColourType = PNG_COLOR_TYPE_RGB; } LIBPNG png_set_IHDR(png_ptr, info_ptr, pDC->X(), pDC->Y(), 8, ColourType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); if (ColProfile.Type == GV_BINARY) { LIBPNG png_set_iCCP(png_ptr, info_ptr, (png_charp)"ColourProfile", NULL, (png_const_bytep) ColProfile.Value.Binary.Data, (png_uint_32) ColProfile.Value.Binary.Length); } int TempLine = pDC->X() * ((pDC->GetBits() <= 8 ? 1 : 3) + (ExtraAlphaChannel ? 1 : 0)); uchar *TempBits = new uchar[pDC->Y() * TempLine]; if (Meter) Meter->SetLimits(0, pDC->Y()); switch (pDC->GetBits()) { case 8: { // Output the palette GPalette *Pal = pDC->Palette(); if (Pal) { int Colours = Pal->GetSize(); GAutoPtr PngPal(new png_color[Colours]); if (PngPal) { for (int i=0; ir; PngPal[i].green = Rgb->g; PngPal[i].blue = Rgb->b; } } LIBPNG png_set_PLTE(png_ptr, info_ptr, PngPal, Colours); } } // Copy the pixels for (int y=0; yY(); y++) { uchar *s = (*pDC)[y]; Png8 *d = (Png8*) (TempBits + (TempLine * y)); for (int x=0; xX(); x++) { *d++ = *s++; } } // Setup the transparent palette entry if (KeyAlpha) { static png_byte Trans[256]; for (uint n=0; nbit_depth = 8; //info_ptr->channels = 3 + (ExtraAlphaChannel ? 1 : 0); //info_ptr->color_type = PNG_COLOR_TYPE_RGB | (KeyAlpha ? PNG_COLOR_MASK_ALPHA : 0); for (int y=0; yY(); y++) { uint16 *s = (uint16*) (*pDC)[y]; if (pDC->GetBits() == 15) { if (KeyAlpha) { Png32 *d = (Png32*) (TempBits + (TempLine * y)); for (int x=0; xX(); x++) { d->r = Rc15(*s); d->g = Gc15(*s); d->b = Bc15(*s); d->a = (d->r == Ar && d->g == Ag && d->b == Ab) ? 0 : 0xff; s++; d++; } } else { Png24 *d = (Png24*) (TempBits + (TempLine * y)); for (int x=0; xX(); x++) { d->r = Rc15(*s); d->g = Gc15(*s); d->b = Bc15(*s); s++; d++; } } } else { if (KeyAlpha) { Png32 *d = (Png32*) (TempBits + (TempLine * y)); for (int x=0; xX(); x++) { d->r = Rc16(*s); d->g = Gc16(*s); d->b = Bc16(*s); d->a = (d->r == Ar && d->g == Ag && d->b == Ab) ? 0 : 0xff; s++; d++; } } else { Png24 *d = (Png24*) (TempBits + (TempLine * y)); for (int x=0; xX(); x++) { d->r = Rc16(*s); d->g = Gc16(*s); d->b = Bc16(*s); s++; d++; } } } } break; } case 24: { //info_ptr->bit_depth = 8; //info_ptr->channels = 3 + (KeyAlpha ? 1 : 0); //info_ptr->color_type = PNG_COLOR_TYPE_RGB | (KeyAlpha ? PNG_COLOR_MASK_ALPHA : 0); for (int y=0; yY(); y++) { System24BitPixel *s = (System24BitPixel*) (*pDC)[y]; if (KeyAlpha) { Png32 *d = (Png32*) (TempBits + (TempLine * y)); for (int x=0; xX(); x++) { d->r = s->r; d->g = s->g; d->b = s->b; d->a = (s->r == Ar && s->g == Ag && s->b == Ab) ? 0 : 0xff; s++; d++; } } else { Png24 *d = (Png24*) (TempBits + (TempLine * y)); for (int x=0; xX(); x++) { d->r = s->r; d->g = s->g; d->b = s->b; s++; d++; } } } break; } case 32: { //info_ptr->bit_depth = 8; //info_ptr->channels = 3 + (ExtraAlphaChannel ? 1 : 0); //info_ptr->color_type = PNG_COLOR_TYPE_RGB | (ExtraAlphaChannel ? PNG_COLOR_MASK_ALPHA : 0); for (int y=0; yY(); y++) { System32BitPixel *s = (System32BitPixel*) (*pDC)[y]; if (ChannelAlpha) { Png32 *d = (Png32*) (TempBits + (TempLine * y)); for (int x=0; xX(); x++) { d->r = s->r; d->g = s->g; d->b = s->b; d->a = s->a; s++; d++; } } else if (KeyAlpha) { Png32 *d = (Png32*) (TempBits + (TempLine * y)); Png32 *e = d + pDC->X(); while (d < e) { if (s->a == 0 || (s->r == Ar && s->g == Ag && s->b == Ab) ) { d->r = 0; d->g = 0; d->b = 0; d->a = 0; } else { d->r = s->r; d->g = s->g; d->b = s->b; d->a = s->a; } s++; d++; } } else { Png24 *d = (Png24*) (TempBits + (TempLine * y)); for (int x=0; xX(); x++) { d->r = s->r; d->g = s->g; d->b = s->b; s++; d++; } } } break; } default: { goto CleanUp; } } png_bytep *row = new png_bytep[pDC->Y()]; if (row) { for (int y=0; yY(); y++) { row[y] = TempBits + (TempLine * y); } LIBPNG png_set_rows(png_ptr, info_ptr, row); LIBPNG png_write_png(png_ptr, info_ptr, 0, 0); Status = IoSuccess; DeleteArray(row); } DeleteArray(TempBits); DeleteObj(pTemp); LIBPNG png_destroy_info_struct(png_ptr, &info_ptr); } CleanUp: LIBPNG png_destroy_write_struct(&png_ptr, NULL); } } return Status; } diff --git a/src/common/Gdc2/Font/GDisplayString.cpp b/src/common/Gdc2/Font/GDisplayString.cpp --- a/src/common/Gdc2/Font/GDisplayString.cpp +++ b/src/common/Gdc2/Font/GDisplayString.cpp @@ -1,2184 +1,2184 @@ ////////////////////////////////////////////////////////////////////// // Includes #include #include #include #include #include "Lgi.h" #include "GVariant.h" #include "GFontSelect.h" #include "GdiLeak.h" #include "GDisplayString.h" #include "GPixelRops.h" #include "LUnicodeString.h" #ifdef FontChange #undef FontChange #endif #ifdef LGI_SDL #include "ftsynth.h" #endif #if WINNATIVE static OsChar GDisplayStringDots[] = {'.', '.', '.', 0}; #endif //345678123456781234567812345678 // 2nd #define DEBUG_CHAR_AT 0 #if defined(__GTK_H__) || (defined(MAC) && !defined(LGI_SDL)) #define DISPLAY_STRING_FRACTIONAL_NATIVE 1 #else #define DISPLAY_STRING_FRACTIONAL_NATIVE 0 #endif template bool StringConvert(Out *&out, ssize_t *OutLen, const In *in, ssize_t InLen) { char OutCs[8], InCs[8]; sprintf_s(OutCs, sizeof(OutCs), "utf-%i", (int)sizeof(Out)<<3); sprintf_s(InCs, sizeof(InCs), "utf-%i", (int)sizeof(In)<<3); if (InLen < 0) InLen = StringLen(in); out = (Out*)LgiNewConvertCp ( OutCs, in, InCs, InLen*sizeof(In) ); if (OutLen) *OutLen = out ? StringLen(out) : 0; return out != NULL; } ////////////////////////////////////////////////////////////////////// #define SubtractPtr(a, b) ( (((NativeInt)(a))-((NativeInt)(b))) / sizeof(*a) ) #define VisibleTabChar 0x2192 #define IsTabChar(c) (c == '\t') // || (c == VisibleTabChar && VisibleTab)) // static OsChar GDisplayStringDots[] = {'.', '.', '.', 0}; #if USE_CORETEXT #include void GDisplayString::CreateAttrStr() { if (!StrCache.Get()) return; if (AttrStr) { CFRelease(AttrStr); AttrStr = NULL; } wchar_t *w = StrCache.Get(); CFStringRef string = CFStringCreateWithBytes(kCFAllocatorDefault, (const uint8*)w, StrlenW(w) * sizeof(*w), kCFStringEncodingUTF32LE, false); if (string) { CFDictionaryRef attributes = Font->GetAttributes(); if (attributes) AttrStr = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes); else LgiAssert(0); CFRelease(string); } } #endif GDisplayString::GDisplayString(GFont *f, const char *s, ssize_t l, GSurface *pdc) { pDC = pdc; Font = f; #if LGI_DSP_STR_CACHE StrCache.Reset(Utf8ToWide(s, l)); #endif #if defined(MAC) StringConvert(Str, &len, s, l); #elif defined(WINNATIVE) || defined(LGI_SDL) StringConvert(Str, &len, s, l); #else Str = NewStr(s, l); len = Str ? strlen(Str) : 0; if (!LgiIsUtf8(Str)) printf("%s:%i - Not valid utf\n", _FL); #endif x = y = 0; xf = 0; yf = 0; DrawOffsetF = 0; LaidOut = 0; AppendDots = 0; VisibleTab = 0; #if defined MAC && !defined(LGI_SDL) Hnd = 0; #if USE_CORETEXT AttrStr = NULL; #endif if (Font && Str) { len = l >= 0 ? l : StringLen(Str); if (len > 0) { #if USE_CORETEXT CreateAttrStr(); #else ATSUCreateTextLayout(&Hnd); #endif } } #elif defined __GTK_H__ Hnd = 0; LastTabOffset = -1; if (Font && Str) { len = l >= 0 ? l : strlen(Str); if (len > 0) { Gtk::GtkPrintContext *PrintCtx = pDC ? pDC->GetPrintContext() : NULL; if (PrintCtx) Hnd = Gtk::gtk_print_context_create_pango_layout(PrintCtx); else Hnd = Gtk::pango_layout_new(GFontSystem::Inst()->GetContext()); } } #endif } GDisplayString::GDisplayString(GFont *f, const char16 *s, ssize_t l, GSurface *pdc) { pDC = pdc; Font = f; #if LGI_DSP_STR_CACHE StrCache.Reset(NewStrW(s, l)); #endif #if defined(MAC) || WINNATIVE || defined(LGI_SDL) StringConvert(Str, &len, s, l); #else Str = WideToUtf8(s, l < 0 ? -1 : l); len = Str ? strlen(Str) : 0; #endif x = y = 0; xf = 0; yf = 0; DrawOffsetF = 0; LaidOut = 0; AppendDots = 0; VisibleTab = 0; #if defined MAC && !defined(LGI_SDL) Hnd = NULL; #if USE_CORETEXT AttrStr = NULL; #endif if (Font && Str && len > 0) { #if USE_CORETEXT CreateAttrStr(); #else OSStatus e = ATSUCreateTextLayout(&Hnd); if (e) printf("%s:%i - ATSUCreateTextLayout failed with %i.\n", _FL, (int)e); #endif } #elif defined __GTK_H__ Hnd = 0; if (Font && Str && len > 0) { Gtk::GtkPrintContext *PrintCtx = pDC ? pDC->GetPrintContext() : NULL; if (PrintCtx) Hnd = Gtk::gtk_print_context_create_pango_layout(PrintCtx); else Hnd = Gtk::pango_layout_new(GFontSystem::Inst()->GetContext()); } #endif } #ifdef _MSC_VER -GDisplayString::GDisplayString(GFont *f, const uint32 *s, ssize_t l, GSurface *pdc) +GDisplayString::GDisplayString(GFont *f, const uint32_t *s, ssize_t l, GSurface *pdc) { pDC = pdc; Font = f; #if LGI_DSP_STR_CACHE size_t Chars = l < 0 ? Strlen(s) : l; StrCache.Reset((char16*)LgiNewConvertCp(LGI_WideCharset, s, "utf-32", Chars*4)); #endif #if defined(MAC) || defined(LGI_SDL) || defined(_MSC_VER) StringConvert(Str, &len, s, l); #else Str = WideToUtf8(s, l < 0 ? -1 : l); len = Str ? strlen(Str) : 0; #endif x = y = 0; xf = 0; yf = 0; DrawOffsetF = 0; LaidOut = 0; AppendDots = 0; VisibleTab = 0; } #endif GDisplayString::~GDisplayString() { #if defined(LGI_SDL) Img.Reset(); #elif defined MAC #if USE_CORETEXT if (Hnd) { CFRelease(Hnd); Hnd = NULL; } if (AttrStr) { CFRelease(AttrStr); AttrStr = NULL; } #else if (Hnd) ATSUDisposeTextLayout(Hnd); #endif #elif defined __GTK_H__ if (Hnd) g_object_unref(Hnd); #endif DeleteArray(Str); } #if defined __GTK_H__ void GDisplayString::UpdateTabs(int Offset, int Size, bool Debug) { if (Hnd && Font && Font->TabSize()) { int Len = 16; LastTabOffset = Offset; Gtk::PangoTabArray *t = Gtk::pango_tab_array_new(Len, true); if (t) { for (int i=0; i> 1); pDC->Line(r.x1, r.y1+Cy, r.x2, r.y1+Cy); pDC->Line(r.x2, r.y1+Cy, r.x2-Cy, r.y1); pDC->Line(r.x2, r.y1+Cy, r.x2-Cy, r.y2); } else // Space { int x = r.x1 + (r.X()>>1) - 1; int Cy = r.y1 + (int)ceil(Font->Ascent()) - 2; pDC->Rectangle(x, Cy, x+1, Cy+1); } } void GDisplayString::Layout(bool Debug) { if (LaidOut || !Font) return; LaidOut = 1; #if defined(LGI_SDL) FT_Face Fnt = Font->Handle(); FT_Error error; if (!Fnt || !Str) return; // Create an array of glyph indexes GArray Glyphs; for (OsChar *s = Str; *s; s++) { FT_UInt index = FT_Get_Char_Index(Fnt, *s); if (index) Glyphs.Add(index); } // Measure the string... GdcPt2 Sz; int FontHt = Font->GetHeight(); int AscentF = (int) (Font->Ascent() * FScale); int LoadMode = FT_LOAD_FORCE_AUTOHINT; for (unsigned i=0; iglyph->metrics.horiBearingY; Sz.x += Fnt->glyph->metrics.horiAdvance; Sz.y = MAX(Sz.y, PyF + Fnt->glyph->metrics.height); } } // Create the memory context to draw into xf = Sz.x; x = ((Sz.x + FScale - 1) >> FShift) + 1; yf = FontHt << FShift; y = FontHt; // ((Sz.y + FScale - 1) >> FShift) + 1; if (Img.Reset(new GMemDC(x, y, CsIndex8))) { // Clear the context to black Img->Colour(0); Img->Rectangle(); bool IsItalic = Font->Italic(); int CurX = 0; int FBaseline = Fnt->size->metrics.ascender; for (unsigned i=0; iglyph); error = FT_Render_Glyph(Fnt->glyph, FT_RENDER_MODE_NORMAL); if (error == 0) { FT_Bitmap &bmp = Fnt->glyph->bitmap; if (bmp.buffer) { // Copy rendered glyph into our image memory int Px = (CurX + (FScale >> 1)) >> FShift; int PyF = AscentF - Fnt->glyph->metrics.horiBearingY; int Py = PyF >> FShift; int Skip = 0; if (Fnt->glyph->format == FT_GLYPH_FORMAT_BITMAP) { Px += Fnt->glyph->bitmap_left; Skip = Fnt->glyph->bitmap_left < 0 ? -Fnt->glyph->bitmap_left : 0; Py = (AscentF >> FShift) - Fnt->glyph->bitmap_top; } LgiAssert(Px + bmp.width <= Img->X()); for (int y=0; y= 0); out += Px+Skip; memcpy(out, in+Skip, bmp.width-Skip); } /* else { LgiAssert(!"No scanline?"); break; } */ } } if (i < Glyphs.Length() - 1) { FT_Vector kerning; FT_Get_Kerning(Fnt, Glyphs[i], Glyphs[i+1], FT_KERNING_DEFAULT, &kerning); CurX += Fnt->glyph->metrics.horiAdvance + kerning.x; } else { CurX += Fnt->glyph->metrics.horiAdvance; } } } } } else LgiTrace("::Layout Create MemDC failed\n"); #elif defined(__GTK_H__) y = Font->GetHeight(); yf = y * PANGO_SCALE; if (!Hnd || !Font->Handle()) { // LgiTrace("%s:%i - Missing handle: %p,%p\n", _FL, Hnd, Font->Handle()); return; } if (!LgiIsUtf8(Str)) { LgiTrace("%s:%i - Not utf8\n", _FL); return; } GFontSystem *FSys = GFontSystem::Inst(); Gtk::pango_context_set_font_description(FSys->GetContext(), Font->Handle()); int TabSizePx = Font->TabSize(); int TabSizeF = TabSizePx * FScale; int TabOffsetF = DrawOffsetF % TabSizeF; int OffsetF = TabOffsetF ? TabSizeF - TabOffsetF : 0; /* if (Debug) { printf("'%s', TabSizeF=%i, TabOffsetF=%i, DrawOffsetF=%i, OffsetF=%i\n", Str, TabSizeF, TabOffsetF, DrawOffsetF, OffsetF); } */ UpdateTabs(OffsetF / FScale, Font->TabSize()); if (Font->Underline()) { Gtk::PangoAttrList *attrs = Gtk::pango_attr_list_new(); Gtk::PangoAttribute *attr = Gtk::pango_attr_underline_new(Gtk::PANGO_UNDERLINE_SINGLE); Gtk::pango_attr_list_insert(attrs, attr); Gtk::pango_layout_set_attributes(Hnd, attrs); Gtk::pango_attr_list_unref(attrs); } Gtk::pango_layout_set_text(Hnd, Str, len); Gtk::pango_layout_get_size(Hnd, &xf, &yf); x = (xf + PANGO_SCALE - 1) / PANGO_SCALE; #if 1 y = Font->GetHeight(); #else y = (yf + PANGO_SCALE - 1) / PANGO_SCALE; #endif if (y > Font->GetHeight()) { printf("%s:%i - Height error: %i > %i\n", _FL, y, Font->GetHeight()); } #elif defined MAC && !defined(LGI_SDL) #if USE_CORETEXT int height = Font->GetHeight(); y = height; if (AttrStr) { LgiAssert(!Hnd); Hnd = CTLineCreateWithAttributedString(AttrStr); if (Hnd) { CGFloat ascent = 0.0; CGFloat descent = 0.0; CGFloat leading = 0.0; double width = CTLineGetTypographicBounds(Hnd, &ascent, &descent, &leading); x = ceil(width); xf = width * FScale; yf = height * FScale; } } #else if (!Hnd || !Str) return; OSStatus e = ATSUSetTextPointerLocation(Hnd, Str, 0, len, len); if (e) { char *a = 0; StringConvert(a, NULL, Str, len); printf("%s:%i - ATSUSetTextPointerLocation failed with errorcode %i (%s)\n", _FL, (int)e, a); DeleteArray(a); return; } e = ATSUSetRunStyle(Hnd, Font->Handle(), 0, len); if (e) { char *a = 0; StringConvert(a, NULL, Str, len); printf("%s:%i - ATSUSetRunStyle failed with errorcode %i (%s)\n", _FL, (int)e, a); DeleteArray(a); return; } ATSUTextMeasurement fTextBefore; ATSUTextMeasurement fTextAfter; if (pDC) { OsPainter dc = pDC->Handle(); ATSUAttributeTag Tags[1] = {kATSUCGContextTag}; ByteCount Sizes[1] = {sizeof(CGContextRef)}; ATSUAttributeValuePtr Values[1] = {&dc}; e = ATSUSetLayoutControls(Hnd, 1, Tags, Sizes, Values); if (e) printf("%s:%i - ATSUSetLayoutControls failed (e=%i)\n", _FL, (int)e); } ATSUTab Tabs[32]; for (int i=0; iTabSize()) << 16; Tabs[i].tabType = kATSULeftTab; } e = ATSUSetTabArray(Hnd, Tabs, CountOf(Tabs)); if (e) printf("%s:%i - ATSUSetTabArray failed (e=%i)\n", _FL, (int)e); e = ATSUGetUnjustifiedBounds( Hnd, kATSUFromTextBeginning, kATSUToTextEnd, &fTextBefore, &fTextAfter, &fAscent, &fDescent); if (e) { char *a = 0; StringConvert(a, NULL, Str, len); printf("%s:%i - ATSUGetUnjustifiedBounds failed with errorcode %i (%s)\n", _FL, (int)e, a); DeleteArray(a); return; } xf = fTextAfter - fTextBefore; yf = fAscent + fDescent; x = (xf + 0xffff) >> 16; y = (yf + 0xffff) >> 16; ATSUSetTransientFontMatching(Hnd, true); #endif #elif defined WINNATIVE int sx, sy, i = 0; if (!Font) return; if (!Font->Handle()) Font->Create(); y = Font->GetHeight(); GFontSystem *Sys = GFontSystem::Inst(); if (Sys && Str) { GFont *f = Font; bool GlyphSub = Font->SubGlyphs(); Info[i].Str = Str; int TabSize = Font->TabSize() ? Font->TabSize() : 32; bool WasTab = IsTabChar(*Str); f = GlyphSub ? Sys->GetGlyph(*Str, Font) : Font; if (f && f != Font) { f->Size(Font->Size()); f->SetWeight(Font->GetWeight()); if (!f->Handle()) f->Create(); } bool Debug = WasTab; - uint32 u32; + uint32_t u32; for (LUnicodeString u(Str, len); true; u++) { u32 = *u; GFont *n = GlyphSub ? Sys->GetGlyph(u32, Font) : Font; bool Change = n != f || // The font changed (IsTabChar(u32) ^ WasTab) || // Entering/leaving a run of tabs !u32 || // Hit a NULL character (u.Get() - Info[i].Str) >= 1000; // This is to stop very long segments not rendering if (Change) { // End last segment if (n && n != Font) { n->Size(Font->Size()); n->SetWeight(Font->GetWeight()); if (!n->Handle()) n->Create(); } Info[i].Len = (int) (u.Get() - Info[i].Str); if (Info[i].Len) { if (WasTab) { // Handle tab(s) for (int t=0; tGetHeight() > Font->GetHeight())) { Info[i].SizeDelta = -1; f->PointSize(Font->PointSize() + Info[i].SizeDelta); f->Create(); } #endif if (!f) { // no font, so ? out the chars... as they aren't available anyway // printf("Font Cache Miss, Len=%i\n\t", Info[i].Len); m = Font; for (int n=0; n_Measure(sx, sy, Info[i].Str, Info[i].Len); x += Info[i].X = sx > 0xffff ? 0xffff : sx; } auto Ch = Info[i].First(); Info[i].FontId = !f || Font == f ? 0 : Sys->Lut[Ch]; i++; } f = n; // Start next segment WasTab = IsTabChar(u32); Info[i].Str = u.Get(); } if (!u32) break; } if (Info.Length() > 0 && Info.Last().Len == 0) { Info.Length(Info.Length()-1); } } xf = x; yf = y; #elif defined BEOS if (Font && Font->Handle()) { int TabSize = Font->TabSize() ? Font->TabSize() : 32; int TabOrigin = DrawOffsetF / FScale; char *End = Str + len; char *s = Str; x = 0; while (s < End) { char *Start = s; while (s < End && *s && *s != '\t') s++; if (s > Start) { // Normal segment CharInfo &i = Info.New(); i.Str = Start; i.Len = s - Start; GdcPt2 size = Font->StringBounds(i.Str, i.Len); i.X = size.x; i.FontId = -1; i.SizeDelta = 0; x += size.x; } Start = s; while (s < End && *s && *s == '\t') s++; if (s > Start) { // Tabs segment CharInfo &i = Info.New(); i.Str = Start; i.Len = s - Start; i.X = 0; i.FontId = -1; i.SizeDelta = 0; for (int n=0; nGetHeight(); xf = x; #if 0 printf("Layout '%s' = %i,%i\n", Str, x, y); for (int i=0; iStr, ci->Len, ci->X); } #endif } else printf("%s:%i - No font or handle.\n", _FL); #endif } int GDisplayString::GetDrawOffset() { return DrawOffsetF >> FShift; } void GDisplayString::SetDrawOffset(int Px) { if (LaidOut) LgiAssert(!"No point setting TabOrigin after string is laid out.\n"); DrawOffsetF = Px << FShift; } bool GDisplayString::ShowVisibleTab() { return VisibleTab; } void GDisplayString::ShowVisibleTab(bool i) { VisibleTab = i; } bool GDisplayString::IsTruncated() { return AppendDots; } void GDisplayString::TruncateWithDots(int Width) { Layout(); #if defined __GTK_H__ if (Hnd) { Gtk::pango_layout_set_ellipsize(Hnd, Gtk::PANGO_ELLIPSIZE_END); Gtk::pango_layout_set_width(Hnd, Width * PANGO_SCALE); } #elif WINNATIVE if (Width < X() + 8) { ssize_t c = CharAt(Width); if (c >= 0 && c < len) { if (c > 0) c--; // fudge room for dots if (c > 0) c--; AppendDots = 1; if (Info.Length()) { int Width = 0; int Pos = 0; for (int i=0; i= Pos && c < Pos + Info[i].Len) { Info[i].Len = (int) (c - Pos); Info[i].Str[Info[i].Len] = 0; GFont *f = Font; if (Info[i].FontId) { GFontSystem *Sys = GFontSystem::Inst(); f = Sys->Font[Info[i].FontId]; f->PointSize(Font->PointSize() + Info[i].SizeDelta); if (!f->Handle()) { f->Create(); } } else { f = Font; } if (f) { int Sx, Sy; f->_Measure(Sx, Sy, Info[i].Str, Info[i].Len); Info[i].X = Sx; Width += Info[i].X; } Info.Length(i + 1); break; } Pos += Info[i].Len; Width += Info[i].X; } int DotsX, DotsY; Font->_Measure(DotsX, DotsY, GDisplayStringDots, 3); x = Width + DotsX; } } } #elif defined(LGI_SDL) #elif defined(MAC) #if USE_CORETEXT if (Hnd) { CFAttributedStringRef truncationString = CFAttributedStringCreate(NULL, CFSTR("\u2026"), Font->GetAttributes()); if (truncationString) { CTLineRef truncationToken = CTLineCreateWithAttributedString(truncationString); CFRelease(truncationString); if (truncationToken) { CTLineRef TruncatedLine = CTLineCreateTruncatedLine(Hnd, Width, kCTLineTruncationEnd, truncationToken); CFRelease(truncationToken); if (TruncatedLine) { CFRelease(Hnd); Hnd = TruncatedLine; } } } } #endif #endif } ssize_t GDisplayString::CharAt(int Px, LgiPxToIndexType Type) { int Status = -1; Layout(); if (Px < 0) { return 0; } else if (Px >= (int)x) { #if defined __GTK_H__ if (Str) { GUtf8Str u(Str); return u.GetChars(); } return 0; #else return len; #endif } #if defined MAC && !defined(LGI_SDL) if (Hnd && Str) { #if USE_CORETEXT #if 1 CGPoint pos = { (CGFloat)Px, 1.0f }; CFIndex idx = CTLineGetStringIndexForPosition(Hnd, pos); return idx; #else CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedString(AttrStr); if (typesetter) { CFIndex count = CTTypesetterSuggestLineBreak(typesetter, 0, Px); CFRelease(typesetter); // printf("CharAt(%i) = %i\n", Px, (int)count); return count; } Status = Off; #endif #else UniCharArrayOffset Off = 0; // Boolean IsLeading; OSStatus e = ATSUPositionToOffset(Hnd, FloatToFixed(Px), FloatToFixed(y / 2), &Off, &IsLeading, &Off2); if (e) printf("%s:%i - ATSUPositionToOffset failed with %i, CharAt(%i) x=%i len=%i\n", _FL, (int)e, Px, x, len); else Status = Off; #endif } #elif defined __GTK_H__ if (Hnd) { int Index = 0, Trailing = 0; if (Gtk::pango_layout_xy_to_index(Hnd, Px * PANGO_SCALE, 0, &Index, &Trailing)) { // printf("Index = %i, Trailing = %i\n", Index, Trailing); GUtf8Str u(Str); Status = 0; while ((OsChar*)u.GetPtr() < Str + Index) { u++; Status++; } } else if (Trailing) { GUtf8Str u(Str + Index); if (u) u++; Status = (OsChar*)u.GetPtr() - Str; } else Status = 0; } #elif defined(LGI_SDL) #else // This case is for Win32 and Haiku. #if defined(BEOS) if (Font && Font->Handle()) #elif defined(WINNATIVE) GFontSystem *Sys = GFontSystem::Inst(); if (Info.Length() && Font && Sys) #endif { int TabSize = Font->TabSize() ? Font->TabSize() : 32; int Cx = 0; int Char = 0; #if DEBUG_CHAR_AT printf("CharAt(%i) Str='%s'\n", Px, Str); #endif for (int i=0; i= Cx && Px < Cx + Info[i].X) { // The position is in this block of characters if (IsTabChar(Info[i].Str[0])) { // Search through tab block for (int t=0; t= Cx && Px < Cx + TabX) { Status = Char; #if DEBUG_CHAR_AT printf("\tIn tab block %i\n", i); #endif break; } Cx += TabX; Char++; } } else { // Find the pos in this block GFont *f = Font; #if defined(BEOS) BString s(Info[i].Str, Info[i].Len); Font->Handle()->TruncateString(&s, B_TRUNCATE_END, Px - Cx); int Fit = s.CountChars(); #elif defined(WIN32) if (Info[i].FontId) { f = Sys->Font[Info[i].FontId]; f->Colour(Font->Fore(), Font->Back()); f->Size(Font->Size()); if (!f->Handle()) { f->Create(); } } int Fit = f->_CharAt(Px - Cx, Info[i].Str, Info[i].Len, Type); #endif #if DEBUG_CHAR_AT printf("\tNon tab block %i, Fit=%i, Px-Cx=%i-%i=%i, Str='%.5s'\n", i, Fit, Px, Cx, Px-Cx, Info[i].Str); #endif if (Fit >= 0) { Status = Char + Fit; } else { Status = -1; } break; } } else { // Not in this block, skip the whole lot Cx += Info[i].X; Char += Info[i].Len; } } if (Status < 0) { Status = Char; } } #endif return Status; } ssize_t GDisplayString::Length() { return len; } void GDisplayString::Length(int New) { Layout(); #if WINNATIVE if (New < len) { GFontSystem *Sys = GFontSystem::Inst(); int CurX = 0; int CurLen = 0; for (int i=0; i= CurLen && New < CurLen + Info[i].Len ) { // In this block int Offset = New - CurLen; Info[i].Len = Offset; Info[i].Str[Info[i].Len] = 0; // Get the font for this block of characters GFont *f = 0; if (Info[i].FontId) { f = Sys->Font[Info[i].FontId]; f->PointSize(Font->PointSize()); f->Transparent(Font->Transparent()); if (!f->Handle()) { f->Create(); } } else { f = Font; } // Chop the current block and re-measure int ChoppedX, Unused; f->_Measure(ChoppedX, Unused, Info[i].Str, Info[i].Len); Info[i].X = ChoppedX; x = CurX + Info[i].X; Info.Length(i + 1); // Leave the loop break; } CurX += Info[i].X; CurLen += Info[i].Len; } } else { printf("%s:%i - New>=Len (%i>=" LPrintfSSizeT" )\n", _FL, New, len); } #endif } int GDisplayString::X() { Layout(); return x; } int GDisplayString::Y() { Layout(); return y; } GdcPt2 GDisplayString::Size() { Layout(); return GdcPt2(x, y); } #if defined LGI_SDL template bool CompositeText8Alpha(GSurface *Out, GSurface *In, GFont *Font, int px, int py, GBlitRegions &Clip) { OutPx map[256]; if (!Out || !In || !Font) return false; // FIXME, do blt clipping here... // Create colour map of the foreground/background colours uint8 *Div255 = Div255Lut; GColour fore = Font->Fore(); GRgb24 fore_px; fore_px.r = fore.r(); fore_px.g = fore.g(); fore_px.b = fore.b(); if (Font->Transparent()) { for (int a=0; a<256; a++) { map[a].r = Div255[a * fore_px.r]; map[a].g = Div255[a * fore_px.g]; map[a].b = Div255[a * fore_px.b]; map[a].a = a; } } else { GColour back = Font->Back(); GRgb24 back_px; back_px.r = back.r(); back_px.g = back.g(); back_px.b = back.b(); for (int a=0; a<256; a++) { int oma = 255 - a; map[a].r = (oma * back_px.r) + (a * fore_px.r) / 255; map[a].g = (oma * back_px.g) + (a * fore_px.g) / 255; map[a].b = (oma * back_px.b) + (a * fore_px.b) / 255; map[a].a = 255; } } uint8 *StartOfBuffer = (*Out)[0]; uint8 *EndOfBuffer = StartOfBuffer + (Out->GetRowStep() * Out->Y()); for (unsigned y=Clip.SrcClip.y1; y<=Clip.SrcClip.y2; y++) { OutPx *d = ((OutPx*) (*Out)[py + y]) + Clip.DstClip.x1; uint8 *i = (*In)[y]; if (!i) return false; uint8 *e = i + Clip.DstClip.X(); LgiAssert((uint8*)d >= StartOfBuffer); if (Font->Transparent()) { uint8 a, o; OutPx *s; while (i < e) { // Alpha blend map and output pixel a = *i++; switch (a) { case 0: break; case 255: // Copy *d = map[a]; break; default: // Blend o = 255 - a; s = map + a; #define NonPreMulOver32NoAlpha(c) d->c = ((s->c * a) + (Div255[d->c * 255] * o)) / 255 NonPreMulOver32NoAlpha(r); NonPreMulOver32NoAlpha(g); NonPreMulOver32NoAlpha(b); break; } d++; } } else { while (i < e) { // Copy rop *d++ = map[*i++]; } } LgiAssert((uint8*)d <= EndOfBuffer); } return true; } template bool CompositeText8NoAlpha(GSurface *Out, GSurface *In, GFont *Font, int px, int py, GBlitRegions &Clip) { GRgba32 map[256]; if (!Out || !In || !Font) return false; // FIXME, do blt clipping here... // Create colour map of the foreground/background colours uint8 *DivLut = Div255Lut; GColour fore = Font->Fore(); GRgb24 fore_px; fore_px.r = fore.r(); fore_px.g = fore.g(); fore_px.b = fore.b(); if (Font->Transparent()) { for (int a=0; a<256; a++) { map[a].r = DivLut[a * fore_px.r]; map[a].g = DivLut[a * fore_px.g]; map[a].b = DivLut[a * fore_px.b]; map[a].a = a; } } else { GColour back = Font->Back(); GRgb24 back_px; back_px.r = back.r(); back_px.g = back.g(); back_px.b = back.b(); for (int a=0; a<256; a++) { int oma = 255 - a; map[a].r = DivLut[(oma * back_px.r) + (a * fore_px.r)]; map[a].g = DivLut[(oma * back_px.g) + (a * fore_px.g)]; map[a].b = DivLut[(oma * back_px.b) + (a * fore_px.b)]; map[a].a = 255; } } uint8 *StartOfBuffer = (*Out)[0]; uint8 *EndOfBuffer = StartOfBuffer + (Out->GetRowStep() * Out->Y()); for (int y=Clip.SrcClip.y1; y<=Clip.SrcClip.y2; y++) { OutPx *dst = (OutPx*) (*Out)[py + y]; if (!dst) continue; dst += Clip.DstClip.x1; if ((uint8*)dst < StartOfBuffer) continue; uint8 *i = (*In)[y]; if (!i) return false; uint8 *e = i + Clip.DstClip.X(); GRgba32 *src; LgiAssert((uint8*)dst >= StartOfBuffer); if (Font->Transparent()) { uint8 a, oma; while (i < e) { // Alpha blend map and output pixel a = *i++; src = map + a; switch (a) { case 0: break; case 255: // Copy dst->r = src->r; dst->g = src->g; dst->b = src->b; break; default: // Blend OverPm32toPm24(src, dst); break; } dst++; } } else { while (i < e) { // Copy rop src = map + *i++; dst->r = src->r; dst->g = src->g; dst->b = src->b; dst++; } } LgiAssert((uint8*)dst <= EndOfBuffer); } return true; } template bool CompositeText5NoAlpha(GSurface *Out, GSurface *In, GFont *Font, int px, int py, GBlitRegions &Clip) { OutPx map[256]; if (!Out || !In || !Font) return false; // FIXME, do blt clipping here... #define MASK_5BIT 0x1f #define MASK_6BIT 0x3f // Create colour map of the foreground/background colours uint8 *Div255 = Div255Lut; GColour fore = Font->Fore(); GRgb24 fore_px; fore_px.r = fore.r(); fore_px.g = fore.g(); fore_px.b = fore.b(); if (Font->Transparent()) { for (int a=0; a<256; a++) { map[a].r = (int)Div255[a * fore_px.r] >> 3; map[a].g = (int)Div255[a * fore_px.g] >> 2; map[a].b = (int)Div255[a * fore_px.b] >> 3; } } else { GColour back = Font->Back(); GRgb24 back_px; back_px.r = back.r(); back_px.g = back.g(); back_px.b = back.b(); for (int a=0; a<256; a++) { int oma = 255 - a; map[a].r = Div255[(oma * back_px.r) + (a * fore_px.r)] >> 3; map[a].g = Div255[(oma * back_px.g) + (a * fore_px.g)] >> 2; map[a].b = Div255[(oma * back_px.b) + (a * fore_px.b)] >> 3; } } uint8 *StartOfBuffer = (*Out)[0]; uint8 *EndOfBuffer = StartOfBuffer + (Out->GetRowStep() * Out->Y()); for (unsigned y=Clip.SrcClip.y1; y<=Clip.SrcClip.y2; y++) { OutPx *dst = ((OutPx*) (*Out)[py + y]); if (!dst) continue; dst += Clip.DstClip.x1; uint8 *i = (*In)[y]; if (!i) return false; uint8 *e = i + Clip.DstClip.X(); LgiAssert((uint8*)dst >= StartOfBuffer); if (Font->Transparent()) { uint8 a; OutPx *src; while (i < e) { // Alpha blend map and output pixel a = *i++; switch (a) { case 0: break; case 255: // Copy *dst = map[a]; break; default: { // Blend #if 0 uint8 oma = 255 - a; src = map + a; GRgb24 d = { G5bitTo8bit(dst->r), G6bitTo8bit(dst->g), G5bitTo8bit(dst->b)}; GRgb24 s = { G5bitTo8bit(src->r), G6bitTo8bit(src->g), G5bitTo8bit(src->b)}; dst->r = Div255[(oma * d.r) + (a * s.r)] >> 3; dst->g = Div255[(oma * d.g) + (a * s.g)] >> 2; dst->b = Div255[(oma * d.b) + (a * s.b)] >> 3; #else uint8 a5 = a >> 3; uint8 a6 = a >> 2; uint8 oma5 = MASK_5BIT - a5; uint8 oma6 = MASK_6BIT - a6; src = map + a; dst->r = ((oma5 * (uint8)dst->r) + (a5 * (uint8)src->r)) / MASK_5BIT; dst->g = ((oma6 * (uint8)dst->g) + (a6 * (uint8)src->g)) / MASK_6BIT; dst->b = ((oma5 * (uint8)dst->b) + (a5 * (uint8)src->b)) / MASK_5BIT; #endif break; } } dst++; } } else { while (i < e) { // Copy rop *dst++ = map[*i++]; } } LgiAssert((uint8*)dst <= EndOfBuffer); } return true; } #endif void GDisplayString::Draw(GSurface *pDC, int px, int py, GRect *r, bool Debug) { Layout(); #if DISPLAY_STRING_FRACTIONAL_NATIVE // GTK / Mac use fractional pixels, so call the fractional version: GRect rc; if (r) { rc = *r; rc.x1 <<= FShift; rc.y1 <<= FShift; #ifdef MAC rc.x2 <<= FShift; rc.y2 <<= FShift; #else rc.x2 = (rc.x2 + 1) << FShift; rc.y2 = (rc.y2 + 1) << FShift; #endif } FDraw(pDC, px << FShift, py << FShift, r ? &rc : NULL, Debug); #elif defined LGI_SDL if (Img && pDC && pDC->Y() > 0 && (*pDC)[0]) { int Ox = 0, Oy = 0; pDC->GetOrigin(Ox, Oy); GBlitRegions Clip(pDC, px-Ox, py-Oy, Img, r); GColourSpace DstCs = pDC->GetColourSpace(); switch (DstCs) { #define DspStrCase(px_fmt, comp) \ case Cs##px_fmt: \ CompositeText##comp(pDC, Img, Font, px-Ox, py-Oy, Clip); \ break; DspStrCase(Rgb16, 5NoAlpha) DspStrCase(Bgr16, 5NoAlpha) DspStrCase(Rgb24, 8NoAlpha) DspStrCase(Bgr24, 8NoAlpha) DspStrCase(Rgbx32, 8NoAlpha) DspStrCase(Bgrx32, 8NoAlpha) DspStrCase(Xrgb32, 8NoAlpha) DspStrCase(Xbgr32, 8NoAlpha) DspStrCase(Rgba32, 8Alpha) DspStrCase(Bgra32, 8Alpha) DspStrCase(Argb32, 8Alpha) DspStrCase(Abgr32, 8Alpha) default: LgiTrace("%s:%i - GDisplayString::Draw Unsupported colour space.\n", _FL); // LgiAssert(!"Unsupported colour space."); break; #undef DspStrCase } } else LgiTrace("::Draw argument error.\n"); #elif defined WINNATIVE if (Info.Length() && pDC && Font) { GFontSystem *Sys = GFontSystem::Inst(); COLOUR Old = pDC->Colour(); int TabSize = Font->TabSize() ? Font->TabSize() : 32; int Ox = px; GColour cFore = Font->Fore(); GColour cBack = Font->Back(); GColour cWhitespace; if (VisibleTab) { cWhitespace = Font->WhitespaceColour(); LgiAssert(cWhitespace.IsValid()); } for (int i=0; iFont[Info[i].FontId]; f->Colour(cFore, cBack); auto Sz = Font->Size(); Sz.Value += Info[i].SizeDelta; f->Size(Sz); f->Transparent(Font->Transparent()); f->Underline(Font->Underline()); if (!f->Handle()) { f->Create(); } } else { f = Font; } if (f) { GRect b; if (r) { b.x1 = i ? px : r->x1; b.y1 = r->y1; b.x2 = i < Info.Length() - 1 ? px + Info[i].X - 1 : r->x2; b.y2 = r->y2; } else { b.x1 = px; b.y1 = py; b.x2 = px + Info[i].X - 1; b.y2 = py + Y() - 1; } if (b.Valid()) { if (IsTabChar(*Info[i].Str)) { // Invisible tab... draw blank space if (!Font->Transparent()) { pDC->Colour(cBack); pDC->Rectangle(&b); } if (VisibleTab) { int X = px; for (int n=0; nColour(cWhitespace); DrawWhiteSpace(pDC, '\t', r); X += Dx; } } } else { // Draw the character(s) GColour Fg = f->Fore(); LgiAssert(Fg.IsValid()); f->_Draw(pDC, px, py, Info[i].Str, Info[i].Len, &b, Fg); if (VisibleTab) { OsChar *start = Info[i].Str; OsChar *s = start; OsChar *e = s + Info[i].Len; int Sp = -1; while (s < e) { if (*s == ' ') { int Sx, Sy; if (Sp < 0) f->_Measure(Sp, Sy, s, 1); f->_Measure(Sx, Sy, start, (int)(s - start)); GRect r(0, 0, Sp-1, Sy-1); r.Offset(px + Sx, py); pDC->Colour(cWhitespace); DrawWhiteSpace(pDC, ' ', r); } s++; } } } } } // Inc my position px += Info[i].X; } if (AppendDots) { int Sx, Sy; Font->_Measure(Sx, Sy, GDisplayStringDots, 3); GRect b; if (r) { b.x1 = px; b.y1 = r->y1; b.x2 = min(px + Sx - 1, r->x2); b.y2 = r->y2; } else { b.x1 = px; b.y1 = py; b.x2 = px + Sx - 1; b.y2 = py + Y() - 1; } GColour Fg = Font->Fore(); Font->_Draw(pDC, px, py, GDisplayStringDots, 3, &b, Fg); } pDC->Colour(Old); } else if (r && Font && !Font->Transparent()) { pDC->Colour(Font->Back()); pDC->Rectangle(r); } #elif defined(BEOS) if (pDC && Font) { rgb_color Fg = Font->Fore(), Bk = Font->Back(); // Paint text BView *Hnd = pDC->Handle(); if (Hnd) { GLocker Locker(Hnd, _FL); Locker.Lock(); drawing_mode Prev = Hnd->DrawingMode(); Hnd->SetDrawingMode(B_OP_COPY); // Draw background if required. if (!Font->Transparent()) { Hnd->SetHighColor(Bk); if (r) { Hnd->FillRect(*r); } else { GRect b; b.ZOff(x-1, y-1); b.Offset(px, py); Hnd->FillRect(b); } } // Draw foreground text segments Hnd->SetHighColor(Fg); Hnd->SetLowColor(Bk); Hnd->SetFont(Font->Handle()); Hnd->SetDrawingMode(B_OP_OVER); int CurX = 0; for (int i=0; iAscent()); if (ci->Str[0] != '\t') Hnd->DrawString(ci->Str, ci->Len, pos); /* printf("DrawString %i, %i -> '%.*s' in %x,%x,%x\n", px, py, ci->Len, ci->Str, Fg.red, Fg.green, Fg.blue); */ #if 0 // useful to debug where strings are drawn { GRect r; r.ZOff(ci->X-1, Font->GetHeight()-1); r.Offset(px + CurX, py); pDC->Box(&r); } #endif CurX += ci->X; } Hnd->SetDrawingMode(Prev); if (!pDC->IsScreen()) Hnd->Sync(); } else printf("%s:%i - Error: no BView to draw on.\n", _FL); } else printf("%s:%i - Error: no DC or Font.\n", _FL); #endif } int GDisplayString::GetDrawOffsetF() { return DrawOffsetF; } void GDisplayString::SetDrawOffsetF(int Fpx) { if (LaidOut) LgiAssert(!"No point setting TabOrigin after string is laid out.\n"); DrawOffsetF = Fpx; } int GDisplayString::FX() { Layout(); return xf; } int GDisplayString::FY() { Layout(); return y; } GdcPt2 GDisplayString::FSize() { Layout(); return GdcPt2(xf, yf); } void GDisplayString::FDraw(GSurface *pDC, int fx, int fy, GRect *frc, bool Debug) { Layout(Debug); #if !DISPLAY_STRING_FRACTIONAL_NATIVE // BeOS / Windows don't use fractional pixels, so call the integer version: GRect rc; if (frc) { rc = *frc; rc.x1 >>= FShift; rc.y1 >>= FShift; rc.x2 >>= FShift; rc.y2 >>= FShift; } Draw(pDC, fx >> FShift, fy >> FShift, frc ? &rc : NULL, Debug); #elif defined __GTK_H__ Gtk::pango_context_set_font_description(GFontSystem::Inst()->GetContext(), Font->Handle()); Gtk::cairo_t *cr = pDC->Handle(); if (!cr) { LgiAssert(!"Can't get cairo."); return; } int Ox = 0, Oy = 0; pDC->GetOrigin(Ox, Oy); Gtk::cairo_save(cr); GRect Client; if (pDC->GetClient(&Client) && Client.Valid()) { Gtk::cairo_rectangle(cr, Client.x1, Client.y1, Client.X(), Client.Y()); Gtk::cairo_clip(cr); Gtk::cairo_new_path(cr); } else { Client.ZOff(-1, -1); } GColour b = Font->Back(); Gtk::cairo_set_source_rgb(cr, (double)b.r()/255.0, (double)b.g()/255.0, (double)b.b()/255.0); if (!Font->Transparent() && frc) { Gtk::cairo_new_path(cr); Gtk::cairo_rectangle ( cr, ((double)frc->x1 / FScale) - Ox, ((double)frc->y1 / FScale) - Oy, (double)frc->X() / FScale, (double)frc->Y() / FScale ); Gtk::cairo_fill(cr); } double Dx = ((double)fx / FScale) - Ox; double Dy = ((double)fy / FScale) - Oy; cairo_translate(cr, Dx, Dy); if (!Font->Transparent() && !frc) { Gtk::cairo_new_path(cr); Gtk::cairo_rectangle(cr, 0, 0, x, y); Gtk::cairo_fill(cr); } if (Hnd) { GColour f = Font->Fore(); Gtk::cairo_set_source_rgb( cr, (double)f.r()/255.0, (double)f.g()/255.0, (double)f.b()/255.0); Gtk::pango_cairo_show_layout(cr, Hnd); if (VisibleTab && Str) { GUtf8Str Ptr(Str); pDC->Colour(Font->WhitespaceColour()); for (int32 u, Idx = 0; u = Ptr; Idx++) { if (IsTabChar(u) || u == ' ') { Gtk::PangoRectangle pos; Gtk::pango_layout_index_to_pos(Hnd, Idx, &pos); GRect r(0, 0, pos.width / FScale, pos.height / FScale); r.Offset(Dx + (pos.x / FScale), Dy + (pos.y / FScale)); DrawWhiteSpace(pDC, u, r); } Ptr++; } } } Gtk::cairo_restore(cr); #elif defined MAC && !defined(LGI_SDL) int Ox = 0, Oy = 0; int px = fx >> FShift; int py = fy >> FShift; GRect rc; if (frc) rc.Set( frc->x1 >> FShift, frc->y1 >> FShift, frc->x2 >> FShift, frc->y2 >> FShift); if (pDC && !pDC->IsScreen()) pDC->GetOrigin(Ox, Oy); if (pDC && !Font->Transparent()) { GColour Old = pDC->Colour(Font->Back()); if (frc) { pDC->Rectangle(&rc); } else { GRect a(px, py, px + x - 1, py + y - 1); pDC->Rectangle(&a); } pDC->Colour(Old); } if (Hnd && pDC && len > 0) { OsPainter dc = pDC->Handle(); #if USE_CORETEXT int y = (pDC->Y() - py + Oy); CGContextSaveGState(dc); pDC->Colour(Font->Fore()); if (pDC->IsScreen()) { if (frc) { CGRect rect = rc; rect.size.width += 1.0; rect.size.height += 1.0; CGContextClipToRect(dc, rect); } CGContextTranslateCTM(dc, 0, pDC->Y()-1); CGContextScaleCTM(dc, 1.0, -1.0); } else { if (frc) { CGContextSaveGState(dc); CGRect rect = rc; rect.origin.x -= Ox; rect.origin.y = pDC->Y() - rect.origin.y + Oy - rect.size.height; rect.size.width += 1.0; rect.size.height += 1.0; CGContextClipToRect(dc, rect); } } CGFloat Tx = (CGFloat)fx / FScale - Ox; CGFloat Ty = (CGFloat)y - Font->Ascent(); CGContextSetTextPosition(dc, Tx, Ty); CTLineDraw(Hnd, dc); CGContextRestoreGState(dc); #else ATSUAttributeTag Tags[1] = {kATSUCGContextTag}; ByteCount Sizes[1] = {sizeof(CGContextRef)}; ATSUAttributeValuePtr Values[1] = {&dc}; e = ATSUSetLayoutControls(Hnd, 1, Tags, Sizes, Values); if (e) { printf("%s:%i - ATSUSetLayoutControls failed (e=%i)\n", _FL, (int)e); } else { // Set style attr ATSURGBAlphaColor c; GColour Fore = Font->Fore(); c.red = (double) Fore.r() / 255.0; c.green = (double) Fore.g() / 255.0; c.blue = (double) Fore.b() / 255.0; c.alpha = 1.0; ATSUAttributeTag Tags[] = {kATSURGBAlphaColorTag}; ATSUAttributeValuePtr Values[] = {&c}; ByteCount Lengths[] = {sizeof(c)}; e = ATSUSetAttributes( Font->Handle(), CountOf(Tags), Tags, Lengths, Values); if (e) { printf("%s:%i - Error setting font attr (e=%i)\n", _FL, (int)e); } else { int y = (pDC->Y() - py + Oy); if (pDC->IsScreen()) { CGContextSaveGState(dc); if (frc) { CGRect rect = rc; rect.size.width += 1.0; rect.size.height += 1.0; CGContextClipToRect(dc, rect); } CGContextTranslateCTM(dc, 0, pDC->Y()-1); CGContextScaleCTM(dc, 1.0, -1.0); e = ATSUDrawText(Hnd, kATSUFromTextBeginning, kATSUToTextEnd, fx - Long2Fix(Ox), Long2Fix(y) - fAscent); CGContextRestoreGState(dc); } else { if (frc) { CGContextSaveGState(dc); CGRect rect = rc; rect.origin.x -= Ox; rect.origin.y = pDC->Y() - rect.origin.y + Oy - rect.size.height; rect.size.width += 1.0; rect.size.height += 1.0; CGContextClipToRect(dc, rect); } e = ATSUDrawText(Hnd, kATSUFromTextBeginning, kATSUToTextEnd, Long2Fix(px - Ox), Long2Fix(y) - fAscent); if (frc) CGContextRestoreGState(dc); } if (e) { char *a = 0; StringConvert(a, NULL, Str, len); printf("%s:%i - ATSUDrawText failed with %i, len=%i, str=%.20s\n", _FL, (int)e, len, a); DeleteArray(a); } } } #endif } #endif } diff --git a/src/common/Gdc2/Font/GFont.cpp b/src/common/Gdc2/Font/GFont.cpp --- a/src/common/Gdc2/Font/GFont.cpp +++ b/src/common/Gdc2/Font/GFont.cpp @@ -1,2441 +1,2441 @@ /*hdr ** FILE: GFont.cpp ** AUTHOR: Matthew Allen ** DATE: 5/5/97 ** DESCRIPTION: Gdc2 Font Support ** ** Copyright (C) 1997-2002, Matthew Allen ** fret@memecode.com */ #ifndef WIN32 #define _WIN32_WINNT 0x500 #endif ////////////////////////////////////////////////////////////////////// // Includes #include #include #include #include #include "Lgi.h" #include "GVariant.h" #include "GFontSelect.h" #include "GDisplayString.h" #include "GdiLeak.h" #include "GDisplayString.h" #include "GStringClass.h" #include "GCss.h" #ifdef FontChange #undef FontChange #endif #define MAC_FONT_SIZE_OFFSET 0 ////////////////////////////////////////////////////////////////////// // Helpers #if defined(LGI_SDL) class FreetypeLib { FT_Library lib; FT_Error err; public: FreetypeLib() { err = FT_Init_FreeType(&lib); if (err) { LgiAssert(0); } } ~FreetypeLib() { if (!err) { FT_Done_FreeType(lib); } } FT_Library Handle() { return lib; } GString GetVersion() { FT_Int amajor = 0, aminor = 0, apatch = 0; FT_Library_Version(lib, &amajor, &aminor, &apatch); GString s; s.Printf("%i.%i.%i", amajor, aminor, apatch); return s; } } Freetype2; GString GetFreetypeLibraryVersion() { return Freetype2.GetVersion(); } #elif defined(WIN32) #ifndef __GNUC__ #include #endif int WinPointToHeight(int Pt, HDC hDC) { int Ht = 0; HWND hDestktop = NULL; if (!hDC) hDC = GetDC(hDestktop = GetDesktopWindow()); if (hDC) Ht = -MulDiv(Pt, GetDeviceCaps(hDC, LOGPIXELSY), 72); if (hDestktop) ReleaseDC(hDestktop, hDC); return Ht; } int WinHeightToPoint(int Ht, HDC hDC) { int Pt = 0; HWND hDestktop = NULL; if (!hDC) hDC = GetDC(hDestktop = GetDesktopWindow()); if (hDC) Pt = -MulDiv(Ht, 72, GetDeviceCaps(hDC, LOGPIXELSY)); if (hDestktop) ReleaseDC(hDestktop, hDC); return Pt; } #elif defined(MAC) // CTFontCreateUIFontForLanguage // #include #include #endif ////////////////////////////////////////////////////////////////////// // Classes class GTypeFacePrivate { public: // Type char *_Face; // type face GCss::Len _Size; // size int _Weight; bool _Italic; bool _Underline; char *_CodePage; int _Quality; // Output GColour _Fore; GColour _Back; GColour WhiteSpace; // Can be empty (if so it's calculated) int _TabSize; bool _Transparent; bool _SubGlyphs; // Backs bool IsSymbol; // Props double _Ascent, _Descent, _Leading; GTypeFacePrivate() { IsSymbol = false; _Ascent = _Descent = _Leading = 0.0; _Face = 0; _Size = GCss::Len(GCss::LenPt, 8.0f); _Weight = FW_NORMAL; _Italic = false; _Underline = false; _CodePage = NewStr("utf-8"); _Fore.Rgb(0, 0, 0); _Back.Rgb(255, 255, 255); _TabSize = 32; _Transparent = false; _Quality = DEFAULT_QUALITY; _SubGlyphs = GFontSystem::Inst()->GetDefaultGlyphSub(); } ~GTypeFacePrivate() { DeleteArray(_Face); DeleteArray(_CodePage); } }; GTypeFace::GTypeFace() { d = new GTypeFacePrivate; } GTypeFace::~GTypeFace() { DeleteObj(d); } bool GTypeFace::operator ==(GTypeFace &t) { if ((Face() == 0) ^ (t.Face() == 0)) return false; if (Face() && t.Face() && stricmp(Face(), t.Face()) != 0) return false; if (Size() != t.Size()) return false; if (GetWeight() != t.GetWeight()) return false; if (Italic() != t.Italic()) return false; if (Underline() != t.Underline()) return false; return true; } // set void GTypeFace::Face(const char *s) { if (s && s != d->_Face && stricmp(s, d->_Face?d->_Face:(char*)"") != 0) { DeleteArray(d->_Face); d->_Face = NewStr(s); LgiAssert(d->_Face != NULL); _OnPropChange(true); } } void GTypeFace::Size(GCss::Len s) { if (d->_Size != s) { d->_Size = s; _OnPropChange(true); } } void GTypeFace::PointSize(int i) { Size(GCss::Len(GCss::LenPt, (float)i)); } void GTypeFace::TabSize(int i) { d->_TabSize = MAX(i, 8); _OnPropChange(false); } void GTypeFace::Quality(int i) { if (d->_Quality != i) { d->_Quality = i; _OnPropChange(true); } } GColour GTypeFace::WhitespaceColour() { if (d->WhiteSpace.IsValid()) return d->WhiteSpace; return d->_Back.Mix(d->_Fore, LGI_WHITESPACE_WEIGHT); } void GTypeFace::WhitespaceColour(GColour c) { d->WhiteSpace = c; _OnPropChange(false); } void GTypeFace::Fore(COLOUR c) { d->_Fore.c24(c); _OnPropChange(false); } void GTypeFace::Fore(GColour c) { d->_Fore = c; _OnPropChange(false); } void GTypeFace::Back(COLOUR c) { d->_Back.c24(c); _OnPropChange(false); } void GTypeFace::Back(GColour c) { d->_Back = c; _OnPropChange(false); } void GTypeFace::SetWeight(int i) { if (d->_Weight != i) { d->_Weight = i; _OnPropChange(true); } } void GTypeFace::Italic(bool i) { if (d->_Italic != i) { d->_Italic = i; _OnPropChange(true); } } void GTypeFace::Underline(bool i) { if (d->_Underline != i) { d->_Underline = i; _OnPropChange(true); } } void GTypeFace::Transparent(bool i) { d->_Transparent = i; _OnPropChange(false); } void GTypeFace::Colour(COLOUR Fore, COLOUR Back) { d->_Fore.c24(Fore); d->_Back.c24(Back); _OnPropChange(false); } void GTypeFace::Colour(GColour Fore, GColour Back) { LgiAssert(Fore.IsValid()); d->_Fore = Fore; d->_Back = Back; // Transparent(Back.Transparent()); _OnPropChange(false); } void GTypeFace::SubGlyphs(bool i) { if (!i || GFontSystem::Inst()->GetGlyphSubSupport()) { d->_SubGlyphs = i; _OnPropChange(false); } } //////////////////////// // get char *GTypeFace::Face() { return d->_Face; } GCss::Len GTypeFace::Size() { return d->_Size; } int GTypeFace::PointSize() { if (d->_Size.Type == GCss::LenPt) return (int)d->_Size.Value; LgiAssert(!"What now?"); return 0; } int GTypeFace::TabSize() { return d->_TabSize; } int GTypeFace::Quality() { return d->_Quality; } GColour GTypeFace::Fore() { return d->_Fore; } GColour GTypeFace::Back() { return d->_Back; } int GTypeFace::GetWeight() { return d->_Weight; } bool GTypeFace::Italic() { return d->_Italic; } bool GTypeFace::Underline() { return d->_Underline; } bool GTypeFace::Transparent() { return d->_Transparent; } bool GTypeFace::SubGlyphs() { return d->_SubGlyphs; } double GTypeFace::Ascent() { return d->_Ascent; } double GTypeFace::Descent() { return d->_Descent; } double GTypeFace::Leading() { return d->_Leading; } //////////////////////////////////////////////////////////////////// // GFont class, the implemention #include "GFontPriv.h" #ifdef WINDOWS GAutoPtr GFontPrivate::Gdi32; #endif GFont::GFont(const char *face, GCss::Len size) { d = new GFontPrivate; if (face && size.IsValid()) { Create(face, size); } } GFont::GFont(OsFont Handle) { d = new GFontPrivate; GFontType Type; if (Type.GetFromRef(Handle)) { Create(&Type); } } GFont::GFont(GFontType &Type) { d = new GFontPrivate; Create(&Type); } GFont::GFont(GFont &Fnt) { d = new GFontPrivate; *this = Fnt; } GFont::~GFont() { Destroy(); DeleteObj(d); } bool GFont::CreateFromCss(const char *Css) { if (!Css) return false; GCss c; c.Parse(Css); return CreateFromCss(&c); } bool GFont::CreateFromCss(GCss *Css) { if (!Css) return false; GCss::StringsDef Fam = Css->FontFamily(); if (Fam.Length()) Face(Fam[0]); GCss::Len Sz = Css->FontSize(); switch (Sz.Type) { case GCss::SizeSmaller: Size(GCss::Len(GCss::LenPt, (float)SysFont->PointSize()-1)); break; case GCss::SizeLarger: Size(GCss::Len(GCss::LenPt, (float)SysFont->PointSize()+1)); break; case GCss::LenInherit: Size(SysFont->Size()); break; default: Size(Sz); break; } GCss::FontWeightType w = Css->FontWeight(); if (w == GCss::FontWeightBold) Bold(true); GCss::FontStyleType s = Css->FontStyle(); if (s == GCss::FontStyleItalic) Italic(true); GCss::TextDecorType dec = Css->TextDecoration(); if (dec == GCss::TextDecorUnderline) Underline(true); return Create(); } bool GFont::Destroy() { bool Status = true; if (d->hFont) { #if LGI_SDL FT_Done_Face(d->hFont); #elif defined(WIN32) DeleteObject(d->hFont); #elif defined MAC #if USE_CORETEXT CFRelease(d->hFont); #else ATSUDisposeStyle(d->hFont); #endif #elif defined LINUX if (d->PangoCtx) { g_object_unref(d->PangoCtx); d->PangoCtx = NULL; } Gtk::pango_font_description_free(d->hFont); #elif defined BEOS DeleteObj(d->hFont); #else LgiAssert(0); #endif d->hFont = 0; } DeleteArray(d->GlyphMap); return Status; } #if defined(MAC) CFDictionaryRef GFont::GetAttributes() { return d->Attributes; } #endif #ifdef BEOS #include "GUtf8.h" GdcPt2 GFont::StringBounds(const char *s, int len) { GdcPt2 Sz(0, GetHeight()); - GArray CacheMiss; + GArray CacheMiss; if (s) { GUtf8Ptr p(s); GUtf8Ptr end(s + (len < 0 ? strlen(s) : len); while (p < end) { - uint32 c = p; + uint32_t c = p; if (c < 0x80) { Sz.x += d->CharX[c]; } else { int cx = d->UnicodeX.Find(c); if (cx) { Sz.x += cx; } else { CacheMiss.Add(c); printf("%s:%i - Char cache miss: %i (0x%x)\n", _FL, c, c); // LgiAssert(!"Impl me."); } } p++; } } return Sz; } #endif uchar *GFont::GetGlyphMap() { return d->GlyphMap; } bool GFont::GetOwnerUnderline() { return d->OwnerUnderline; } void GFont::_OnPropChange(bool FontChange) { if (FontChange) { Destroy(); } } OsFont GFont::Handle() { return d->hFont; } int GFont::GetHeight() { if (!d->hFont) { Create(); } LgiAssert(d->Height != 0); return d->Height; } bool GFont::IsValid() { bool Status = false; // recreate font #ifdef WIN32 if (!d->hFont) { Status = Create(Face(), Size()); } #else if (d->Dirty) { Status = Create(Face(), Size()); d->Dirty = false; } #endif else { Status = true; } return Status; } #ifdef WIN32 #define ENCODING_TABLE_SIZE 8 class type_4_cmap { public: uint16 format; uint16 length; uint16 language; uint16 seg_count_x_2; uint16 search_range; uint16 entry_selector; uint16 range_shift; // uint16 reserved; uint16 arrays[1]; uint16 SegCount() { return seg_count_x_2 / 2; } uint16 *GetIdRangeOffset() { return arrays + 1 + (SegCount()*3); } uint16 *GetStartCount() { return arrays + 1 + SegCount(); } uint16 *GetEndCount() { /* Apparently the reseved spot is not reserved for the end_count array!? */ return arrays; } }; class cmap_encoding_subtable { public: uint16 platform_id; uint16 encoding_id; - uint32 offset; + uint32_t offset; }; #define INT16_SWAP(i) ( ((i)>>8) | (((i)&0xff)<<8) ) #define INT32_SWAP(i) ( ( ((i)&0x000000ff) << 24) | \ ( ((i)&0x0000ff00) << 8 ) | \ ( ((i)&0x00ff0000) >> 8 ) | \ ( ((i)&0xff000000) >> 24) ) #define MAKE_TT_TABLE_NAME(c1, c2, c3, c4) \ - (((uint32)c4) << 24 | ((uint32)c3) << 16 | ((uint32)c2) << 8 | ((uint32)c1)) + (((uint32_t)c4) << 24 | ((uint32_t)c3) << 16 | ((uint32_t)c2) << 8 | ((uint32_t)c1)) #define CMAP (MAKE_TT_TABLE_NAME('c','m','a','p')) #define CMAP_HEADER_SIZE 4 #define APPLE_UNICODE_PLATFORM_ID 0 #define MACINTOSH_PLATFORM_ID 1 #define ISO_PLATFORM_ID 2 #define MICROSOFT_PLATFORM_ID 3 #define SYMBOL_ENCODING_ID 0 #define UNICODE_ENCODING_ID 1 #define UCS4_ENCODING_ID 10 -type_4_cmap *GetUnicodeTable(HFONT hFont, uint16 &Length) +type_4_cmap *GetUnicodeTable(HFONT hFont, uint16_t &Length) { bool Status = false; type_4_cmap *Table = 0; HDC hDC = GetDC(0); if (hDC) { HFONT Old = (HFONT)SelectObject(hDC, hFont); - uint16 Tables = 0; - uint32 Offset = 0; + uint16_t Tables = 0; + uint32_t Offset = 0; // Get The number of encoding tables, at offset 2 - int32 Res = GetFontData(hDC, CMAP, 2, &Tables, sizeof(uint16)); - if (Res == sizeof (uint16)) + auto Res = GetFontData(hDC, CMAP, 2, &Tables, sizeof(uint16_t)); + if (Res == sizeof (uint16_t)) { Tables = INT16_SWAP(Tables); cmap_encoding_subtable *Sub = (cmap_encoding_subtable*)malloc(ENCODING_TABLE_SIZE*Tables); if (Sub) { Res = GetFontData(hDC, CMAP, CMAP_HEADER_SIZE, Sub, ENCODING_TABLE_SIZE*Tables); if (Res == ENCODING_TABLE_SIZE*Tables) { for (int i = 0; i < Tables; i++) { Sub[i].platform_id = INT16_SWAP(Sub[i].platform_id); Sub[i].encoding_id = INT16_SWAP(Sub[i].encoding_id); Sub[i].offset = INT32_SWAP(Sub[i].offset); if (Sub[i].platform_id == MICROSOFT_PLATFORM_ID && Sub[i].encoding_id == UNICODE_ENCODING_ID) { Offset = Sub[i].offset; } } } free(Sub); } } if (Offset) { Length = 0; - uint Res = GetFontData(hDC, CMAP, Offset + 2, &Length, sizeof(uint16)); + uint Res = GetFontData(hDC, CMAP, Offset + 2, &Length, sizeof(uint16_t)); if (Res == sizeof(uint16)) { Length = INT16_SWAP(Length); Table = (type_4_cmap*)malloc(Length); Res = GetFontData(hDC, CMAP, Offset, Table, Length); if (Res == Length) { Table->format = INT16_SWAP(Table->format); Table->length = INT16_SWAP(Table->length); Table->language = INT16_SWAP(Table->language); Table->seg_count_x_2 = INT16_SWAP(Table->seg_count_x_2); Table->search_range = INT16_SWAP(Table->search_range); Table->entry_selector = INT16_SWAP(Table->entry_selector); Table->range_shift = INT16_SWAP(Table->range_shift); if (Table->format == 4) { uint16 *tbl_end = (uint16 *)((char *)Table + Length); uint16 *tbl = Table->arrays; while (tbl < tbl_end) { *tbl = INT16_SWAP(*tbl); tbl++; } Status = true; } } } } SelectObject(hDC, Old); ReleaseDC(0, hDC); } if (!Status) { free(Table); Table = 0; } return Table; } #endif GSurface *GFont::GetSurface() { return d->pSurface; } bool GFont::Create(const char *face, GCss::Len size, GSurface *pSurface) { bool FaceChanging = false; bool SizeChanging = false; bool ValidInitFaceSize = ValidStr(Face()) && Size().IsValid(); if (face) { if (!Face() || strcmp(Face(), face) != 0) { FaceChanging = true; } Face(face); } if (size.IsValid()) { SizeChanging = GTypeFace::d->_Size != size; GTypeFace::d->_Size = size; } if ((SizeChanging || FaceChanging) && this == SysFont && ValidInitFaceSize) { LgiTrace("Warning: Changing sysfont definition.\n"); } if (this == SysFont) { printf("Setting sysfont up '%s' %i\n", Face(), PointSize()); } #if LGI_SDL GString FaceName; #if defined(WIN32) const char *Ext = "ttf"; GString FontPath = "c:\\Windows\\Fonts"; #elif defined(LINUX) const char *Ext = "ttf"; GString FontPath = "/usr/share/fonts/truetype"; #elif defined(MAC) const char *Ext = "ttc"; GString FontPath = "/System/Library/Fonts"; #else #error "Put your font path here" #endif GFile::Path p = FontPath.Get(); FaceName.Printf("%s.%s", Face(), Ext); p += FaceName; GString Full = p.GetFull(); if (!FileExists(Full)) { GArray Files; GArray Extensions; GString Pattern; Pattern.Printf("*.%s", Ext); Extensions.Add(Pattern.Get()); LgiRecursiveFileSearch(FontPath, &Extensions, &Files, NULL, NULL, NULL, NULL); char *Match = NULL; for (unsigned i=0; ihFont); if (error) { LgiTrace("%s:%i - FT_New_Face failed with %i\n", _FL, error); } else { int Dpi = LgiScreenDpi(); int PtSize = PointSize(); int PxSize = (int) (PtSize * Dpi / 72.0); error = FT_Set_Char_Size( d->hFont, /* handle to face object */ 0, /* char_width in 1/64th of points */ PtSize*64, /* char_height in 1/64th of points */ Dpi, /* horizontal device resolution */ Dpi); if (error) { LgiTrace("%s:%i - FT_Set_Char_Size failed with %i\n", _FL, error); } d->Height = (int) (ceil((double)d->hFont->height * PxSize / d->hFont->units_per_EM) + 0.0001); GTypeFace::d->_Ascent = (double)d->hFont->ascender * PxSize / d->hFont->units_per_EM; LgiAssert(d->Height > GTypeFace::d->_Ascent); GTypeFace::d->_Descent = d->Height - GTypeFace::d->_Ascent; return true; } #elif WINNATIVE if (d->hFont) { DeleteObject(d->hFont); d->hFont = 0; } d->pSurface = pSurface; HDC hDC = pSurface ? pSurface->Handle() : GetDC(0); auto Sz = Size(); int Win32Height = 0; if (Sz.Type == GCss::LenPt) Win32Height = WinPointToHeight((int)Sz.Value, hDC); else if (Sz.Type == GCss::LenPx) Win32Height = (int)(Sz.Value * 1.2); else LgiAssert(!"What now?"); GTypeFace::d->IsSymbol = GTypeFace::d->_Face && ( stristr(GTypeFace::d->_Face, "wingdings") || stristr(GTypeFace::d->_Face, "symbol") ); int Cs; if (GTypeFace::d->IsSymbol) Cs = SYMBOL_CHARSET; else Cs = ANSI_CHARSET; d->OwnerUnderline = Face() && stricmp(Face(), "Courier New") == 0 && Size().Type == GCss::LenPt && (PointSize() == 8 || PointSize() == 9) && GTypeFace::d->_Underline; GAutoWString wFace(Utf8ToWide(GTypeFace::d->_Face)); d->hFont = ::CreateFont(Win32Height, 0, 0, 0, GTypeFace::d->_Weight, GTypeFace::d->_Italic, d->OwnerUnderline ? false : GTypeFace::d->_Underline, false, Cs, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, GTypeFace::d->_Quality, FF_DONTCARE, wFace); if (d->hFont) { HFONT hFnt = (HFONT) SelectObject(hDC, d->hFont); TEXTMETRIC tm; ZeroObj(tm); if (GetTextMetrics(hDC, &tm)) { d->Height = tm.tmHeight; GTypeFace::d->_Ascent = tm.tmAscent; GTypeFace::d->_Descent = tm.tmDescent; GTypeFace::d->_Leading = tm.tmInternalLeading; } else { SIZE Size = {0, 0}; GetTextExtentPoint32(hDC, L"Ag", 2, &Size); d->Height = Size.cy; } uint Bytes = (MAX_UNICODE + 1) >> 3; if (!d->GlyphMap) { d->GlyphMap = new uchar[Bytes]; } if (d->GlyphMap) { memset(d->GlyphMap, 0, Bytes); GArray OsVer; int OsType = LgiGetOs(&OsVer); if (OsType == LGI_OS_WIN9X || OsVer[0] < 5) // GetFontUnicodeRanges is supported on >= Win2k { bool HideUnihan = false; LgiAssert(sizeof(type_4_cmap)==16); uint16 Length = 0; type_4_cmap *t = GetUnicodeTable(Handle(), Length); if (t) { uint16 SegCount = t->seg_count_x_2 / 2; uint16 *EndCount = t->GetEndCount(); uint16 *StartCount = t->GetStartCount(); uint16 *IdRangeOffset = t->GetIdRangeOffset(); for (int i = 0; i= 0x3400 && u <= 0x9FAF) { // APPROXIMATE } else { // EXACT } if ((u >> 3) < Bytes) { d->GlyphMap[u>>3] |= 1 << (u & 7); } } } else { uint16 *End = (uint16*) (((char*)t)+Length); ssize_t IdBytes = End - IdRangeOffset; for (uint u = StartCount[i]; u <= EndCount[i] && IdRangeOffset[i] != 0xffff; u++) { uint id = *(IdRangeOffset[i]/2 + (u - StartCount[i]) + &IdRangeOffset[i]); if (id) { if (HideUnihan && u >= 0x3400 && u <= 0x9FAF) { // APPROXIMATE } else { // EXACT } if ((u >> 3) < Bytes) { d->GlyphMap[u>>3] |= 1 << (u & 7); } } } } } } else { // not a TTF? assume that it can handle 00-ff in the current ansi cp /* char *Cp = LgiAnsiToLgiCp(); for (int i=0; i<=0x7f; i++) { char16 u; uchar c = i; void *In = &c; int Size = sizeof(c); LgiBufConvertCp(&u, "ucs-2", sizeof(u), In, Cp, Size); if ((u >> 3) < Bytes) { GlyphMap[u>>3] |= 1 << (u & 7); } } */ } } else { typedef DWORD (WINAPI *Proc_GetFontUnicodeRanges)(HDC, LPGLYPHSET); if (!d->Gdi32) d->Gdi32.Reset(new GLibrary("Gdi32")); if (d->Gdi32) { Proc_GetFontUnicodeRanges GetFontUnicodeRanges = (Proc_GetFontUnicodeRanges)d->Gdi32->GetAddress("GetFontUnicodeRanges"); if (GetFontUnicodeRanges) { DWORD BufSize = GetFontUnicodeRanges(hDC, 0); LPGLYPHSET Set = (LPGLYPHSET) new char[BufSize]; if (Set && GetFontUnicodeRanges(hDC, Set) > 0) { for (DWORD i=0; icRanges; i++) { WCRANGE *Range = Set->ranges + i; for (int n=0; ncGlyphs; n++) { DWORD u = Range->wcLow + n; if (u >> 3 < Bytes) { d->GlyphMap[u>>3] |= 1 << (u & 7); } } } } DeleteArray((char*&)Set); } } if (GTypeFace::d->IsSymbol) { // Lies! It's all Lies! Symbol doesn't support non-breaking space. int u = 0xa0; d->GlyphMap[u >> 3] &= ~(1 << (u & 7)); } } if (d->GlyphMap) { memset(d->GlyphMap, 0xff, 128/8); } } SelectObject(hDC, hFnt); } if (!pSurface) ReleaseDC(0, hDC); return (d->hFont != 0); #elif defined BEOS if (Face()) { d->hFont = new BFont; d->hFont->SetSize(PointSize()); int f = 0; if (Bold()) f |= B_BOLD_FACE; if (Italic()) f |= B_ITALIC_FACE; if (Underline()) f |= B_UNDERSCORE_FACE; if (!f) f |= B_REGULAR_FACE; d->hFont->SetFamilyAndFace(Face(), f); font_height h; d->hFont->GetHeight(&h); GTypeFace::d->_Ascent = h.ascent; GTypeFace::d->_Descent = h.descent; d->Height = ceil(h.ascent + h.descent); // Create glyph size cache of the first 128 characters. StringWidth is too slow // to do for every string at runtime. d->CharX[0] = 0; for (int i=1; i<128; i++) { char str[] = {i, 0}; d->CharX[i] = (int)d->hFont->StringWidth(str, 1); } } return true; #elif defined __GTK_H__ Destroy(); d->hFont = Gtk::pango_font_description_new(); if (!d->hFont) printf("%s:%i - pango_font_description_new failed: Face='%s' Size=%i Bold=%i Italic=%i\n", _FL, Face(), PointSize(), Bold(), Italic()); else if (!ValidStr(Face())) printf("%s:%i - No font face.\n", _FL); else if (!Size().IsValid()) printf("%s:%i - Invalid size.\n", _FL); else { auto Sz = Size(); Gtk::pango_font_description_set_family(d->hFont, Face()); if (Sz.Type == GCss::LenPt) Gtk::pango_font_description_set_size(d->hFont, Sz.Value * PANGO_SCALE); else if (Sz.Type == GCss::LenPx) Gtk::pango_font_description_set_absolute_size(d->hFont, Sz.Value * PANGO_SCALE); else { LgiAssert(0); return false; } if (Bold()) Gtk::pango_font_description_set_weight(d->hFont, Gtk::PANGO_WEIGHT_BOLD); // printf("Creating pango font %s, %i\n", Face(), PointSize()); // Get metrics for this font... Gtk::GtkPrintContext *PrintCtx = pSurface ? pSurface->GetPrintContext() : NULL; Gtk::PangoContext *SysCtx = GFontSystem::Inst()->GetContext(); if (PrintCtx) d->PangoCtx = gtk_print_context_create_pango_context(PrintCtx); Gtk::PangoFontMetrics *m = Gtk::pango_context_get_metrics(d->PangoCtx ? d->PangoCtx : SysCtx, d->hFont, 0); if (!m) printf("pango_font_get_metrics failed.\n"); else { GTypeFace::d->_Ascent = (double)Gtk::pango_font_metrics_get_ascent(m) / PANGO_SCALE; GTypeFace::d->_Descent = (double)Gtk::pango_font_metrics_get_descent(m) / PANGO_SCALE; d->Height = ceil(GTypeFace::d->_Ascent + GTypeFace::d->_Descent + 1/*hack the underscores to work*/); // printf("Created '%s:%i' %f + %f = %i\n", Face(), PointSize(), GTypeFace::d->_Ascent, GTypeFace::d->_Descent, d->Height); #if 1 if (PrintCtx) { LgiTrace("GFont::Create %s,%f (%i,%i,%i) (%.1f,%.1f,%i)\n", Gtk::pango_font_description_get_family(d->hFont), (double)Gtk::pango_font_description_get_size(d->hFont) / PANGO_SCALE, Gtk::pango_font_metrics_get_ascent(m), Gtk::pango_font_metrics_get_descent(m), PANGO_SCALE, GTypeFace::d->_Ascent, GTypeFace::d->_Descent, d->Height); } #endif Gtk::pango_font_metrics_unref(m); return true; } } #elif defined MAC Destroy(); // OSStatus e; if (this == SysFont) LgiTrace("%s:%i - WARNING: you are re-creating the system font... this is bad!!!!\n", _FL); #if USE_CORETEXT if (Face()) { if (d->Attributes) CFRelease(d->Attributes); auto Sz = Size(); GString sFamily(Face()); GString sBold("Bold"); int keys = 1; CFStringRef key[5] = { kCTFontFamilyNameAttribute }; CFTypeRef values[5] = { sFamily.CreateStringRef() }; if (!values[0]) return false; if (Bold()) { key[keys] = kCTFontStyleNameAttribute; values[keys++] = sBold.CreateStringRef(); } CFDictionaryRef FontAttrD = CFDictionaryCreate( kCFAllocatorDefault, (const void**)&key, (const void**)&values, keys, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (FontAttrD) { CTFontDescriptorRef descriptor = CTFontDescriptorCreateWithAttributes(FontAttrD); if (descriptor) { float PtSz = 0.0; if (Sz.Type == GCss::LenPt) PtSz = Sz.Value; else if (Sz.Type == GCss::LenPx) PtSz = Sz.Value * 72.0f / LgiScreenDpi(); else LgiAssert(!"Impl me."); d->hFont = CTFontCreateWithFontDescriptor(descriptor, PtSz, NULL); CFRelease(descriptor); } else LgiAssert(0); CFRelease(FontAttrD); } else LgiAssert(0); if (d->hFont) { GTypeFace::d->_Ascent = CTFontGetAscent(d->hFont); GTypeFace::d->_Descent = CTFontGetDescent(d->hFont); GTypeFace::d->_Leading = CTFontGetLeading(d->hFont); d->Height = ceil(GTypeFace::d->_Ascent + GTypeFace::d->_Descent + GTypeFace::d->_Leading); int keys = 2; CFStringRef key[5] = { kCTFontAttributeName, kCTForegroundColorFromContextAttributeName }; CFTypeRef values[5] = { d->hFont, kCFBooleanTrue }; if (Underline()) { key[keys] = kCTUnderlineStyleAttributeName; CTUnderlineStyle u = kCTUnderlineStyleSingle; values[keys++] = CFNumberCreate(NULL, kCFNumberSInt32Type, &u); } d->Attributes = CFDictionaryCreate( kCFAllocatorDefault, (const void**)&key, (const void**)&values, keys, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); return true; } for (int i=0; ihFont))) { // Lookup ID if (!Face()) { LgiAssert(!"No font face"); return false; } char *sFace = Face(); CFStringRef fontName = CFStringCreateWithBytes( kCFAllocatorDefault, (UInt8*)sFace, strlen(sFace), kCFStringEncodingUTF8, false); if (!fontName) return false; ATSFontRef atsFont = ATSFontFindFromName(fontName, kATSOptionFlagsDefault); CFRelease(fontName); ATSUFontID font = (ATSUFontID)atsFont; if (e) printf("%s:%i - Error getting font id (e=%i)\n", _FL, (int)e); else { Fixed Size; Size = PointSize() << 16; Boolean IsBold = Bold(); Boolean IsItalic = Italic(); Boolean IsUnder = Underline(); // Set style attr ATSUAttributeTag Tags[] = {kATSUFontTag, kATSUSizeTag, kATSUQDItalicTag, kATSUQDUnderlineTag, kATSUQDBoldfaceTag}; ATSUAttributeValuePtr Values[] = {&font, &Size, &IsItalic, &IsUnder, &IsBold}; ByteCount Lengths[] = {sizeof(font), sizeof(Size), sizeof(IsItalic), sizeof(IsUnder), sizeof(IsBold)}; if (!(e = ATSUSetAttributes(d->hFont, CountOf(Tags), Tags, Lengths, Values))) { GDisplayString ds(this, "A"); d->Height = ds.Y(); return true; } } } else { printf("%s:%i - Error creating font (e=%i)\n", _FL, (int)e); } #endif #endif return false; } char16 *GFont::_ToUnicode(char *In, ssize_t &Len) { char16 *WStr = 0; if (In && Len > 0) { WStr = (char16*)LgiNewConvertCp(LGI_WideCharset, In, GTypeFace::d->_CodePage, Len); if (WStr) { ssize_t l = StrlenW(WStr); if (l < Len) { Len = l; } } } return WStr; } #if defined WINNATIVE bool GFont::Create(GFontType *LogFont, GSurface *pSurface) { if (d->hFont) { DeleteObject(d->hFont); d->hFont = 0; } if (LogFont) { // set props PointSize(WinHeightToPoint(LogFont->Info.lfHeight)); GString uFace = LogFont->Info.lfFaceName; if (ValidStr(uFace)) { Face(uFace); Quality(LogFont->Info.lfQuality); Bold(LogFont->Info.lfWeight >= FW_BOLD); Italic(LogFont->Info.lfItalic != FALSE); Underline(LogFont->Info.lfUnderline != FALSE); // create the handle Create(0, 0, pSurface); } } return (d->hFont != 0); } #elif defined BEOS bool GFont::Create(GFontType *LogFont, GSurface *pSurface) { bool Status = false; if (LogFont) { Face(LogFont->Info.Face()); PointSize(LogFont->Info.PointSize()); Italic(LogFont->Info.Italic()); Underline(LogFont->Info.Underline()); Bold(LogFont->Info.Bold()); Status = true; } return Status; } #else bool GFont::Create(GFontType *LogFont, GSurface *pSurface) { if (LogFont) { GCss::Len Sz(GCss::LenPt, (float)LogFont->GetPointSize()); return Create(LogFont->GetFace(), Sz, pSurface); } return false; } #endif GFont &GFont::operator =(GFont &f) { Face(f.Face()); Size(f.Size()); TabSize(f.TabSize()); Quality(f.Quality()); Fore(f.Fore()); Back(f.Back()); SetWeight(f.GetWeight()); Italic(f.Italic()); Underline(f.Underline()); Transparent(f.Transparent()); return *this; } /////////////////////////////////////////////////////////////////////// GFontType::GFontType(const char *face, int pointsize) { #if defined WINNATIVE ZeroObj(Info); if (face) { swprintf_s(Info.lfFaceName, CountOf(Info.lfFaceName), L"%S", face); } if (pointsize) { Info.lfHeight = WinHeightToPoint(pointsize); } #else if (face) Info.Face(face); if (pointsize) Info.PointSize(pointsize); #endif } GFontType::~GFontType() { } char *GFontType::GetFace() { #ifdef WINNATIVE Buf = Info.lfFaceName; return Buf; #else return Info.Face(); #endif } void GFontType::SetFace(const char *Face) { #ifdef WINNATIVE if (Face) { swprintf_s(Info.lfFaceName, CountOf(Info.lfFaceName), L"%S", Face); } else { Info.lfFaceName[0] = 0; } #else Info.Face(Face); #endif } int GFontType::GetPointSize() { #ifdef WINNATIVE return WinHeightToPoint(Info.lfHeight); #else return Info.PointSize(); #endif } void GFontType::SetPointSize(int PointSize) { #ifdef WINNATIVE Info.lfHeight = WinPointToHeight(PointSize); #else Info.PointSize(PointSize); #endif } bool GFontType::DoUI(GView *Parent) { bool Status = false; #if defined WIN32 void *i = &Info; int bytes = sizeof(Info); #else char i[128]; int bytes = sprintf_s(i, sizeof(i), "%s,%i", Info.Face(), Info.PointSize()); #endif GFontSelect Dlg(Parent, i, bytes); if (Dlg.DoModal() == IDOK) { #ifdef WIN32 Dlg.Serialize(i, sizeof(Info), true); #else if (Dlg.Face) Info.Face(Dlg.Face); Info.PointSize(Dlg.Size); #endif Status = true; } return Status; } bool GFontType::GetDescription(char *Str, int SLen) { if (Str && GetFace()) { sprintf_s(Str, SLen, "%s, %i pt", GetFace(), GetPointSize()); return true; } return false; } /* bool GFontType::Serialize(ObjProperties *Options, char *OptName, bool Write) { bool Status = false; if (Options && OptName) { #if defined WIN32 if (Write) { Status = Options->Set(OptName, &Info, sizeof(Info)); } else { void *Data = 0; int Len = 0; if (Options->Get(OptName, Data, Len) && Len == sizeof(Info) && Data) { memcpy(&Info, Data, Len); Status = true; } } #else if (Write) { char Temp[128]; sprintf_s(Temp, sizeof(Temp), "%s,%i pt", Info.Face(), Info.PointSize()); Status = Options->Set(OptName, Temp); } else { char *Temp = 0; if (Options->Get(OptName, Temp) && ValidStr(Temp)) { char *OurCopy = NewStr(Temp); if (OurCopy) { char *Comma = strchr(OurCopy, ','); if (Comma) { *Comma++ = 0; Info.Face(OurCopy); Info.PointSize(atoi(Comma)); Status = true; } DeleteArray(OurCopy); } } } #endif } return Status; } */ bool GFontType::Serialize(GDom *Options, const char *OptName, bool Write) { bool Status = false; if (Options && OptName) { GVariant v; #if defined WINNATIVE if (Write) { v.SetBinary(sizeof(Info), &Info); Status = Options->SetValue(OptName, v); } else { if (Options->GetValue(OptName, v)) { if (v.Type == GV_BINARY && v.Value.Binary.Length == sizeof(Info)) { memcpy(&Info, v.Value.Binary.Data, sizeof(Info)); Status = ValidStrW(Info.lfFaceName); } } } #else if (Write) { char Temp[128]; sprintf_s(Temp, sizeof(Temp), "%s,%i pt", Info.Face(), Info.PointSize()); Status = Options->SetValue(OptName, v = Temp); } else { if (Options->GetValue(OptName, v) && ValidStr(v.Str())) { char *Comma = strchr(v.Str(), ','); if (Comma) { *Comma++ = 0; int PtSize = atoi(Comma); Info.Face(v.Str()); Info.PointSize(PtSize); // printf("FontTypeSer getting '%s' = '%s' pt %i\n", OptName, v.Str(), PtSize); Status = true; } } } #endif } return Status; } bool GFontType::GetConfigFont(const char *Tag) { bool Status = false; GXmlTag *Font = LgiApp->GetConfig(Tag); if (Font) { // read from config file char *f, *p; if ((f = Font->GetAttr("Face")) && (p = Font->GetAttr("Point"))) { SetFace(f); SetPointSize(atoi(p)); Status = true; } } return Status; } class GFontTypeCache { char DefFace[64]; int DefSize; char Face[64]; int Size; public: GFontTypeCache(char *defface, int defsize) { ZeroObj(Face); Size = -1; strcpy_s(DefFace, sizeof(DefFace), defface); DefSize = defsize; } char *GetFace(char *Type = 0) { #ifdef LINUX if (!IsInit()) { char f[256]; int s; if (_GetSystemFont(Type, f, sizeof(f), s)) { strcpy_s(Face, sizeof(Face), f); Size = s; } else { Face[0] = 0; Size = 0; } } #endif return ValidStr(Face) ? Face : DefFace; } int GetSize() { return Size > 0 ? Size : DefSize; } bool IsInit() { return Size >= 0; } }; #ifdef MAC bool MacGetSystemFont(GTypeFace &Info, CTFontUIFontType Which) { CTFontRef ref = CTFontCreateUIFontForLanguage(Which, 0.0, NULL); if (!ref) return false; bool Status = false; CFStringRef name = CTFontCopyFamilyName(ref); if (name) { CGFloat sz = CTFontGetSize(ref); GString face(name); Info.Face(face); Info.PointSize((int)sz + MAC_FONT_SIZE_OFFSET); CFRelease(name); Status = true; } CFRelease(ref); return Status; } #endif bool GFontType::GetSystemFont(const char *Which) { bool Status = false; if (!Which) { LgiAssert(!"No param supplied."); return false; } #if LGI_SDL #elif defined WINNATIVE // Get the system settings NONCLIENTMETRICS info; info.cbSize = sizeof(info); #if (WINVER >= 0x0600) GArray Ver; if (LgiGetOs(&Ver) == LGI_OS_WIN32 && Ver[0] <= 5) info.cbSize -= 4; #endif BOOL InfoOk = SystemParametersInfo( SPI_GETNONCLIENTMETRICS, info.cbSize, &info, 0); if (!InfoOk) { LgiTrace("%s:%i - SystemParametersInfo failed with 0x%x (info.cbSize=%i, os=%i, %i)\n", _FL, GetLastError(), info.cbSize, LgiGetOs(), LGI_OS_WIN9X); } // Convert windows font height into points int Height = WinHeightToPoint(info.lfMessageFont.lfHeight); #elif defined __GTK_H__ // Define some defaults.. in case the system settings aren't there static char DefFont[64] = #ifdef __CYGWIN__ "Luxi Sans"; #else "Sans"; #endif int DefSize = 10; int Offset = 0; static bool First = true; if (First) { bool ConfigFontUsed = false; char p[MAX_PATH]; LGetSystemPath(LSP_HOME, p, sizeof(p)); LgiMakePath(p, sizeof(p), p, ".lgi.conf"); if (FileExists(p)) { GAutoString a(ReadTextFile(p)); if (a) { GString s; s = a.Get(); GString::Array Lines = s.Split("\n"); for (int i=0; i 1) { strcpy_s(DefFont, sizeof(DefFont), d[0]); int PtSize = d[1].Int(); if (PtSize > 0) { DefSize = PtSize; ConfigFontUsed = true; printf("Config font %s : %i\n", DefFont, DefSize); } } } } } } else printf("Can't read '%s'\n", p); } if (!ConfigFontUsed) { Gtk::GtkStyle *s = Gtk::gtk_style_new(); if (s) { const char *fam = Gtk::pango_font_description_get_family(s->font_desc); if (fam) { strcpy_s(DefFont, sizeof(DefFont), fam); } else printf("%s:%i - pango_font_description_get_family failed.\n", _FL); if (Gtk::pango_font_description_get_size_is_absolute(s->font_desc)) { float Px = Gtk::pango_font_description_get_size(s->font_desc) / PANGO_SCALE; float Dpi = (float)LgiScreenDpi(); DefSize = (Px * 72.0) / Dpi; printf("pango px=%f, Dpi=%f\n", Px, Dpi); } else { DefSize = Gtk::pango_font_description_get_size(s->font_desc) / PANGO_SCALE; } g_object_unref(s); } else printf("%s:%i - gtk_style_new failed.\n", _FL); } First = false; } #endif if (!_stricmp(Which, "System")) { Status = GetConfigFont("Font-System"); if (!Status) { // read from system #if LGI_SDL #if defined(WIN32) Info.Face("Tahoma"); Info.PointSize(11); Status = true; #elif defined(MAC) Info.Face("LucidaGrande"); Info.PointSize(11); Status = true; #elif defined(LINUX) Info.Face("Sans"); Info.PointSize(11); Status = true; #else #error fix me #endif #elif defined WINNATIVE if (InfoOk) { // Copy the font metrics memcpy(&Info, &info.lfMessageFont, sizeof(Info)); Status = true; } else LgiTrace("%s:%i - Info not ok.\n", _FL); #elif defined BEOS // BeOS has no system wide setting so give a valid default Info.Face("Swis721 BT"); Info.PointSize(11); Status = true; #elif defined __GTK_H__ Info.Face(DefFont); Info.PointSize(DefSize); Status = true; #elif defined MAC #if USE_CORETEXT Status = MacGetSystemFont(Info, kCTFontUIFontControlContent); #else Str255 Name; SInt16 Size; Style St; OSStatus e = GetThemeFont( kThemeSmallSystemFont, smSystemScript, Name, &Size, &St); if (e) printf("%s:%i - GetThemeFont failed with %i\n", __FILE__, __LINE__, (int)e); else { Info.Face(p2c(Name)); Info.PointSize(Size); Status = true; // printf("System=%s,%i\n", Info.Face(), Size); } #endif #endif } } else if (!stricmp(Which, "Menu")) { Status = GetConfigFont("Font-Menu"); if (!Status) { #if LGI_SDL LgiAssert(!"Impl me."); #elif defined WINNATIVE if (InfoOk) { // Copy the font metrics memcpy(&Info, &info.lfMenuFont, sizeof(Info)); Status = true; } #elif defined BEOS // BeOS has no system wide setting so give a valid default Info.Face("Swis721 BT"); Info.PointSize(11); Status = true; #elif defined __GTK_H__ Info.Face(DefFont); Info.PointSize(DefSize); Status = true; #elif defined MAC && !defined COCOA #if USE_CORETEXT Status = MacGetSystemFont(Info, kCTFontUIFontMenuItem); #else Str255 Name; SInt16 Size; Style St; OSStatus e = GetThemeFont( kThemeMenuItemFont, smSystemScript, Name, &Size, &St); if (e) printf("%s:%i - GetThemeFont failed with %i\n", __FILE__, __LINE__, (int)e); else { Info.Face(p2c(Name)); Info.PointSize(Size); Status = true; } #endif #endif } } else if (!stricmp(Which, "Caption")) { Status = GetConfigFont("Font-Caption"); if (!Status) { #if LGI_SDL LgiAssert(!"Impl me."); #elif defined WINNATIVE if (InfoOk) { // Copy the font metrics memcpy(&Info, &info.lfCaptionFont, sizeof(Info)); Status = true; } #elif defined BEOS // BeOS has no system wide setting so give a valid default Info.Face("Swis721 BT"); Info.PointSize(11); Status = true; #elif defined LINUX #elif defined __GTK_H__ Info.Face(DefFont); Info.PointSize(DefSize-1); Status = true; #elif defined MAC && !defined COCOA #if USE_CORETEXT Status = MacGetSystemFont(Info, kCTFontUIFontToolbar); #else Str255 Name; SInt16 Size; Style St; OSStatus e = GetThemeFont( kThemeToolbarFont, smSystemScript, Name, &Size, &St); if (e) printf("%s:%i - GetThemeFont failed with %i\n", __FILE__, __LINE__, (int)e); else { Info.Face(p2c(Name)); Info.PointSize(Size); Status = true; } #endif #endif } } else if (!stricmp(Which, "Status")) { Status = GetConfigFont("Font-Status"); if (!Status) { #if LGI_SDL LgiAssert(!"Impl me."); #elif defined WINNATIVE if (InfoOk) { // Copy the font metrics memcpy(&Info, &info.lfStatusFont, sizeof(Info)); Status = true; } #elif defined BEOS // BeOS has no system wide setting so give a valid default Info.Face("Swis721 BT"); Info.PointSize(11); Status = true; #elif defined __GTK_H__ Info.Face(DefFont); Info.PointSize(DefSize); Status = true; #elif defined MAC && !defined COCOA #if USE_CORETEXT Status = MacGetSystemFont(Info, kCTFontUIFontSystemDetail); #else Str255 Name; SInt16 Size; Style St; OSStatus e = GetThemeFont( kThemeToolbarFont, smSystemScript, Name, &Size, &St); if (e) printf("%s:%i - GetThemeFont failed with %i\n", __FILE__, __LINE__, (int)e); else { Info.Face(p2c(Name)); Info.PointSize(Size); Status = true; } #endif #endif } } else if (!stricmp(Which, "Small")) { Status = GetConfigFont("Font-Small"); if (!Status) { #if LGI_SDL LgiAssert(!"Impl me."); #elif defined WINNATIVE if (InfoOk) { // Copy the font metrics memcpy(&Info, &info.lfSmCaptionFont, sizeof(Info)); if (LgiGetOs() == LGI_OS_WIN9X && _wcsicmp(Info.lfFaceName, L"ms sans serif") == 0) { SetFace("Arial"); } // Make it a bit smaller than the system font Info.lfHeight = WinPointToHeight(WinHeightToPoint(info.lfMessageFont.lfHeight)-1); Info.lfWeight = FW_NORMAL; Status = true; } #elif defined BEOS // BeOS has no system wide setting so give a valid default Info.Face("Swis721 BT"); Info.PointSize(9); Status = true; #elif defined __GTK_H__ Info.Face(DefFont); Info.PointSize(DefSize-1); Status = true; #elif defined MAC && !defined COCOA #if USE_CORETEXT Status = MacGetSystemFont(Info, kCTFontUIFontSmallSystem); #else Str255 Name; SInt16 Size; Style St; OSStatus e = GetThemeFont( kThemeSmallSystemFont, smSystemScript, Name, &Size, &St); if (e) printf("%s:%i - GetThemeFont failed with %i\n", __FILE__, __LINE__, (int)e); else { Info.Face(p2c(Name)); Info.PointSize(Size - 2); Status = true; } #endif #endif } } else if (!stricmp(Which, "Fixed")) { Status = GetConfigFont("Font-Fixed"); if (!Status) { #if LGI_SDL LgiAssert(!"Impl me."); #elif defined WINNATIVE // SetFace("Courier New"); SetFace("Consolas"); Info.lfHeight = WinPointToHeight(10); Status = true; #elif defined BEOS Info.Face("Courier10 BT"); Info.PointSize(12); Status = true; #elif defined LINUX Info.Face("Courier New"); Info.PointSize(DefSize); Status = true; #elif defined MAC Status = MacGetSystemFont(Info, kCTFontUIFontUserFixedPitch); #endif } } else { LgiAssert(!"Invalid param supplied."); } // printf("GetSystemFont(%s)=%i %s,%i\n", Which, Status, Info.Face(), Info.PointSize()); return Status; } bool GFontType::GetFromRef(OsFont Handle) { #if defined WIN32 return GetObject(Handle, sizeof(Info), &Info) == sizeof(Info); #else // FIXME return false; #endif } GFont *GFontType::Create(GSurface *pSurface) { GFont *New = new GFont; if (New) { if (!New->Create(this, pSurface)) { DeleteObj(New); } if (New) LgiAssert(New->GetHeight() > 0); } return New; } char16 WinSymbolToUnicode[256] = { /* 0 to 15 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 to 31 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 32 to 47 */ 32, 9998, 9986, 9985, 0, 0, 0, 0, 9742, 9990, 9993, 9993, 0, 0, 0, 0, /* 48 to 63 */ 0, 0, 0, 0, 0, 0, 8987, 9000, 0, 0, 0, 0, 0, 0, 9991, 9997, /* 64 to 79 */ 9997, 9996, 0, 0, 0, 9756, 9758, 9757, 9759, 0, 9786, 9786, 9785, 0, 9760, 0, /* 80 to 95 */ 0, 9992, 9788, 0, 10052, 10014, 10014, 10013, 10016, 10017, 9770, 9775, 2384, 9784, 9800, 9801, /* 96 to 111 */ 9802, 9803, 9804, 9805, 9806, 9807, 9808, 9809, 9810, 9811, 38, 38, 9679, 10061, 9632, 9633, /* 112 to 127 */ 9633, 10065, 10066, 9674, 9674, 9670, 10070, 9670, 8999, 9043, 8984, 10048, 10047, 10077, 10078, 0, /* 128 to 143 */ 9450, 9312, 9313, 9314, 9315, 9316, 9317, 9318, 9319, 9320, 9321, 0, 10102, 10103, 10104, 10105, /* 144 to 159 */ 10106, 10107, 10108, 10109, 10110, 10111, 10087, 9753, 9753, 10087, 10087, 9753, 9753, 10087, 8226, 9679, /* 160 to 175 */ 160, 9675, 9675, 9675, 9737, 9737, 10061, 9642, 9633, 0, 10022, 9733, 10038, 10039, 10040, 10037, /* 176 to 191 */ 0, 0, 10023, 0, 65533, 10026, 10032, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 192 to 207 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10086, 10086, 10086, /* 208 to 223 */ 10086, 10086, 10086, 10086, 10086, 9003, 8998, 0, 10146, 0, 0, 0, 10162, 0, 0, 0, /* 224 to 239 */ 0, 0, 0, 0, 0, 0, 0, 0, 10132, 0, 0, 0, 0, 0, 0, 8678, /* 240 to 255 */ 8680, 8679, 8681, 8660, 8661, 8662, 8663, 8665, 8664, 0, 0, 10007, 10003, 9746, 9745, 0, }; GAutoString GFont::ConvertToUnicode(char16 *Input, ssize_t Len) { GAutoString a; if (GTypeFace::d->IsSymbol) { // F***ing wingdings. if (Input) { GStringPipe p(256); if (Len < 0) Len = StrlenW(Input); char16 *c = Input, *e = Input + Len; while (c < e) { if (*c < 256 && WinSymbolToUnicode[*c]) { p.Write(WinSymbolToUnicode + *c, sizeof(char16)); } else { p.Write(c, sizeof(char16)); } c++; } GAutoWString w(p.NewStrW()); a.Reset(WideToUtf8(w)); } } else { // Normal utf-8 text... a.Reset(WideToUtf8(Input, Len)); } return a; } diff --git a/src/common/Gdc2/Font/GFontCodePages.cpp b/src/common/Gdc2/Font/GFontCodePages.cpp --- a/src/common/Gdc2/Font/GFontCodePages.cpp +++ b/src/common/Gdc2/Font/GFontCodePages.cpp @@ -1,1633 +1,1633 @@ #include #include #include #include "Lgi.h" #include "GToken.h" #include "GFont.h" struct UnicodeMappings { int Unicode; char Ascii; } MissingMaps[] = { {0x2019, '\''}, {0x201C, '\"'}, {0x201D, '\"'}, {0, 0} }; -typedef uint32 iso2022jp_block[16]; +typedef uint32_t iso2022jp_block[16]; iso2022jp_block *iso2022jp_map[128]; iso2022jp_block iso2022jp_blocks[] = { {0,0,0x10000000,0,0,0x53118c,0x800000,0x800000,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0xfffe0000,0xfffe03fb,0x3fb,0}, {0xffff0002,0xffffffff,0x2ffff,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0x33510000,0x80d0063,0,0,0,0,0,0,0x8,0x800,0,0,0xf0000,0,0x140000,0}, {0x6404098d,0x20301f81,0x40000,0xcc3,0xcc,0x20,0,0,0x40000,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0x3999900f,0x99999939,0x804,0,0,0x300c0003,0xc8c0,0x8000}, {0x60,0,0x5,0xa400,0,0,0,0,0,0,0,0,0,0,0,0}, {0x103fffef,0,0xfffffffe,0xffffffff,0x780fffff,0xfffffffe,0xffffffff,0x787fffff,0,0,0,0,0,0,0,0}, {0x43f36f8b,0x9b462442,0xe3e0e82c,0x400a0004,0xdb365f65,0x4497977,0xe3f0ecd7,0x8c56038,0x3403e602,0x35518000,0x7eabe0c8,0x98698200,0x2942a948,0x8060e803,0xad93441c,0x4568c03a}, {0x8656aa60,0x2403f7a,0x14618388,0x21741020,0x7022021,0x40bc3000,0x4462a624,0xa2060a8,0x85740217,0x9c840402,0x14157bfb,0x11e27f24,0x2efb665,0x20ff1f75,0x38403a70,0x676326c3}, {0x20924dd9,0xfc946b0,0x4850bc98,0xa03f8638,0x88162388,0x52323e09,0xe3a422aa,0xc72c00dd,0x26e1a166,0x8f0a840b,0x559e27eb,0x89bbc241,0x85400014,0x8496361,0x8ad07f0c,0x5cfff3e}, {0xa803ff1a,0x7b407a41,0x80024745,0x38eb0500,0x5d851,0x710c9934,0x1000397,0x24046366,0x5180d0,0x430ac000,0x30c89071,0x58000008,0xf7000e99,0x415f80,0x941000b0,0x62800018}, {0x9d00240,0x1568200,0x8015004,0x5101d10,0x1084c1,0x10504025,0x4d8a410f,0xa60d4009,0x914cab19,0x98121c0,0x3c485,0x80000652,0x80b04,0x9041d,0x905c4849,0x16900009}, {0x22200c65,0x24338412,0x47960c03,0x42250a04,0x90880028,0x4f084900,0xd3aa14a2,0x3e87d830,0x1f618604,0x41867ea4,0x5b3c390,0x211857a5,0x2a48241e,0x4a041128,0x161b0a40,0x88400d60}, {0x9502020a,0x10608221,0x4000243,0x80001444,0xc040000,0x70000000,0xc11a06,0xc00024a,0x401a00,0x40451404,0xbdb30029,0x52b0a78,0xbfa0bba9,0x8379407c,0xe81d12fc,0xc5694bf6}, {0x44aeff6,0xff022115,0x402bed63,0x242d033,0x131000,0x59ca1b02,0x20000a0,0x2c41a703,0x8ff24880,0x204,0x10055800,0x489200,0x20011894,0x34805004,0x684c3200,0x68be49ea}, {0x2e42184c,0x21c9a820,0x80b050b9,0xff7c001e,0x14e0849a,0x1e028c1,0xac49870e,0xdddb130f,0x89fbbe1a,0x51a2a2e0,0x32ca5502,0x928b3e46,0x438f1dbf,0x32186703,0x33c03028,0xa9230811}, {0x3a65c000,0x4028fe3,0x86252c4e,0xa1bf3d,0x8cd43a1a,0x317c06c9,0x950a00e0,0xedb018b,0x8c20e34b,0xf0101182,0xa7287d94,0x40fbc9ac,0x6534484,0x44445a90,0x13fc8,0xf5d40048}, {0xec577701,0x891dc442,0x49286b83,0xd2424109,0x59fe061d,0x3a221800,0x3b9fb7e4,0xc0eaf003,0x82021386,0xe4008980,0x10a1b200,0xcc44b80,0x8944d309,0x48341faf,0xc458259,0x450420a}, {0x10c8a040,0x44503140,0x1004004,0x5408280,0x442c0108,0x1a056a30,0x51420a6,0x645690cf,0x31000021,0xcbf09c18,0x63e2a120,0x1b5104c,0x9a83538c,0x3281b8b2,0xa84987a,0xc0233e7}, {0x9018d4cc,0x9070a1a1,0xe0048a1e,0x451c3d4,0x21c2439a,0x53104844,0x36400292,0xf3bd0241,0xe8f0ab09,0xa5d27dc0,0xd24bc242,0xd0afa43f,0x34a11aa0,0x3d88247,0x651bc452,0xc83ad294}, {0x40c8001c,0x33140e06,0xb21b614f,0xc0d00088,0xa898a02a,0x166ba1c5,0x85b42e50,0x604c08b,0x1e04f933,0xa251056e,0x76380400,0x73b8ec07,0x18324406,0xc8164081,0x63097c8a,0xaa042980}, {0xca9c1c24,0x27604e0e,0x83000990,0x81040046,0x10816011,0x908540d,0xcc0a000e,0xc000500,0xa0440430,0x6784008b,0x8a195288,0x8b18865e,0x41602e59,0x9cbe8c10,0x891c6861,0x89800}, {0x89a8100,0x41900018,0xe4a14007,0x640d0505,0xe4d310e,0xff0a4806,0x2aa81632,0xb852e,0xca841800,0x696c0e20,0x16000032,0x3905658,0x1a285120,0x11248000,0x432618e1,0xeaa5d52}, {0xae280fa0,0x4500fa7b,0x89406408,0xc044c880,0xb1419005,0x24c48424,0x603a1a34,0xc1949000,0x3a8246,0xc106180d,0x99100022,0x1511e050,0x824057,0x20a041a,0x8930004f,0x444ad813}, {0xed228a02,0x400510c0,0x1021000,0x31018808,0x2044600,0x708f000,0xa2008900,0x22020000,0x16100200,0x10400042,0x2605200,0x200052f4,0x82308510,0x42021100,0x80b54308,0x9a2070e1}, {0x8012040,0xfc653500,0xab0419c1,0x62140286,0x440087,0x2449085,0xa85405c,0x33803207,0xb8c00400,0xc0d0ce20,0x80c030,0xd250508,0x400a90,0x80c0200,0x40006505,0x41026421}, {0x268,0x847c0024,0xde200002,0x40498619,0x40000808,0x20010084,0x10108400,0x1c742cd,0xd52a7038,0x1d8f1968,0x3e12be50,0x81d92ef5,0x2412cec4,0x732e0828,0x4b3424ac,0xd41d020c}, {0x80002a02,0x8110097,0x114411c4,0x7d451786,0x64949d9,0x87914000,0xd8c4254c,0x491444ba,0xc8001b92,0x15800271,0xc000081,0xc200096a,0x40024800,0xba493021,0x1c802080,0x1008e2ac}, {0x341004,0x841400e1,0x20000020,0x10149800,0x4aa70c2,0x54208688,0x4130c62,0x20109180,0x2064082,0x54001c40,0xe4e90383,0x84802125,0x2000e433,0xe60944c0,0x81260a03,0x80112da}, {0x97906901,0xf8864001,0x81e24d,0xa6510a0e,0x81ec011a,0x8441c600,0xb62cadb8,0x8741a46f,0x4b028d54,0x2681161,0x2057bb60,0x43350a0,0xb7b4a8c0,0x1122402,0x20009ad3,0xc82271}, {0x809e2081,0xe1800c8a,0x8151b009,0x40281031,0x89a52a0e,0x620e69b6,0xd1444425,0x4d548085,0x1fb12c75,0x862dd807,0x4841d87c,0x226e414e,0x9e088200,0xed37f80c,0x75268c80,0x8149313}, {0xc8040e32,0x6ea6484e,0x66702c4a,0xba0126c0,0x185dd30c,0,0,0,0,0x5400000,0x81337020,0x3a54f81,0x641055ec,0x2344c318,0x341462,0x1a090a43}, {0x13a5187b,0xa8480102,0xc5440440,0xe2dd8106,0x2d481af0,0x416b626,0x6e405058,0x31128032,0xc0007e4,0x420a8208,0x803b4840,0x87134860,0x3428850d,0xe5290319,0x870a2345,0x5c1825a9}, {0xd9c577a6,0x3e85e00,0xa7000081,0x41c6cd54,0xa2042800,0x2b0ab860,0xda9e0020,0xe1a08ea,0x11c0427c,0x3768908,0x1058621,0x18a80000,0xc44846a0,0x20220d05,0x91485422,0x28978a01}, {0x87898,0x31221605,0x8804240,0x6a2fa4e,0x92110814,0x9b042002,0x6432e52,0x90105000,0x85ba0041,0x20203042,0x5a04f0b,0x40802708,0x1a930591,0x600df50,0x3021a202,0x4e800630}, {0x4c80cc4,0x8001a004,0xd4316000,0xa020880,0x281c00,0x418e18,0xca106ad0,0x4b00f210,0x1506274d,0x88900220,0x82a85a00,0x81504549,0x80002004,0x2c088804,0x508d1,0x4ac48001}, {0x62e020,0xa42008e,0x6a8c3055,0xe0a5090e,0x42c42906,0x80b34814,0xb330803e,0x731c0102,0x600d1494,0x9400c20,0xc040301a,0xc094a451,0x5c88dca,0xa40c96c2,0x34040001,0x11000c8}, {0xa9c9550d,0x1c5a2428,0x48370142,0x100f7a4d,0x452a32b4,0x9205317b,0x5c44b894,0x458a68d7,0x2ed15097,0x42081943,0x9d40d202,0x20979840,0x64d5409,0,0,0}, {0,0x84800000,0x4215542,0x17001c06,0x61107624,0xb9ddff87,0x5c0a659f,0x3c00245d,0x59adb0,0,0,0x9b28d0,0x2000422,0x44080108,0xac409804,0x90288d0a}, {0xe0018700,0x310400,0x82211794,0x10540019,0x21a2cb2,0x40039c02,0x88043d60,0x7900080c,0xba3c1628,0xcb088640,0x90807274,0x1e,0xd8000000,0x9c87e188,0x4124034,0x2791ae64}, {0xe6fbe86b,0x5366408f,0x537feea6,0xb5e4e32b,0x2869f,0x1228548,0x8004402,0x20a02116,0x2040004,0x52000,0x1547e00,0x1ac162c,0x10852a84,0x5308c14,0xb943fbc3,0x906000ca}, {0x40326000,0x80901200,0x4c810b30,0x40020054,0x1d6a0029,0x2802000,0x48000,0x150c2610,0x7018040,0xc24d94d,0x18502810,0x50205001,0x4d01000,0x2017080,0x21c30108,0x132}, {0x7190088,0x5600802,0x4c0e0012,0xf0a10405,0x2,0,0,0,0,0,0,0x800000,0x35a8e8d,0x5a0421bd,0x11703488,0x26}, {0x10000000,0x8804c502,0xf801b815,0x25ed147c,0x1bb0ed60,0x1bd70589,0x1a627af3,0xac50d0c,0x524ae5d1,0x63050490,0x52440354,0x16122b57,0x1101a872,0x182949,0x10080948,0x886c6000}, {0x58f916e,0x39903012,0x4930f840,0x1b8880,0,0x428500,0x98000058,0x7014ea04,0x611d1628,0x60005113,0xa71a24,0,0x3c00000,0x10187120,0xa9270172,0x89066004}, {0x20cc022,0x40810900,0x8ca0202d,0xe34,0,0x11012100,0xc11a8011,0x892ec4c,0x85000040,0x1806c7ac,0x512e03e,0x108000,0x80ce4008,0x2106d01,0x8568641,0x27011e}, {0x83d3750,0x4e05e032,0x48401c0,0x1400081,0,0,0,0x591aa0,0x882443c8,0xc8001d48,0x72030152,0x4049013,0x4008280,0xd148a10,0x2088056,0x2704a040}, {0x4c000000,0,0,0xa3200000,0xa0ae1902,0xdf002660,0x7b15f010,0x3ad08121,0x284180,0x48001003,0x8014cc00,0xc414cf,0x30202000,0x1,0,0}, {0,0,0,0,0,0,0,0,0xffffdf7a,0xefffffff,0x3fffffff,0,0,0,0,0x2} }; class LgiIso2022Jp { public: LgiIso2022Jp() { int n, o = 0, i = 0; for (n=0; n<3; n++) iso2022jp_map[o++] = &iso2022jp_blocks[i++]; o += 13; for (n=0; n<4; n++) iso2022jp_map[o++] = &iso2022jp_blocks[i++];; o += 4; iso2022jp_map[o++] = &iso2022jp_blocks[i++];; o += 14; for (n=0; n<41; n++) iso2022jp_map[o++] = &iso2022jp_blocks[i++];; o += 47; iso2022jp_map[o++] = &iso2022jp_blocks[i++];; LgiAssert(o == 128); } bool CanEncode(char16 *s, ssize_t l) { if (s) { if (l < 0) l = StrlenW(s); for (int i=0; i> 9]; if (!*block) { return false; } u &= 0x1ff; if ( ( *block[u >> 5] & (1 << (u & 0x1f)) ) == 0 ) { return false; } } return true; } return false; } } Iso2022Jp; ///////////////////////////////////////////////////////////////////////////////////// bool LgiIsUtf8(const char *s, ssize_t len) { #define LenCheck(Need) \ if (len >= 0 && (len - (s - Start)) < Need) \ goto Utf8Error; #define TrailCheck() \ if (!IsUtf8_Trail(*s)) \ goto Utf8Error; \ s++; if (!s || *s == 0) return true; /* { ssize_t Len = len >= 0 ? len : Strlen(s); printf("Utf: %p %i\n", s, (int)Len); for (int i=0; iHasIconv(true); #endif // LGI_STATIC return false; } static GCharset LgiCharsets[] = { // Good 'ol ascii GCharset("us-ascii", "ASCII", _gdc_usascii_mapping, "ascii-us,iso-ir-6,ANSI_X3.4-1986,ISO_646.irv,ASCII,ISO646-US,us,IBM367,cp367,csASCII"), // Unicode (up here so they get found first) GCharset("utf-8", "Utf-8"), GCharset("utf-16", "Utf-16"), GCharset("utf-32", "Utf-32"), GCharset("ucs-2", "Ucs-2"), // ISO (get prefered over windows charsets by being first in this list) GCharset("iso-8859-1", "ISO 8859-1 (West Europe)", _gdc_ISO_8859_identity_mapping, "iso-ir-100,ISO_8859-1,latin1,l1,IBM819,CP819,csISOLatin1,iso8859-1"), GCharset("iso-8859-2", "ISO 8859-2 (East Europe)", _gdc_ISO_8859_2_mapping, "iso-ir-101,ISO_8859-2,latin2,l2,csISOLatin2"), GCharset("iso-8859-3", "ISO 8859-3 (Latin Script)", _gdc_ISO_8859_3_mapping, "iso-ir-109,ISO_8859-3,latin3,l3,csISOLatin3"), GCharset("iso-8859-4", "ISO 8859-4 (Baltic)", _gdc_ISO_8859_4_mapping, "iso-ir-110,ISO_8859-4,latin4,l4,csISOLatin4"), GCharset("iso-8859-5", "ISO 8859-5 (Russian)", _gdc_ISO_8859_5_mapping, "iso-ir-144,ISO_8859-5,cyrillic,csISOLatinCyrillic"), GCharset("iso-8859-6", "ISO 8859-6 (Arabic)", _gdc_ISO_8859_6_mapping, "iso-ir-127,ISO_8859-6,ECMA-114,ASMO-708,arabic,csISOLatinArabic"), GCharset("iso-8859-7", "ISO 8859-7 (Greek)", _gdc_ISO_8859_7_mapping, "iso-ir-126,ISO_8859-7,ELOT_928,ECMA-118,greek,greek8,csISOLatinGreek"), GCharset("iso-8859-8", "ISO 8859-8 (Hebrew)", _gdc_ISO_8859_8_mapping, "iso-ir-138,ISO_8859-8,hebrew,csISOLatinHebrew,iso-8859-8-i"), GCharset("iso-8859-9", "ISO 8859-9 (Turkish)", _gdc_ISO_8859_9_mapping, "iso-ir-148,ISO_8859-9,latin5,l5,csISOLatin5"), GCharset("iso-8859-13", "ISO 8859-13 (Baltik)", _gdc_ISO_8859_13_mapping, "ISO_8859-9"), GCharset("iso-8859-15", "ISO 8859-15 (Latic 9)", _gdc_ISO_8859_15_mapping, "ISO_8859-15"), // Windows GCharset("windows-874", "Windows 874 (Thai)", _gdc_win_874_mapping, "iso-8859-11,cp874"), GCharset("windows-932", "Windows 932 (Japanese)"), GCharset("windows-936", "Windows 936 (Chinese)"), GCharset("windows-949", "Windows 949 (Korean)"), GCharset("windows-950", "Windows 950 (Chinese)"), GCharset("windows-1250", "Windows 1250 (Latin 2)", _gdc_win_1250_mapping, "x-cp1250,cp1250"), GCharset("windows-1251", "Windows 1251 (Cyrillic)", _gdc_win_1251_mapping, "x-cp1251,cp1251"), GCharset("windows-1252", "Windows 1252 (Latin 1)", _gdc_win_1252_mapping, "x-cp1252,cp1252"), GCharset("windows-1253", "Windows 1253 (Greek)", _gdc_win_1253_mapping, "x-cp1253,cp1253"), GCharset("windows-1254", "Windows 1254 (Turkish)", _gdc_win_1254_mapping, "x-cp1254,cp1254"), GCharset("windows-1255", "Windows 1255 (Hebrew)", _gdc_win_1255_mapping, "x-cp1255,cp1255"), GCharset("windows-1256", "Windows 1256 (Arabic)", _gdc_win_1256_mapping, "x-cp1256,cp1256"), GCharset("windows-1257", "Windows 1257 (Baltic)", _gdc_win_1257_mapping, "x-cp1257,cp1257"), GCharset("windows-1258", "Windows 1258 (Veitnam)", _gdc_win_1258_mapping, "x-cp1258,cp1258"), // Russian GCharset("koi8-r", "KOI8-R", _gdc_koi8r_mapping, "csKOI8R"), GCharset("koi8-u", "KOI8-U", _gdc_koi8u_mapping, "csKOI8U"), GCharset("koi8-ru", "KOI8-RU", _gdc_koi8ru_mapping, "csKOI8RU"), GCharset("koi8-t", "KOI8-T (Tajik)"), // Codepages GCharset("cp850", "Cp850", 0, "IBM850,850,csPC850Multilingual"), GCharset("cp862", "Cp862", 0, "IBM862,862,csPC862LatinHebrew"), GCharset("cp866", "Cp866", 0, "IBM866,866,csIBM866"), GCharset("cp1133", "Cp1133 (Laotian)"), // Japanese GCharset("euc-jp", "EUC-JP", 0, "csEUCPkdFmtJapanese"), GCharset("shift_jis", "SHIFT_JIS", 0, "MS_Kanji,csShiftJIS"), GCharset("cp932", "cp932", 0, 0), GCharset("iso-2022-jp", "ISO-2022-JP", 0, "csISO2022JP"), GCharset("iso-2022-jp-1", "ISO-2022-JP-1"), GCharset("iso-2022-jp-2", "ISO-2022-JP-2", 0, "csISO2022JP2"), // Chinese GCharset("euc-cn", "EUC-CN (Chinese)"), GCharset("hz-gb-2312", "HZ (Chinese)", 0, "hz"), GCharset("gbk", "GBK (Chinese)", 0, "CP936,MS936,windows-936,x-gbk,gb2312,GB-2312,csGB2312,GB2312_CHARSET"), GCharset("gb18030", "GB18030 (Chinese)"), GCharset("euc-tw", "EUC-TW (Chinese)"), GCharset("big5", "BIG5 (Chinese)", 0, "csBig5"), GCharset("big5-hkscs", "BIG5-HKSCS (Chinese)"), // GCharset("gb2312", "GB-2312 (Chinese)", 0, "GB-2312,csGB2312"), GCharset("iso-2022-cn", "ISO-2022-CN (Chinese)"), GCharset("iso-2022-cn-eXT","ISO-2022-CN-EXT (Chinese)"), // Korean GCharset("euc-kr", "EUC-KR", 0, "csEUCKR"), GCharset("iso-2022-kr", "ISO-2022-KR", 0, "csISO2022KR"), GCharset("johab", "JOHAB"), GCharset("cp949", "CP949", 0, "ks_c_5601-1987,ks_c_5601"), // Armenian // GCharset("armscii-8", "ARMSCII-8 (Armenian)"), // Georgian GCharset("Georgian-Academy","Georgian-Academy"), GCharset("Georgian-PS", " Georgian-PS"), // Thai GCharset("tis-620", "TIS-620 (Thai)"), // Laotian GCharset("mulelao-1", "MuleLao-1"), // Vietnamese GCharset("viscii", "VISCII (Vietnamese)", 0, "csVISCII"), GCharset("tcvn", "TCVN (Vietnamese)"), // EOF marker GCharset() }; static GCharsetSystem CharsetSystem; GCharset *LgiGetCpInfo(const char *Cs) { return CharsetSystem.GetCsInfo(Cs); } ///////////////////////////////////////////////////////////////////////////// // Utf-16 conversion int LgiCpToAnsi(char *cp) { int Ansi = 0; if (cp && strnicmp(cp, "windows-", 8) == 0) { Ansi = atoi(cp+9); } return Ansi; } ssize_t LgiBufConvertCp(void *Out, const char *OutCp, ssize_t OutLen, const void *&In, const char *InCp, ssize_t &InLen) { int Status = 0; if (Out && OutCp && In && InCp) { GCharset *InInfo = LgiGetCpInfo(InCp); GCharset *OutInfo = LgiGetCpInfo(OutCp); if (InInfo && OutInfo) { char *In8 = (char*)In; uchar *Out8 = (uchar*)Out; if (InLen < 0) { switch (InInfo->Type) { case CpMapped: case CpUtf8: case CpIconv: InLen = (int)strlen((char*)In); break; case CpUtf16: case CpWindowsDb: InLen = StringLen((uint16*)In) << 1; break; case CpUtf32: - InLen = StringLen((uint32*)In) << 2; + InLen = StringLen((uint32_t*)In) << 2; break; default: LgiAssert(0); return 0; } } #ifdef WIN32 if (InInfo->Type == CpWindowsDb && OutInfo->Type == CpUtf16) { // mb -> unicode char Cp[32]; sprintf_s(Cp, sizeof(Cp), ".%s", InInfo->Charset + 8); setlocale(LC_ALL, Cp); void *Start = Out; while (OutLen >= sizeof(char16) && InLen > 0) { int s = mbtowc((char16*)Out, (char*)In, min(InLen, MB_CUR_MAX)); if (s > 0) { ((char*&)In) += s; InLen -= s; ((char16*&)Out)++; OutLen -= sizeof(char16); } else break; } return (NativeInt)Out-(NativeInt)Start; } else if (InInfo->Type == CpUtf16 && OutInfo->Type == CpWindowsDb) { // unicode -> mb char Cp[32]; sprintf_s(Cp, sizeof(Cp), ".%s", OutInfo->Charset + 8); setlocale(LC_ALL, Cp); void *Start = Out; while (OutLen >= MB_CUR_MAX && InLen > sizeof(char16) ) { #if 1 int s = 0; errno_t err = wctomb_s(&s, (char*)Out, OutLen, ((char16*)In)[0]); if (err || s == 0) break; #else int s = wctomb((char*)Out, ((char16*)In)[0] ); if (s > 0) #endif { ((char16*&)In)++; InLen -= sizeof(char16); ((char*&)Out) += s; OutLen -= s; } } return (NativeInt)Out-(NativeInt)Start; } else #endif if (InInfo->Type == CpIconv || OutInfo->Type == CpIconv) { #ifndef LGI_STATIC GFontSystem *Fs = GFontSystem::Inst(); if (Fs) return Fs->IconvConvert(OutInfo->GetIconvName(), (char*)Out, OutLen, InInfo->GetIconvName(), (const char*&)In, InLen); #else LgiAssert(!"No iconv in static build"); #endif } else { // Mapped or Utf conversion - uint32 Utf32 = 0; + uint32_t Utf32 = 0; while (OutLen > 0 && InLen > 0) { char *RewindIn = In8; ptrdiff_t RewindInLen = InLen; // Convert input char to Utf-32 switch (InInfo->Type) { case CpMapped: { if (*In8) { uchar i = (uchar)*In8++; InLen--; if (i & 0x80) { Utf32 = InInfo->UnicodeMap[i - 0x80]; if (!Utf32) Utf32 = '?'; } else { Utf32 = i; } } else { Utf32 = 0; InLen = 0; } break; } case CpUtf8: { - Utf32 = LgiUtf8To32((uint8 *&)In8, InLen); + Utf32 = LgiUtf8To32((uint8_t *&)In8, InLen); break; } case CpUtf16: { - Utf32 = LgiUtf16To32((const uint16 *&)In8, InLen); + Utf32 = LgiUtf16To32((const uint16_t *&)In8, InLen); if (Utf32 == 0xfeff || Utf32 == 0xfffe) continue; break; } case CpUtf32: { - Utf32 = *((uint32*&)In8)++; + Utf32 = *((uint32_t*&)In8)++; InLen -= 4; break; } default: LgiAssert(0); break; } if (!Utf32) { break; } // Convert Utf-32 into output format switch (OutInfo->Type) { case CpMapped: { if (Utf32 & ~0x7f) { int n; for (n=0; n<128; n++) { if (OutInfo->UnicodeMap[n] == Utf32) { *Out8++ = 0x80 + n; break; } } if (n >= 128) { for (n=0; MissingMaps[n].Unicode; n++) { if (MissingMaps[n].Unicode == Utf32) { *Out8++ = MissingMaps[n].Ascii; break; } } if (!MissingMaps[n].Unicode) { *Out8++ = '?'; } } } else { *Out8++ = Utf32; } OutLen--; break; } case CpUtf8: { // uchar *PrevOut8 = Out8; - if (!LgiUtf32To8(Utf32, (uint8*&) Out8, OutLen)) + if (!LgiUtf32To8(Utf32, (uint8_t*&) Out8, OutLen)) { // Not enough buffer to encode the character In8 = RewindIn; InLen = RewindInLen; OutLen = 0; } break; } case CpUtf16: { - LgiUtf32To16(Utf32, (uint16*&)Out8, OutLen); + LgiUtf32To16(Utf32, (uint16_t*&)Out8, OutLen); break; } case CpUtf32: { - *((uint32*&)Out8)++ = Utf32; + *((uint32_t*&)Out8)++ = Utf32; OutLen -= 4; break; } default: { break; } } } In = (void*)In8; Status = (int) (Out8 - (uchar*)Out); } } else { // printf("%s:%i - LgiBufConvertCp failed '%s' -> '%s'.\n", __FILE__, __LINE__, InCp, OutCp); } } return Status; } template T *DupeString(T *s, ssize_t Len = -1) { if (!s) return NULL; if (Len < 0) { Len = 0; while (s[Len]) Len++; } T *ns = new T[Len+1]; if (!ns) return NULL; memcpy(ns, s, sizeof(T) * Len); ns[Len] = 0; return ns; } void *LgiNewConvertCp(const char *OutCp, const void *In, const char *InCp, ssize_t InLen) { if (!OutCp || !In || !InCp) return NULL; GCharset *InInfo = LgiGetCpInfo(InCp); GCharset *OutInfo = LgiGetCpInfo(OutCp); if (!InInfo || !OutInfo) return NULL; GMemQueue b; if (InLen < 0) { switch (InInfo->Type) { case CpMapped: case CpUtf8: case CpIconv: InLen = (int)strlen((char*)In); break; case CpUtf16: case CpWindowsDb: InLen = StringLen((uint16*)In) << 1; break; case CpUtf32: - InLen = StringLen((uint32*)In) << 2; + InLen = StringLen((uint32_t*)In) << 2; break; default: LgiAssert(0); return NULL; } } int NullSize; switch (OutInfo->Type) { case CpMapped: case CpUtf8: case CpIconv: NullSize = 1; break; case CpUtf16: case CpWindowsDb: NullSize = 2; break; case CpUtf32: NullSize = 4; break; default: LgiAssert(0); return NULL; } if (!stricmp(InCp, OutCp)) { if (InInfo->Type == CpUtf16) { return DupeString((uint16*)In, InLen/sizeof(uint16)); } else if (InInfo->Type == CpUtf32) { - return DupeString((uint32*)In, InLen/sizeof(uint32)); + return DupeString((uint32_t*)In, InLen/sizeof(uint32_t)); } else { return NewStr((char*)In, InLen); } } if (InInfo->Type == CpIconv || OutInfo->Type == CpIconv) { #ifndef LGI_STATIC GFontSystem *Fs = GFontSystem::Inst(); if (Fs) { const char *InCs = InInfo->GetIconvName(); const char *OutCs = OutInfo->GetIconvName(); if (!Fs->IconvConvert(OutCs, &b, InCs, (const char*&)In, InLen)) { InCp = "iso-8859-1"; goto BufConvert; } } #else LgiAssert(!"No inconv in static build"); #endif } else { BufConvert: char Buf[2 << 10]; while (InLen > 0) { ssize_t Bytes = LgiBufConvertCp(Buf, OutCp, sizeof(Buf), In, InCp, InLen); if (Bytes > 0) { b.Write((uchar*)Buf, (int)Bytes); } else { break; } } } return b.GetSize() ? b.New(NullSize) : 0; } int LgiCharLen(const void *Str, const char *Cp, int Bytes) { if (Str && Cp) { GCharset *InInfo = LgiGetCpInfo(Cp); if (InInfo) { switch (InInfo->Type) { default: case CpMapped: { return (int)strlen((char*)Str); } case CpUtf8: { uchar *s = (uchar*)Str; int Len = 0; if (Bytes > 0) { uchar *e = s + Bytes; while (*s && s < e) { LgiNextUtf8((char*&)s); Len++; } } else { while (*s) { LgiNextUtf8((char*&)s); Len++; } } return Len; } case CpUtf16: { return StringLen((uint16*)Str); } case CpUtf32: { - return StringLen((uint32*)Str); + return StringLen((uint32_t*)Str); } } } } return 0; } bool LgiIsCpImplemented(char *Cp) { return LgiGetCpInfo(Cp) != 0; } const char *LgiAnsiToLgiCp(int AnsiCodePage) { if (AnsiCodePage < 0) { #ifdef WIN32 AnsiCodePage = GetACP(); #else return "utf-8"; #endif } #define WinCp(i) case i: return "windows-" #i; switch (AnsiCodePage) { WinCp(874) WinCp(932) WinCp(936) WinCp(949) WinCp(950) WinCp(1250) WinCp(1251) WinCp(1252) WinCp(1253) WinCp(1254) WinCp(1255) WinCp(1256) WinCp(1257) WinCp(1258) case 20127: return "us-ascii"; case 28591: return "iso-8859-1"; case 28592: return "iso-8859-2"; case 28593: return "iso-8859-3"; case 28594: return "iso-8859-4"; case 28595: return "iso-8859-5"; case 28596: return "iso-8859-6"; case 28597: return "iso-8859-7"; case 28598: return "iso-8859-8"; case 28599: return "iso-8859-9"; case 28600: return "ISO-8859-10"; case 28605: return "ISO-8859-15"; case 50220: case 50221: return "iso-2022-jp"; case 51932: return "euc-jp"; case 51949: return "euc-kr"; case 65001: return "utf-8"; } #undef WinCp return 0; } char *LgiSeekUtf8(const char *Ptr, ssize_t D, char *Start) { uchar *p = (uchar*)Ptr; if (p) { if (D >= 0) { for (int i=0; i(uchar*)Start; i++) { p--; while (p>(uchar*)Start && IsUtf8_Trail(*p)) p--; } } else { // You must pass a start point to move backwards in // the utf-8 string, otherwise you can run off the // beginning of the array. LgiAssert(0); } } return (char*)p; } bool LgiMatchCharset(short *Map, char16 *Utf, bool &Has8Bit) { if (Map && Utf) { // Test Charset because we have a map of all the chars in it... char16 *c; for (c = Utf; *c; c++) { if (*c > 0x7f) { // Check char Has8Bit = true; int i; for (i=0; i<128; i++) { if (Map[i] == *c) break; } if (i >= 128) { // Char not found return false; } } } if (Has8Bit) { if (!*c) { return true; } } } return false; } const char *LgiDetectCharset(const char *Utf8, ssize_t Len, List *Prefs) { const char *Status = "utf-8"; // The default.. GAutoWString Utf((char16*)LgiNewConvertCp(LGI_WideCharset, Utf8, "utf-8", Len)); if (Utf) { if (Prefs) { for (char *p = Prefs->First(); p; p = Prefs->Next()) { GCharset *Cp = CharsetSystem.GetCsInfo(p); if (Cp && stricmp(Cp->Charset, "us-ascii") != 0 && Cp->UnicodeMap) { bool Has8Bit = false; if (LgiMatchCharset(Cp->UnicodeMap, Utf, Has8Bit)) { return Cp->Charset; } if (!Has8Bit) { return "us-ascii"; } } } } for (GCharset *Cp = LgiCharsets + 1; Cp->Charset; Cp++) { if (Cp->UnicodeMap) { bool Has8Bit = false; if (LgiMatchCharset(Cp->UnicodeMap, Utf, Has8Bit)) { return Cp->Charset; } if (!Has8Bit) { return "us-ascii"; } } } } return Status; } char *LgiToNativeCp(const char *In, ssize_t InLen) { const char *Cp = LgiAnsiToLgiCp(); #ifdef WIN32 GCharset *CpInfo = LgiGetCpInfo(Cp); if (!CpInfo || CpInfo->Type == CpWindowsDb) { if (In) { // Double byte charset conversion, don't rely on iconv // being around to do the conversion. setlocale(LC_ALL, ".ACP"); if (InLen < 0) InLen = strlen(In); char16 *Wide = Utf8ToWide(In, InLen); if (Wide) { size_t Converted; size_t Len = wcstombs_s(&Converted, NULL, 0, Wide, 0); char *Buf = Len > 0 ? new char[Len+1] : 0; if (Buf) { wcstombs_s(&Converted, Buf, Len+1, Wide, Len+1); Buf[Len] = 0; } DeleteArray(Wide); return Buf; } } return 0; } #endif return (char*)LgiNewConvertCp(Cp, In, "utf-8", InLen); } char *LgiFromNativeCp(const char *In, ssize_t InLen) { const char *Cp = LgiAnsiToLgiCp(); #ifdef WIN32 GCharset *CpInfo = LgiGetCpInfo(Cp); if (!CpInfo || CpInfo->Type == CpWindowsDb) { if (In) { // Double byte charset conversion, don't rely on iconv // being around to do the conversion. setlocale(LC_ALL, ".ACP"); if (InLen < 0) { #ifdef __GNUC__ // FIXME InLen = strlen(In); #else InLen = _mbstrlen(In); #endif } else { // Work out how many chars 'InLen' bytes is ssize_t Bytes = InLen; const char *i = In; int Chars = 0; while (*i && Bytes > 0) { int n = mblen(i, MB_CUR_MAX); if (n > 0) { Chars++; Bytes -= n; i += n; } else break; } InLen = Chars; } size_t Converted; size_t Len = mbstowcs_s(&Converted, NULL, 0, In, 0); if (Len) { char16 *Buf = new char16[Len+1]; if (Buf) { mbstowcs_s(&Converted, Buf, Len, In, Len); Buf[Len] = 0; char *Utf8 = WideToUtf8(Buf); DeleteArray(Buf); return Utf8; } } } return 0; } #endif return (char*)LgiNewConvertCp("utf-8", In, Cp, InLen); } /////////////////////////////////////////////////////////////////////////// struct GCharsetSystemPriv { GCharset *Utf8; GCharset *Utf16; LHashTbl, GCharset*> Charsets; GCharsetSystemPriv() : Charsets(512) { Utf8 = 0; Utf16 = 0; } }; GCharsetSystem::GCharsetSystem() { char l[256]; // Charset setup, store all the charset pointers // in a hash table for O(1) lookup. d = new GCharsetSystemPriv; LgiAssert(LgiCharsets->Charset != NULL); for (GCharset *Cs = LgiCharsets; Cs->Charset; Cs++) { strcpy_s(l, sizeof(l), Cs->Charset); #ifdef _MSC_VER _strlwr_s(l, sizeof(l)); #else strlwr(l); #endif if (!stricmp(l, "utf-8")) d->Utf8 = Cs; else if (!stricmp(l, "utf-16")) d->Utf16 = Cs; d->Charsets.Add(l, Cs); GToken a(Cs->AlternateNames, ","); for (int n=0; nCharsets.Add(l, Cs); } } } GCharsetSystem::~GCharsetSystem() { DeleteObj(d); } GCharset *GCharsetSystem::GetCsInfo(const char *Cp) { if (Cp && d) { // Lookup the charset in the hash table char l[256]; strcpy_s(l, sizeof(l), Cp); #ifdef _MSC_VER _strlwr_s(l, sizeof(l)); #else strlwr(l); #endif if (!stricmp(l, "utf-8")) return d->Utf8; else if (!stricmp(l, "utf-16")) return d->Utf16; GCharset *Cs = (GCharset*) d->Charsets.Find(l); if (Cs) { return Cs; } else { // printf("%s:%i - No charset '%s' in font sub system.\n", __FILE__, __LINE__, l); // printf("Charsets=%i\n", Charsets->GetUsed()); } } return 0; } GCharset *LgiGetCsInfo(const char *Cs) { return CharsetSystem.GetCsInfo(Cs); } GCharset *GCharsetSystem::GetCsList() { return LgiCharsets; } GCharset *LgiGetCsList() { return LgiCharsets; } diff --git a/src/common/Gdc2/Font/GFontSystem.cpp b/src/common/Gdc2/Font/GFontSystem.cpp --- a/src/common/Gdc2/Font/GFontSystem.cpp +++ b/src/common/Gdc2/Font/GFontSystem.cpp @@ -1,752 +1,752 @@ #include #include #include "Lgi.h" #include "GToken.h" #include "GLibrary.h" #include "GLibraryUtils.h" #if defined(LGI_STATIC) || defined(BEOS) #undef HAS_ICONV #endif #if HAS_ICONV // // Get 'iconv.h' from http://www.gnu.org/software/libiconv // Current download at time of writing: // http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.8.tar.gz // // Then add whatever include path to your project or development // settings. Otherwise you can build without extended charset // support by changing the HAS_ICONV define in Lgi.h to '0' // // Linux should always build with iconv, on windows you may or // may not want to bother depending on what sort of app your // writing. // #include "iconv.h" #if defined(WIN32) typedef const char IconvChar; #else typedef char IconvChar; #endif #endif ///////////////////////////////////////////////////////////////////// // Private growable class for binary compatability class GFontSystemPrivate : public GLibrary { public: bool DefaultGlyphSub; int Used, Refs; bool FontTableLoaded; bool SubSupport; bool CheckedConfig; bool LibCheck; #ifdef __GTK_H__ Gtk::PangoFontMap *Map; Gtk::PangoContext *Ctx; GFontSystemPrivate() { Map = Gtk::pango_cairo_font_map_get_default(); if (!Map) LgiAssert(!"pango_cairo_font_map_get_default failed.\n"); Ctx = Gtk::pango_cairo_font_map_create_context((Gtk::PangoCairoFontMap*)Map); if (!Ctx) LgiAssert(!"pango_cairo_font_map_create_context failed.\n"); } #endif #if HAS_ICONV #ifdef WIN32 DynFunc2(iconv_t, libiconv_open, const char*, tocode, const char*, fromcode); DynFunc5(size_t, libiconv, iconv_t, cd, IconvChar**, inbuf, size_t*, inbytesleft, char**, outbuf, size_t*, outbytesleft); DynFunc1(int, libiconv_close, iconv_t, cd); #elif !defined(MAC) // Use glibc I guess iconv_t libiconv_open(const char *tocode, const char *fromcode) { return ::iconv_open(tocode, fromcode); } size_t libiconv(iconv_t cd, IconvChar** inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) { return ::iconv(cd, inbuf, inbytesleft, outbuf, outbytesleft); } int libiconv_close(iconv_t cd) { return ::iconv_close(cd); } bool IsLoaded() { return true; } #endif #endif // HAS_ICONV }; ///////////////////////////////////////////////////////////////////// static bool FontSystemDone = false; GFontSystem *GFontSystem::Me = 0; GFontSystem *GFontSystem::Inst() { if (!Me && !FontSystemDone) new GFontSystem; return Me; } GFontSystem::GFontSystem() { Me = this; d = new GFontSystemPrivate; // Glyph sub setup int Os = LgiGetOs(); d->SubSupport = (Os == LGI_OS_LINUX) || (Os == LGI_OS_WIN64) || (Os == LGI_OS_WIN32); // && Rev == 0); // WinXP does it's own glyph substitution d->DefaultGlyphSub = d->SubSupport; d->CheckedConfig = false; d->FontTableLoaded = false; d->Used = 1; // the '0th' font spot isn't used // because Lut[Char] == 0 means no font // available d->Refs = 0; d->LibCheck = false; ZeroObj(Lut); // Clear the table to 'no font' ZeroObj(Font); // Initialize the list of fonts to empty } GFontSystem::~GFontSystem() { // Clean up all our resources for (int i=0; iUsed; i++) { DeleteObj(Font[i]); } DeleteObj(d); Me = 0; FontSystemDone = true; } #ifdef __GTK_H__ Gtk::PangoFontMap *GFontSystem::GetFontMap() { return d->Map; } Gtk::PangoContext *GFontSystem::GetContext() { return d->Ctx; } #endif bool GFontSystem::GetGlyphSubSupport() { return d->SubSupport; } bool GFontSystem::GetDefaultGlyphSub() { if (!d->CheckedConfig && LgiApp) { GXmlTag *FontSys = LgiApp->GetConfig("font_system"); if (FontSys) { char *GlyphSub; if ((GlyphSub = FontSys->GetAttr("glyph_sub"))) { d->DefaultGlyphSub = atoi(GlyphSub) != 0; } } d->CheckedConfig = true; } return d->DefaultGlyphSub; } void GFontSystem::SetDefaultGlyphSub(bool i) { if (d->SubSupport) d->DefaultGlyphSub = i; } #ifdef WINNATIVE int CALLBACK _EnumFonts(ENUMLOGFONT FAR *lpelf, NEWTEXTMETRIC FAR *lpntm, int FontType, LPARAM lParam) { GString::Array *p = (GString::Array*) lParam; if (p) p->New() = lpelf->elfLogFont.lfFaceName; return true; } #endif int StringSort(GString *a, GString *b) { if (a && b) return stricmp(*a, *b); return 0; } bool GFontSystem::EnumerateFonts(GString::Array &Fonts) { Fonts.SetFixedLength(false); if (!AllFonts.Length()) { #if defined WINNATIVE HDC hDC = CreateCompatibleDC(NULL); if (hDC) { EnumFontFamilies( hDC, NULL, (FONTENUMPROC) _EnumFonts, (LPARAM) &AllFonts); DeleteDC(hDC); } #elif defined BEOS int32 numFamilies = count_font_families(); for (int32 i = 0; i < numFamilies; i++) { font_family Temp; uint32 flags; if (get_font_family(i, &Temp, &flags) == B_OK) { AllFonts.Insert(NewStr(Temp)); } } #elif defined __GTK_H__ Gtk::PangoFontFamily ** families; int n_families; Gtk::PangoFontMap * fontmap; fontmap = Gtk::pango_cairo_font_map_get_default(); Gtk::pango_font_map_list_families (fontmap, & families, & n_families); for (int i = 0; i < n_families; i++) { Gtk::PangoFontFamily * family = families[i]; const char * family_name; family_name = Gtk::pango_font_family_get_name (family); AllFonts.New() = family_name; } Gtk::g_free (families); #elif defined XWIN XObject o; XftFontSet *Set = XftListFonts( o.XDisplay(), 0, 0, XFT_FAMILY, 0); if (Set) { for (int i=0; infont; i++) { char s[256]; if (XftNameUnparse(Set->fonts[i], s, sizeof(s))) { AllFonts.Insert(NewStr(s)); } } XftFontSetDestroy(Set); } #elif defined MAC && !defined COCOA CFArrayRef fontFamilies = CTFontManagerCopyAvailableFontFamilyNames(); if (fontFamilies) { for(CFIndex i = 0; i < CFArrayGetCount(fontFamilies); i++) { CFStringRef fontName = (CFStringRef) CFArrayGetValueAtIndex(fontFamilies, i); if (fontName) { AllFonts.New() = fontName; } } CFRelease(fontFamilies); } #endif AllFonts.Sort(StringSort); } Fonts = AllFonts; return true; } bool GFontSystem::HasIconv(bool Quiet) { if (d->IsLoaded()) return true; bool Status = false; if (!d->LibCheck) { d->LibCheck = true; Status = d->Load("libiconv." LGI_LIBRARY_EXT); if (!Status && !Quiet) { if (!NeedsCapability("libiconv")) { static bool Warn = true; if (Warn) { Warn = false; LgiAssert(!"Iconv is not available"); } } } } return Status; } #if defined MAC && !defined COCOA // This converts a normal charset to an Apple encoding ID static CFStringEncoding CharsetToEncoding(const char *cs) { CFStringRef InputCs = CFStringCreateWithCString(0, cs, kCFStringEncodingUTF8); if (!InputCs) return kCFStringEncodingUTF8; // Um what to do here? CFStringEncoding enc = CFStringConvertIANACharSetNameToEncoding(InputCs); CFRelease(InputCs); return enc; } #endif ssize_t GFontSystem::IconvConvert(const char *OutCs, GStreamI *Out, const char *InCs, const char *&In, ssize_t InLen) { LgiAssert(InLen > 0); if (!Out || !In) return 0; #if defined(MAC) #if !defined COCOA char Buf[2 << 10]; CFStringEncoding InEnc = CharsetToEncoding(InCs); CFStringEncoding OutEnc = CharsetToEncoding(OutCs); if (InEnc != kCFStringEncodingInvalidId && OutEnc != kCFStringEncodingInvalidId) { CFStringRef r = CFStringCreateWithBytes(0, (const UInt8 *)In, InLen, InEnc, false); if (r) { CFRange g = { 0, CFStringGetLength(r) }; CFIndex used = 0; CFIndex ret; while ((ret = CFStringGetBytes(r, g, OutEnc, '?', false, (UInt8*)Buf, sizeof(Buf), &used)) > 0 && g.length > 0) { // char16 *b = (char16*)Buf; Out->Write(Buf, used); g.location += ret; g.length -= ret; } CFRelease(r); } else return 0; } else return 0; #endif #elif HAS_ICONV if (!HasIconv(false)) { return 0; } char Buf[2 << 10]; iconv_t Conv; if ((NativeInt)(Conv = d->libiconv_open(OutCs, InCs)) >= 0) { char *i = (char*)In; LgiAssert((NativeInt)Conv != 0xffffffff); while (InLen) { char *o = (char*)Buf; size_t OutLen = sizeof(Buf); ssize_t OldInLen = InLen; size_t InSz = InLen; ssize_t s = d->libiconv(Conv, (IconvChar**)&i, &InSz, &o, &OutLen); InLen = InSz; Out->Write((uchar*)Buf, sizeof(Buf) - OutLen); if (OldInLen == InLen) break; } d->libiconv_close(Conv); } else { LgiTrace("Iconv won't load.\n"); return 0; } #endif return 1; } ssize_t GFontSystem::IconvConvert(const char *OutCs, char *Out, ssize_t OutLen, const char *InCs, const char *&In, ssize_t InLen) { ssize_t Status = 0; if (!Out || !In || !HasIconv(false)) return 0; #if defined(MAC) #if !defined COCOA CFStringEncoding InEnc = CharsetToEncoding(InCs); CFStringEncoding OutEnc = CharsetToEncoding(OutCs); if (InEnc != kCFStringEncodingInvalidId && OutEnc != kCFStringEncodingInvalidId) { CFStringRef r = CFStringCreateWithBytes(0, (const UInt8 *)In, InLen, InEnc, false); if (r) { CFRange g = { 0, CFStringGetLength(r) }; CFIndex ret = CFStringGetBytes(r, g, OutEnc, '?', false, (UInt8*)Out, OutLen, 0); CFRelease(r); return ret; } } #endif #elif HAS_ICONV // Set locale yet? static bool sl = false; if (!sl) { sl = true; setlocale(LC_ALL, ""); } // Iconv conversion // const char *InCs = InInfo->GetIconvName(); // const char *OutCs = OutInfo->GetIconvName(); iconv_t Conv; if ((Conv = d->libiconv_open(OutCs, InCs)) >= 0) { size_t InSz = InLen; size_t OutSz = OutLen; char *o = Out; char *i = (char*)In; // Convert char *Start = o; ssize_t s = d->libiconv(Conv, (IconvChar**)&i, &InSz, &o, &OutSz); InLen = InSz; OutLen = OutSz; d->libiconv_close(Conv); In = (const char*)i; Status = (NativeInt)o-(NativeInt)Out; } else { LgiTrace("Iconv not present/won't load.\n"); } #endif return Status; } GFont *GFontSystem::GetBestFont(char *Str) { GFont *MatchingFont = 0; if (d->SubSupport) { char16 *s = Utf8ToWide(Str); if (s) { // Make list of possible fonts List Possibles; char16 *i; for (i = s; *i; i++) { GFont *Font = GetGlyph(*i, SysFont); if (Font) { bool Has = false; for (GFont *h=Possibles.First(); h; h=Possibles.Next()) { if (h == Font) { Has = true; break; } } if (!Has) { Possibles.Insert(Font); } } } // Choose best match amongst possibles int MatchingChars = 0; for (GFont *h=Possibles.First(); h; h=Possibles.Next()) { int Chars = 0; for (i = s; *i; i++) { if (h->GetGlyphMap() && _HasUnicodeGlyph(h->GetGlyphMap(), *i)) { Chars++; } } if (!MatchingFont || Chars > MatchingChars) { MatchingFont = h; } } DeleteArray(s); } } return MatchingFont; } typedef LHashTbl,int> FontMap; DeclGArrayCompare(FontNameCmp, GString, FontMap) { int ap = param->Find(*a); int bp = param->Find(*b); return bp - ap; } bool GFontSystem::AddFont(GAutoPtr Fnt) { if (!Fnt) return false; if (d->Used >= CountOf(Font)) return false; Fnt->Create(); auto *Map = Fnt->GetGlyphMap(); if (Map) { - uint8 Used = d->Used; + uint8_t Used = d->Used; // Insert all the characters of this font into the LUT // so that we can map from a character back to the font for (int k=0; k<=MAX_UNICODE; k += 8) { // unroll the loop for maximum speed.. - uint8 m = Map[k >> 3]; + uint8_t m = Map[k >> 3]; #define TestLut(i) \ if (!Lut[k+i] && (m & (1 << i))) \ Lut[k+i] = Used; TestLut(0); TestLut(1); TestLut(2); TestLut(3); TestLut(4); TestLut(5); TestLut(6); TestLut(7); } } Font[d->Used++] = Fnt.Release(); return true; } -GFont *GFontSystem::GetGlyph(uint32 u, GFont *UserFont) +GFont *GFontSystem::GetGlyph(uint32_t u, GFont *UserFont) { if (u > MAX_UNICODE || !UserFont) { LgiAssert(!"Invalid character"); return 0; } // Check app font if (!d->SubSupport || (UserFont->GetGlyphMap() && _HasUnicodeGlyph(UserFont->GetGlyphMap(), u))) { return UserFont; } // Check LUT GFont *Has = 0; if (Lut[u]) { Has = Font[Lut[u]]; LgiAssert(Has != NULL); if (!Has) { LgiTrace("%s:%i - Font table missing pointer. u=%i Lut[u]=%i\n", _FL, u, Lut[u]); Has = UserFont; } } else if (d->Used < 255 && !d->FontTableLoaded) { // Add fonts to Lut... if (!SubFonts.Length()) { #if LGI_EXCEPTIONS try { #endif FontMap Pref(0, 0); if (GFontSystem::Inst()->EnumerateFonts(SubFonts)) { // Reorder font list to prefer certain known as good fonts or // avoid certain bad fonts. if (LgiGetOs() == LGI_OS_WIN32 || LgiGetOs() == LGI_OS_WIN64) { Pref.Add("Microsoft Sans Serif", 1); Pref.Add("Arial Unicode MS", 1); Pref.Add("Verdana", 1); Pref.Add("Tahoma", 1); Pref.Add("Bookworm", -1); Pref.Add("Christmas Tree", -1); Pref.Add("MingLiU", -1); } if (LgiGetOs() == LGI_OS_LINUX) { // Windows fonts are much better than anything Linux // has to offer. Pref.Add("Verdana", 1); Pref.Add("Tahoma", 1); Pref.Add("Arial Unicode MS", 1); // Most linux fonts suck... and the rest aren't much // good either Pref.Add("AR PL *", -1); Pref.Add("Baekmuk *", -1); Pref.Add("console8*", -1); Pref.Add("Courier*", -1); Pref.Add("Fangsong*", -1); Pref.Add("Kochi*", -1); Pref.Add("MiscFixed", -1); Pref.Add("Serto*", -1); Pref.Add("Standard Symbols*", -1); Pref.Add("Nimbus*", -1); } // Prefer these fonts... SubFonts.Sort(FontNameCmp, &Pref); // Delete fonts prefixed with '@' to the end, as they are for // vertical rendering... and aren't suitable for what LGI uses // fonts for. for (unsigned i=0; iUsed; while (SubFonts.Length() > 0 && (LgiCurrentTime() - Start) < 10) { GString f = SubFonts[0]; SubFonts.DeleteAt(0, true); if (d->Used >= CountOf(Font)) { // No more space SubFonts.Empty(); break; } GAutoPtr Fnt(new GFont); if (Fnt) { *Fnt.Get() = *UserFont; Fnt->Face(f); if (AddFont(Fnt)) { GFont *Prev = Font[d->Used - 1]; if (_HasUnicodeGlyph(Prev->GetGlyphMap(), u)) { Has = Prev; LgiAssert(Has != NULL); break; } } } } LgiTrace("Loaded %i fonts for glyph sub.\n", d->Used - Used); #if LGI_EXCEPTIONS } catch (...) { LgiTrace("%s:%i - Glyph search crashed.\n", _FL); } #endif if (!SubFonts.Length()) { d->FontTableLoaded = true; } } return Has; } diff --git a/src/common/Gdc2/Font/LStringLayout.cpp b/src/common/Gdc2/Font/LStringLayout.cpp --- a/src/common/Gdc2/Font/LStringLayout.cpp +++ b/src/common/Gdc2/Font/LStringLayout.cpp @@ -1,597 +1,597 @@ #include "Lgi.h" #include "LStringLayout.h" #define DEBUG_PROFILE_LAYOUT 0 #define DEBUG_LAYOUT 0 static char White[] = " \t\r\n"; //////////////////////////////////////////////////////////////////////////////////// void LLayoutString::Set(int LineIdx, int FixX, int YPx, LLayoutRun *Lr, ssize_t Start) { Line = LineIdx; Src = Lr; Fx = FixX; y = YPx; Offset = Start; GCss::ColorDef Fill = Src->Color(); if (Fill.Type == GCss::ColorRgb) Fore.Set(Fill.Rgb32, 32); else if (Fill.Type != GCss::ColorTransparent) Fore.Set(LC_TEXT, 24); Fill = Src->BackgroundColor(); if (Fill.Type == GCss::ColorRgb) Back.Set(Fill.Rgb32, 32); } //////////////////////////////////////////////////////////////////////////////////// LStringLayout::LStringLayout(GFontCache *fc) { FontCache = fc; Wrap = false; AmpersandToUnderline = false; Debug = false; Empty(); } LStringLayout::~LStringLayout() { Empty(); } void LStringLayout::Empty() { Min.Zero(); Max.Zero(); MinLines = 0; Bounds.ZOff(-1, -1); Strs.DeleteObjects(); Text.DeleteObjects(); } bool LStringLayout::Add(const char *Str, GCss *Style) { if (!Str) return false; if (AmpersandToUnderline) { LLayoutRun *r; for (const char *s = Str; *s; ) { const char *e = s; // Find '&' or end of string while (*e && !(e[0] == '&' && e[1] != '&')) e++; if (e > s) { // Add text before '&' r = new LLayoutRun(Style); r->Text.Set(s, e - s); Text.Add(r); } if (!*e) break; // End of string // Add '&'ed char r = new LLayoutRun(Style); r->TextDecoration(GCss::TextDecorUnderline); s = e + 1; // Skip the '&' itself GUtf8Ptr p(s); // Find the end of the next unicode char p++; if ((const char*)p.GetPtr() == s) break; // No more text: exit r->Text.Set(s, (const char*)p.GetPtr()-s); Text.Add(r); s = (const char*) p.GetPtr(); } } else // No parsing required { LLayoutRun *r = new LLayoutRun(Style); r->Text = Str; Text.Add(r); } return true; } -uint32 LStringLayout::NextChar(char *s) +uint32_t LStringLayout::NextChar(char *s) { ssize_t Len = 0; while (s[Len] && Len < 6) Len++; - return LgiUtf8To32((uint8*&)s, Len); + return LgiUtf8To32((uint8_t*&)s, Len); } -uint32 LStringLayout::PrevChar(char *s) +uint32_t LStringLayout::PrevChar(char *s) { if (IsUtf8_Lead(*s) || IsUtf8_1Byte(*s)) { s--; ssize_t Len = 1; while (IsUtf8_Trail(*s) && Len < 6) { s--; Len++; } - return LgiUtf8To32((uint8*&)s, Len); + return LgiUtf8To32((uint8_t*&)s, Len); } return 0; } GFont *LStringLayout::GetBaseFont() { return FontCache && FontCache->GetDefaultFont() ? FontCache->GetDefaultFont() : SysFont; } void LStringLayout::SetBaseFont(GFont *f) { if (FontCache) FontCache->SetDefaultFont(f); } typedef GArray LayoutArray; // Pre-layout min/max calculation void LStringLayout::DoPreLayout(int32 &MinX, int32 &MaxX) { MinX = 0; MaxX = 0; GFont *f = GetBaseFont(); if (!Text.Length() || !f) return; GArray Lines; int Line = 0; for (LLayoutRun **Run = NULL; Text.Iterate(Run); ) { char *s = (*Run)->Text; GFont *f = FontCache ? FontCache->GetFont(*Run) : SysFont; LgiAssert(f != NULL); char *Start = s; while (*s) { if (*s == '\n') { Lines[Line++].Add(new LLayoutString(f, Start, s - Start)); Start = s + 1; } s++; } if (s > Start) Lines[Line].Add(new LLayoutString(f, Start, s - Start)); s = (*Run)->Text; while (*s) { while (*s && strchr(White, *s)) s++; char *e = s; while (*e) { - uint32 c = NextChar(e); + uint32_t c = NextChar(e); if (c == 0) break; if (e > s && LGI_BreakableChar(c)) break; char *cur = e; e = LgiSeekUtf8(e, 1); if (e == cur) // sanity check... { LgiAssert(!"LgiSeekUtf8 broke."); break; } } if (e == s) break; GDisplayString d(f, s, (int) (e - s)); MinX = MAX(d.X(), MinX); s = e; } } for (unsigned i=0; iFX(); } int LineX = Fx >> GDisplayString::FShift; MaxX = MAX(MaxX, LineX); Ln.DeleteObjects(); } } struct Break { LLayoutRun *Run; ssize_t Bytes; void Set(LLayoutRun *r, ssize_t b) { Run = r; Bytes = b; } }; #define GotoNextLine() \ LineFX = 0; \ MinLines++; \ y += LineHeight; \ StartLine = Strs.Length(); \ Breaks.Empty(); // Create the lines from text bool LStringLayout::DoLayout(int Width, int MinYSize, bool DebugLog) { #if DEBUG_PROFILE_LAYOUT GProfile Prof("LStringLayout::DoLayout"); Prof.HideResultsIfBelow(100); #endif // Empty Min.x = Max.x = 0; Min.y = Max.y = 0; MinLines = 0; Strs.DeleteObjects(); // Param validation GFont *f = GetBaseFont(); if (!f || !Text.Length() || Width <= 0) { Min.y = Max.y = MAX((f ? f : SysFont)->GetHeight(), MinYSize); return false; } // Loop over input int y = 0; int LineFX = 0; int LineHeight = 0; int Shift = GDisplayString::FShift; // By definition these potential break points are all // on the same line. Clear the array on each new line. GArray Breaks; /// Index of the display string starting the line ssize_t StartLine = 0; /// There is alway one line of text... even if it's empty MinLines = 1; #if DEBUG_LAYOUT if (Debug) LgiTrace("Text.Len=%i\n", Text.Length()); #endif for (LLayoutRun **Run = NULL; Text.Iterate(Run); ) { char *Start = (*Run)->Text; GUtf8Ptr s(Start); #if DEBUG_LAYOUT if (Debug) LgiTrace(" Run='%s' %p\n", s.GetPtr(), *Run); #endif #if DEBUG_PROFILE_LAYOUT GString Pm; Pm.Printf("[%i] Run = '%.*s'\n", (int) (Run - Text.AddressOf()), max(20, (*Run)->Text.Length()), s.GetPtr()); Prof.Add(Pm); #endif while (s) { GUtf8Ptr e(s); ssize_t StartOffset = (char*)s.GetPtr() - Start; #if DEBUG_LAYOUT if (Debug) LgiTrace(" Breaks: "); #endif - for (uint32 Ch; (Ch = e); e++) + for (uint32_t Ch; (Ch = e); e++) { if (Ch == '\n') break; if ((char*)e.GetPtr() > Start && LGI_BreakableChar(Ch)) { ssize_t Pos = (char*)e.GetPtr() - Start; #if DEBUG_LAYOUT if (Debug) LgiTrace("%i, ", (int)Pos); #endif Breaks.New().Set(*Run, Pos); } } #if DEBUG_LAYOUT if (Debug) LgiTrace("\n"); #endif ssize_t Bytes = e - s; GFont *Fnt = NULL; if (FontCache) Fnt = FontCache->GetFont(*Run); if (!Fnt) Fnt = f; #if DEBUG_PROFILE_LAYOUT Prof.Add("CreateStr"); #endif // Create a display string for the segment LLayoutString *n = new LLayoutString(Fnt, Bytes ? (char*)s.GetPtr() : (char*)"", Bytes ? (int)Bytes : 1); if (n) { n->Set(MinLines - 1, LineFX, y, *Run, StartOffset); LineFX += n->FX(); // Do min / max size calculation Min.x = Min.x ? MIN(Min.x, LineFX) : LineFX; Max.x = MAX(Max.x, LineFX); if (Wrap && (LineFX >> Shift) > Width) { // If wrapping, work out the split point and the text is too long int PosPx = Width - (n->Fx >> Shift); ssize_t Chars = n->CharAt(PosPx); #if DEBUG_LAYOUT if (Debug) LgiTrace(" CharAt(%i)=%i\n", PosPx, (int)Chars); #endif if (Breaks.Length() > 0) { #if DEBUG_PROFILE_LAYOUT Prof.Add("WrapWithBreaks"); #endif // Break at previous word break Strs.Add(n); // Roll back till we get a break point that fits for (ssize_t i = Breaks.Length() - 1; i >= 0; i--) { Break &b = Breaks[i]; #if DEBUG_LAYOUT if (Debug) LgiTrace(" Testing Break %i: %p %i\n", i, b.Run, b.Bytes); #endif // Calc line width from 'StartLine' to 'Break[i]' int FixX = 0; GAutoPtr Broken; size_t k; for (k=StartLine; k(Strs[k]); if (!Ls) return false; if (b.Run == Ls->Src && b.Bytes >= Ls->Offset) { // Add just the part till the break if (Broken.Reset(new LLayoutString(Ls, b.Run->Text.Get() + Ls->Offset, b.Bytes - Ls->Offset))) FixX += Broken->FX(); break; } else // Add whole string FixX += Ls->FX(); } #if DEBUG_LAYOUT if (Debug) LgiTrace(" Len=%i of %i\n", FixX, Width); #endif if ((FixX >> Shift) <= Width || i == 0) { // Found a good fit... #if DEBUG_LAYOUT if (Debug) LgiTrace(" Found a fit\n"); #endif // So we want to keep 'StartLine' to 'k-1' as is... while (Strs.Length() > k) { delete Strs.Last(); Strs.PopLast(); } if (Broken) { // Then change out from 'k' onwards for 'Broken' LineHeight = MAX(LineHeight, Broken->Y()); Strs.Add(Broken.Release()); } LineFX = FixX; s = b.Run->Text.Get() + b.Bytes; ssize_t Idx = Text.IndexOf(b.Run); if (Idx < 0) { LgiAssert(0); return false; } Run = Text.AddressOf(Idx); break; } } // Start pouring from the break pos... while (s && strchr(White, s)) s++; // Move to the next line... GotoNextLine(); continue; } else { #if DEBUG_PROFILE_LAYOUT Prof.Add("WrapNoBreaks"); #endif // Break at next word break e = s; e += Chars; - for (uint32 Ch; (Ch = e); e++) + for (uint32_t Ch; (Ch = e); e++) { if (LGI_BreakableChar(Ch)) break; } } LineFX -= n->FX(); DeleteObj(n); n = new LLayoutString(Fnt, (char*)s.GetPtr(), e - s); n->Set(MinLines - 1, LineFX, y, *Run, (char*)s.GetPtr() - Start); LineHeight = MAX(LineHeight, n->Y()); GotoNextLine(); } #if DEBUG_PROFILE_LAYOUT Prof.Add("Bounds"); #endif GRect Sr(0, 0, n->X()-1, n->Y()-1); Sr.Offset(n->Fx >> Shift, n->y); if (Strs.Length()) Bounds.Union(&Sr); else Bounds = Sr; LineHeight = MAX(LineHeight, Sr.Y()); Strs.Add(n); } if (e == '\n') { s = ++e; #if DEBUG_PROFILE_LAYOUT Prof.Add("NewLine"); #endif GotoNextLine(); } else s = e; } } #if DEBUG_PROFILE_LAYOUT Prof.Add("Post"); #endif Min.y = LineHeight * MinLines; if (Min.y < MinYSize) Min.y = MinYSize; Max.y = y + LineHeight; if (Max.y < MinYSize) Max.y = MinYSize; if (DebugLog) LgiTrace("CreateTxtLayout(%i) min=%i,%i max=%i,%i\n", Width, Min.x, Min.y, Max.x, Min.y); return true; } void LStringLayout::Paint( GSurface *pDC, GdcPt2 pt, GColour Back, GRect &rc, bool Enabled, bool Focused) { if (!pDC) return; #ifdef WINDOWS GRegion Rgn(rc); #else // Fill the background... if (!Back.IsTransparent()) { pDC->Colour(Back); pDC->Rectangle(&rc); } int Shift = GDisplayString::FShift; #endif GColour FocusFore(LC_FOCUS_SEL_FORE, 24); // Draw all the text for (GDisplayString **ds = NULL; Strs.Iterate(ds); ) { LLayoutString *s = dynamic_cast(*ds); GFont *f = s->GetFont(); GColour Bk = s->Back.IsTransparent() ? Back : s->Back; #ifdef WINDOWS int y = pt.y + s->y; GRect r(pt.x + s->Fx, y, pt.x + s->Fx + s->FX() - 1, y + s->Y() - 1); Rgn.Subtract(&r); f->Transparent(Bk.IsTransparent()); #else f->Transparent(true); #endif // LgiTrace("'%S' @ %i,%i\n", (const char16*)(**ds), r.x1, r.y1); if (Enabled) { f->Colour(Focused ? FocusFore : s->Fore, Bk); #ifdef WINDOWS s->Draw(pDC, r.x1, r.y1, &r); #else GdcPt2 k((pt.x << Shift) + s->Fx, (pt.y + s->y) << Shift); s->FDraw(pDC, k.x, k.y); #endif } else { f->Transparent(Bk.IsTransparent()); f->Colour(GColour(LC_LIGHT, 24), Bk); #ifdef WINDOWS s->Draw(pDC, r.x1+1, r.y1+1, &r); #else GdcPt2 k(((pt.x+1) << Shift) + s->Fx, (pt.y + 1 + s->y) << Shift); s->FDraw(pDC, k.x, k.y); #endif f->Transparent(true); f->Colour(LC_LOW, LC_MED); #ifdef WINDOWS s->Draw(pDC, r.x1, r.y1, &r); #else s->FDraw(pDC, (pt.x << Shift) + s->Fx, (pt.y + s->y) << Shift); #endif } } #ifdef WINDOWS // Fill any remaining area with background... if (!Back.IsTransparent()) { pDC->Colour(Back); for (GRect *r=Rgn.First(); r; r=Rgn.Next()) pDC->Rectangle(r); } #endif } diff --git a/src/common/Gdc2/GColour.cpp b/src/common/Gdc2/GColour.cpp --- a/src/common/Gdc2/GColour.cpp +++ b/src/common/Gdc2/GColour.cpp @@ -1,532 +1,532 @@ #include "Lgi.h" #include "GPalette.h" const GColour GColour::Black(0, 0, 0); const GColour GColour::White(255, 255, 255); const GColour GColour::Red(255, 0, 0); const GColour GColour::Green(0, 192, 0); const GColour GColour::Blue(0, 0, 255); GColour::GColour() { space = CsNone; flat = 0; pal = NULL; } -GColour::GColour(uint8 idx8, GPalette *palette) +GColour::GColour(uint8_t idx8, GPalette *palette) { c8(idx8, palette); } GColour::GColour(int r, int g, int b, int a) { c32(Rgba32(r, g, b, a)); } -GColour::GColour(uint32 c, int bits, GPalette *palette) +GColour::GColour(uint32_t c, int bits, GPalette *palette) { Set(c, bits, palette); } int GColour::HlsValue(double fN1, double fN2, double fHue) const { if (fHue > 360.0) fHue -= 360.0; else if (fHue < 0.0) fHue += 360.0; if (fHue < 60.0) return (int) ((fN1 + (fN2 - fN1) * fHue / 60.0) * 255.0 + 0.5); else if (fHue < 180.0) return (int) ((fN2 * 255.0) + 0.5); else if (fHue < 240.0) return (int) ((fN1 + (fN2 - fN1) * (240.0 - fHue) / 60.0) * 255.0 + 0.5); return (int) ((fN1 * 255.0) + 0.5); } GColourSpace GColour::GetColourSpace() { return space; } bool GColour::IsValid() { return space != CsNone; } void GColour::Empty() { space = CsNone; } bool GColour::IsTransparent() { if (space == System32BitColourSpace) return rgb.a == 0; else if (space == CsIndex8) return !pal || index >= pal->GetSize(); return space == CsNone; } void GColour::Rgb(int r, int g, int b, int a) { c32(Rgba32(r, g, b, a)); } -void GColour::Set(uint32 c, int bits, GPalette *palette) +void GColour::Set(uint32_t c, int bits, GPalette *palette) { pal = 0; switch (bits) { case 8: { index = c; space = CsIndex8; pal = palette; break; } case 15: { space = System32BitColourSpace; rgb.r = Rc15(c); rgb.g = Gc15(c); rgb.b = Bc15(c); rgb.a = 255; break; } case 16: { space = System32BitColourSpace; rgb.r = Rc16(c); rgb.g = Gc16(c); rgb.b = Bc16(c); rgb.a = 255; break; } case 24: case 48: { space = System32BitColourSpace; rgb.r = R24(c); rgb.g = G24(c); rgb.b = B24(c); rgb.a = 255; break; } case 32: case 64: { space = System32BitColourSpace; rgb.r = R32(c); rgb.g = G32(c); rgb.b = B32(c); rgb.a = A32(c); break; } default: { space = System32BitColourSpace; flat = 0; LgiTrace("Error: Unable to set colour %x, %i\n", c, bits); LgiAssert(!"Not a known colour depth."); } } } -uint32 GColour::Get(int bits) +uint32_t GColour::Get(int bits) { switch (bits) { case 8: if (space == CsIndex8) return index; LgiAssert(!"Not supported."); break; case 24: return c24(); case 32: return c32(); } return 0; } -uint8 GColour::r() const +uint8_t GColour::r() const { return R32(c32()); } -uint8 GColour::g() const +uint8_t GColour::g() const { return G32(c32()); } -uint8 GColour::b() const +uint8_t GColour::b() const { return B32(c32()); } -uint8 GColour::a() const +uint8_t GColour::a() const { return A32(c32()); } -uint8 GColour::c8() const +uint8_t GColour::c8() const { return index; } -void GColour::c8(uint8 c, GPalette *p) +void GColour::c8(uint8_t c, GPalette *p) { space = CsIndex8; pal = p; index = c; } -uint32 GColour::c24() const +uint32_t GColour::c24() const { if (space == System32BitColourSpace) { return Rgb24(rgb.r, rgb.g, rgb.b); } else if (space == CsIndex8) { if (pal) { // colour palette lookup if (index < pal->GetSize()) { GdcRGB *c = (*pal)[index]; if (c) { return Rgb24(c->r, c->g, c->b); } } return 0; } return Rgb24(index, index, index); // monochome } else if (space == CsHls32) { return Rgb32To24(c32()); } // Black... return 0; } -void GColour::c24(uint32 c) +void GColour::c24(uint32_t c) { space = System32BitColourSpace; rgb.r = R24(c); rgb.g = G24(c); rgb.b = B24(c); rgb.a = 255; pal = NULL; } -uint32 GColour::c32() const +uint32_t GColour::c32() const { if (space == System32BitColourSpace) { return Rgba32(rgb.r, rgb.g, rgb.b, rgb.a); } else if (space == CsIndex8) { if (pal) { // colour palette lookup if (index < pal->GetSize()) { GdcRGB *c = (*pal)[index]; if (c) { return Rgb32(c->r, c->g, c->b); } } return 0; } return Rgb32(index, index, index); // monochome } else if (space == CsHls32) { // Convert from HLS back to RGB GColour tmp = *this; tmp.ToRGB(); return tmp.c32(); } // Transparent? return 0; } -void GColour::c32(uint32 c) +void GColour::c32(uint32_t c) { space = System32BitColourSpace; pal = NULL; rgb.r = R32(c); rgb.g = G32(c); rgb.b = B32(c); rgb.a = A32(c); } GColour GColour::Mix(GColour Tint, float RatioOfTint) const { COLOUR c1 = c32(); COLOUR c2 = Tint.c32(); double RatioThis = 1.0 - RatioOfTint; int r = (int) ((R32(c1) * RatioThis) + (R32(c2) * RatioOfTint)); int g = (int) ((G32(c1) * RatioThis) + (G32(c2) * RatioOfTint)); int b = (int) ((B32(c1) * RatioThis) + (B32(c2) * RatioOfTint)); int a = (int) ((A32(c1) * RatioThis) + (A32(c2) * RatioOfTint)); return GColour(r, g, b, a); } -uint32 GColour::GetH() +uint32_t GColour::GetH() { ToHLS(); return hls.h; } bool GColour::HueIsUndefined() { ToHLS(); return hls.h == HUE_UNDEFINED; } -uint32 GColour::GetL() +uint32_t GColour::GetL() { ToHLS(); return hls.l; } -uint32 GColour::GetS() +uint32_t GColour::GetS() { ToHLS(); return hls.s; } bool GColour::ToHLS() { if (space == CsHls32) return true; - uint32 nMax, nMin, nDelta, c = c32(); + uint32_t nMax, nMin, nDelta, c = c32(); int R = R32(c), G = G32(c), B = B32(c); double fHue; nMax = MAX(R, MAX(G, B)); nMin = MIN(R, MIN(G, B)); if (nMax == nMin) return false; hls.l = (nMax + nMin) / 2; if (hls.l < 128) hls.s = (uchar) ((255.0 * ((double)(nMax - nMin)) / (double)(nMax + nMin)) + 0.5); else hls.s = (uchar) ((255.0 * ((double)(nMax - nMin)) / (double)(511 - nMax - nMin)) + 0.5); nDelta = nMax - nMin; if (R == nMax) fHue = ((double) (G - B)) / (double) nDelta; else if (G == nMax) fHue = 2.0 + ((double) (B - R)) / (double) nDelta; else fHue = 4.0 + ((double) (R - G)) / (double) nDelta; fHue *= 60; if (fHue < 0.0) fHue += 360.0; hls.h = (uint16) (fHue + 0.5); space = CsHls32; pal = NULL; return true; } -void GColour::SetHLS(uint16 h, uint8 l, uint8 s) +void GColour::SetHLS(uint16 h, uint8_t l, uint8_t s) { space = CsHls32; hls.h = h; hls.l = l; hls.s = s; } void GColour::ToRGB() { GHls32 Hls = hls; if (Hls.s == 0) { Rgb(0, 0, 0); } else { while (Hls.h >= 360) Hls.h -= 360; while (hls.h < 0) Hls.h += 360; double fHue = (double) Hls.h, fM1, fM2; double fLightness = ((double) Hls.l) / 255.0; double fSaturation = ((double) Hls.s) / 255.0; if (Hls.l < 128) fM2 = fLightness * (1 + fSaturation); else fM2 = fLightness + fSaturation - (fLightness * fSaturation); fM1 = 2.0 * fLightness - fM2; Rgb(HlsValue(fM1, fM2, fHue + 120.0), HlsValue(fM1, fM2, fHue), HlsValue(fM1, fM2, fHue - 120.0)); } } int GColour::GetGray(int BitDepth) { if (BitDepth == 8) { int R = r() * 77; int G = g() * 151; int B = b() * 28; return (R + G + B) >> 8; } double Scale = 1 << BitDepth; int R = (int) ((double) r() * (0.3 * Scale) + 0.5); int G = (int) ((double) g() * (0.59 * Scale) + 0.5); int B = (int) ((double) b() * (0.11 * Scale) + 0.5); return (R + G + B) >> BitDepth; } -uint32 GColour::GetNative() +uint32_t GColour::GetNative() { #ifdef WIN32 if (space == CsIndex8) { if (pal && index < pal->GetSize()) { GdcRGB *c = (*pal)[index]; if (c) { return RGB(c->r, c->g, c->b); } } return RGB(index, index, index); } else if (space == System32BitColourSpace) { return RGB(rgb.r, rgb.g, rgb.b); } else { LgiAssert(0); } #else LgiAssert(0); #endif return c32(); } char *GColour::GetStr() { static char Buf[4][32]; static int Idx = 0; int b = Idx++; if (Idx >= 4) Idx = 0; switch (space) { case System32BitColourSpace: if (rgb.a == 0xff) { sprintf_s( Buf[b], 32, "rgb(%i,%i,%i)", rgb.r, rgb.g, rgb.b); } else { sprintf_s( Buf[b], 32, "rgba(%i,%i,%i,%i)", rgb.r, rgb.g, rgb.b, rgb.a); } break; case CsIndex8: sprintf_s( Buf[b], 32, "index(%i)", index); break; case CsHls32: sprintf_s( Buf[b], 32, "hls(%i,%i,%i)", hls.h, hls.l, hls.s); break; default: sprintf_s( Buf[b], 32, "unknown(%i)", space); break; } return Buf[b]; } bool GColour::SetStr(const char *str) { if (!str) return false; GString Str = str; char *s = strchr(Str, '('); if (!s) return false; char *e = strchr(s + 1, ')'); if (!s) return false; *s = 0; *e = 0; GString::Array Comp = GString(s+1).Split(","); if (!Stricmp(Str.Get(), "rgb")) { if (Comp.Length() == 3) Rgb((int)Comp[0].Int(), (int)Comp[1].Int(), (int)Comp[2].Int()); else if (Comp.Length() == 4) Rgb((int)Comp[0].Int(), (int)Comp[1].Int(), (int)Comp[2].Int(), (int)Comp[3].Int()); else return false; } else if (!Stricmp(Str.Get(), "hls")) { if (Comp.Length() == 3) - SetHLS((uint16) Comp[0].Int(), (uint8) Comp[1].Int(), (uint8) Comp[2].Int()); + SetHLS((uint16) Comp[0].Int(), (uint8_t) Comp[1].Int(), (uint8_t) Comp[2].Int()); else return false; } else if (!Stricmp(Str.Get(), "index")) { if (Comp.Length() == 1) { - index = (uint8) Comp[0].Int(); + index = (uint8_t) Comp[0].Int(); space = CsIndex8; } else return false; } else return false; return true; } diff --git a/src/common/Gdc2/GSurface.cpp b/src/common/Gdc2/GSurface.cpp --- a/src/common/Gdc2/GSurface.cpp +++ b/src/common/Gdc2/GSurface.cpp @@ -1,2036 +1,2036 @@ /*hdr ** FILE: GdcPrim.cpp ** AUTHOR: Matthew Allen ** DATE: 1/3/97 ** DESCRIPTION: GDC v2.xx device independent primitives ** ** Copyright (C) 2001, Matthew Allen ** fret@memecode.com */ #include #include #include #include #include #include "Gdc2.h" #include "GdiLeak.h" #include "GPalette.h" #include "GVariant.h" #define LINE_SOLID 0xFFFFFFFF #ifdef MAC #define REGISTER #else #define REGISTER register #endif void GSurface::Init() { OriginX = OriginY = 0; pMem = NULL; pAlphaDC = 0; Flags = 0; Clip.ZOff(-1, -1); pPalette = NULL; pApp = NULL; ColourSpace = CsNone; for (int i=0; iX(), pDC->Y(), pDC->GetColourSpace())) { Blt(0, 0, pDC); if (pDC->Palette()) { GPalette *Pal = new GPalette(pDC->Palette()); if (Pal) { Palette(Pal, true); } } } } GSurface::~GSurface() { #if defined(LINUX) && !defined(LGI_SDL) if (Cairo) { Gtk::cairo_destroy(Cairo); Cairo = 0; } #endif DrawOnAlpha(false); DeleteObj(pMem); DeleteObj(pAlphaDC); if (pPalette && (Flags & GDC_OWN_PALETTE)) { DeleteObj(pPalette); } if ( (Flags & GDC_OWN_APPLICATOR) && !(Flags & GDC_CACHED_APPLICATOR)) { DeleteObj(pApp); } for (int i=0; iBase) return NULL; GRect clip = r; GRect bounds = Bounds(); clip.Intersection(&bounds); if (!clip.Valid()) return NULL; GAutoPtr s(new GSurface); if (!s) return NULL; int BytePx = pMem->GetBits() >> 3; s->pMem = new GBmpMem; s->pMem->Base = pMem->Base + (pMem->Line * clip.y1) + (BytePx * clip.x1); s->pMem->x = clip.X(); s->pMem->y = clip.Y(); s->pMem->Line = pMem->Line; s->pMem->Cs = pMem->Cs; s->pMem->Flags = 0; // Don't own memory. s->Clip = s->Bounds(); s->ColourSpace = pMem->Cs; s->Op(GDC_SET); return s.Release(); } template -void SetAlphaPm(Px *src, int x, uint8 a) +void SetAlphaPm(Px *src, int x, uint8_t a) { - REG uint8 *Lut = Div255Lut; + REG uint8_t *Lut = Div255Lut; REG Px *s = src; REG Px *e = s + x; while (s < e) { s->r = Lut[s->r * a]; s->g = Lut[s->g * a]; s->b = Lut[s->b * a]; s->a = Lut[s->a * a]; s++; } } template -void SetAlphaNpm(Px *src, int x, uint8 a) +void SetAlphaNpm(Px *src, int x, uint8_t a) { - REG uint8 *Lut = Div255Lut; + REG uint8_t *Lut = Div255Lut; REG Px *s = src; REG Px *e = s + x; while (s < e) { s->a = Lut[s->a * a]; s++; } } -bool GSurface::SetConstantAlpha(uint8 Alpha) +bool GSurface::SetConstantAlpha(uint8_t Alpha) { bool HasAlpha = GColourSpaceHasAlpha(GetColourSpace()); if (!HasAlpha) return false; if (!pMem || !pMem->Base) return false; for (int y=0; yy; y++) { - uint8 *src = pMem->Base + (y * pMem->Line); + uint8_t *src = pMem->Base + (y * pMem->Line); if (pMem->PreMul()) { switch (pMem->Cs) { #define SetAlphaCase(px) \ case Cs##px: SetAlphaPm((G##px*)src, pMem->x, Alpha); break SetAlphaCase(Rgba32); SetAlphaCase(Bgra32); SetAlphaCase(Argb32); SetAlphaCase(Abgr32); #undef SetAlphaCase default: LgiAssert(!"Unknown CS."); break; } } else { switch (pMem->Cs) { #define SetAlphaCase(px) \ case Cs##px: SetAlphaNpm((G##px*)src, pMem->x, Alpha); break SetAlphaCase(Rgba32); SetAlphaCase(Bgra32); SetAlphaCase(Argb32); SetAlphaCase(Abgr32); #undef SetAlphaCase default: LgiAssert(!"Unknown CS."); break; } } } return true; } OsBitmap GSurface::GetBitmap() { #if WINNATIVE return hBmp; #else return NULL; #endif } OsPainter GSurface::Handle() { #if WINNATIVE return hDC; #elif defined(__GTK_H__) return Cairo; #else return 0; #endif } uchar *GSurface::operator[](int y) { if (pMem && pMem->Base && y >= 0 && y < pMem->y) { return pMem->Base + (pMem->Line * y); } return 0; } void GSurface::Set(int x, int y) { OrgXy(x, y); if (x >= Clip.x1 && y >= Clip.y1 && x <= Clip.x2 && y <= Clip.y2) { pApp->SetPtr(x, y); pApp->Set(); Update(GDC_BITS_CHANGE); } } COLOUR GSurface::Get(int x, int y) { OrgXy(x, y); if (x >= Clip.x1 && y >= Clip.y1 && x <= Clip.x2 && y <= Clip.y2) { pApp->SetPtr(x, y); return pApp->Get(); } return (COLOUR)-1; } void GSurface::HLine(int x1, int x2, int y) { OrgXy(x1, y); OrgX(x2); if (x1 > x2) LgiSwap(x1, x2); if (x1 < Clip.x1) x1 = Clip.x1; if (x2 > Clip.x2) x2 = Clip.x2; if (x1 <= x2 && y >= Clip.y1 && y <= Clip.y2) { pApp->SetPtr(x1, y); if (LineBits == LINE_SOLID) { pApp->Rectangle(x2 - x1 + 1, 1); Update(GDC_BITS_CHANGE); } else { for (; x1 <= x2; x1++) { if (LineMask & LineBits) { pApp->Set(); } LineMask >>= 1; if (!LineMask) LineMask = LineReset; pApp->IncX(); } Update(GDC_BITS_CHANGE); } } } void GSurface::VLine(int x, int y1, int y2) { OrgXy(x, y1); OrgY(y2); if (y1 > y2) LgiSwap(y1, y2); if (y1 < Clip.y1) y1 = Clip.y1; if (y2 > Clip.y2) y2 = Clip.y2; if (y1 <= y2 && x >= Clip.x1 && x <= Clip.x2) { pApp->SetPtr(x, y1); if (LineBits == LINE_SOLID) { pApp->VLine(y2 - y1 + 1); Update(GDC_BITS_CHANGE); } else { for (; y1 <= y2; y1++) { if (LineMask & LineBits) { pApp->Set(); } LineMask >>= 1; if (!LineMask) LineMask = LineReset; pApp->IncY(); } Update(GDC_BITS_CHANGE); } } } void GSurface::Line(int x1, int y1, int x2, int y2) { if (x1 == x2) { VLine(x1, y1, y2); } else if (y1 == y2) { HLine(x1, x2, y1); } else if (Clip.Valid()) { OrgXy(x1, y1); OrgXy(x2, y2); // angled if (y1 > y2) { LgiSwap(y1, y2); LgiSwap(x1, x2); } GRect Bound(x1, y1, x2, y2); Bound.Normal(); if (!Bound.Overlap(&Clip)) return; double m = (double) (y2-y1) / (x2-x1); double b = (double) y1 - (m*x1); int xt = (int) (((double)Clip.y1-b)/m); int xb = (int) (((double)Clip.y2-b)/m); if (y1 < Clip.y1) { x1 = xt; y1 = Clip.y1; } if (y2 > Clip.y2) { x2 = xb; y2 = Clip.y2; } int X1, X2; if (x1 < x2) { X1 = x1; X2 = x2; } else { X1 = x2; X2 = x1; } if (X1 < Clip.x1) { if (X2 < Clip.x1) return; if (x1 < Clip.x1) { y1 = (int) (((double) Clip.x1 * m) + b); x1 = Clip.x1; } else { y2 = (int) (((double) Clip.x1 * m) + b); x2 = Clip.x1; } } if (X2 > Clip.x2) { if (X1 > Clip.x2) return; if (x1 > Clip.x2) { y1 = (int) (((double) Clip.x2 * m) + b); x1 = Clip.x2; } else { y2 = (int) (((double) Clip.x2 * m) + b); x2 = Clip.x2; } } int dx = abs(x2-x1); int dy = abs(y2-y1); int EInc, ENoInc, E, Inc = 1; // printf("Dx: %i\nDy: %i\n", dx, dy); if (dy < dx) { // flat if (x1 > x2) { LgiSwap(x1, x2); LgiSwap(y1, y2); } if (y1 > y2) { Inc = -1; } EInc = dy + dy; E = EInc - dx; ENoInc = E - dx; pApp->SetPtr(x1, y1); if (LineBits == LINE_SOLID) { for (; x1<=x2; x1++) { pApp->Set(); if (E < 0) { pApp->IncX(); E += EInc; } else { pApp->IncPtr(1, Inc); E += ENoInc; } } } else { for (; x1<=x2; x1++) { if (LineBits & LineMask) { pApp->Set(); } LineMask >>= 1; if (!LineMask) LineMask = LineReset; if (E < 0) { pApp->IncX(); E += EInc; } else { pApp->IncPtr(1, Inc); E += ENoInc; } } } } else { if (x1 > x2) { Inc = -1; } // steep EInc = dx + dx; E = EInc - dy; ENoInc = E - dy; pApp->SetPtr(x1, y1); if (LineBits == LINE_SOLID) { for (; y1<=y2; y1++) { pApp->Set(); if (E < 0) { pApp->IncY(); E += EInc; } else { pApp->IncPtr(Inc, 1); E += ENoInc; } } } else { for (; y1<=y2; y1++) { if (LineBits & LineMask) { pApp->Set(); } LineMask >>= 1; if (!LineMask) LineMask = LineReset; if (E < 0) { pApp->IncY(); E += EInc; } else { pApp->IncPtr(Inc, 1); E += ENoInc; } } } } Update(GDC_BITS_CHANGE); } } void GSurface::Circle(double Cx, double Cy, double radius) { int cx = (int)Cx; int cy = (int)Cy; int d = (int) (3 - (2 * radius)); int x = 0; int y = (int) radius; Set(cx, cy + y); Set(cx, cy - y); Set(cx + y, cy); Set(cx - y, cy); if (d < 0) { d += (4 * x) + 6; } else { d += (4 * (x - y)) + 10; y--; } x++; while (x < y) { Set(cx + x, cy + y); Set(cx - x, cy + y); Set(cx + x, cy - y); Set(cx - x, cy - y); Set(cx + y, cy + x); Set(cx - y, cy + x); Set(cx + y, cy - x); Set(cx - y, cy - x); if (d < 0) { d += (4 * x) + 6; } else { d += (4 * (x - y)) + 10; y--; } x++; } if (x == y) { Set(cx + x, cy + x); Set(cx - x, cy + x); Set(cx + x, cy - x); Set(cx - x, cy - x); } Update(GDC_BITS_CHANGE); } void GSurface::FilledCircle(double Cx, double Cy, double radius) { int cx = (int)Cx; int cy = (int)Cy; int d = (int) (3 - 2 * radius); int x = 0; int y = (int) radius; // HLine(cx + y, cx - y, cy); if (d < 0) { d += 4 * x + 6; } else { HLine(cx, cx, cy + y); d += 4 * (x - y) + 10; y--; } while (x < y) { HLine(cx + y, cx - y, cy + x); if (x != 0) HLine(cx + y, cx - y, cy - x); if (d < 0) { d += 4 * x + 6; } else { HLine(cx + x, cx - x, cy + y); HLine(cx + x, cx - x, cy - y); d += 4 * (x - y) + 10; y--; } x++; } if (x == y) { HLine(cx + y, cx - y, cy + x); HLine(cx + y, cx - y, cy - x); } Update(GDC_BITS_CHANGE); } void GSurface::Box(GRect *a) { if (a) { GRect b = *a; b.Normal(); if (b.x1 != b.x2 && b.y1 != b.y2) { HLine(b.x1, b.x2 - 1, b.y1); VLine(b.x2, b.y1, b.y2 - 1); HLine(b.x1 + 1, b.x2, b.y2); VLine(b.x1, b.y1 + 1, b.y2); } else { Set(b.x1, b.y1); } } else { GRect b(0, 0, X()-1, Y()-1); HLine(b.x1, b.x2 - 1, b.y1); VLine(b.x2, b.y1, b.y2 - 1); HLine(b.x1 + 1, b.x2, b.y2); VLine(b.x1, b.y1 + 1, b.y2); } } void GSurface::Box(int x1, int y1, int x2, int y2) { GRect a(x1, y1, x2, y2); Box(&a); } void GSurface::Rectangle(GRect *a) { GRect b; if (a) b = a; else b.ZOff(pMem->x-1, pMem->y-1); OrgRgn(b); b.Normal(); b.Bound(&Clip); if (b.Valid()) { pApp->SetPtr(b.x1, b.y1); pApp->Rectangle(b.X(), b.Y()); Update(GDC_BITS_CHANGE); } } void GSurface::Rectangle(int x1, int y1, int x2, int y2) { GRect a(x1, y1, x2, y2); Rectangle(&a); } void GSurface::Ellipse(double Cx, double Cy, double A, double B) { #define incx() x++, dxt += d2xt, t += dxt #define incy() y--, dyt += d2yt, t += dyt int x = 0, y = (int)B; int a = (int)A; if (a % 2 == 0) a--; int b = (int)B; int xc = (int)Cx; int yc = (int)Cy; long a2 = (long)(a*a), b2 = (long)(b*b); long crit1 = -(a2/4 + (int)a%2 + b2); long crit2 = -(b2/4 + b%2 + a2); long crit3 = -(b2/4 + b%2); long t = -a2*y; /* e(x+1/2,y-1/2) - (a^2+b^2)/4 */ long dxt = 2*b2*x, dyt = -2*a2*y; long d2xt = 2*b2, d2yt = 2*a2; if (a % 2) { // Odd version while (y>=0 && x<=a) { Set(xc+x, yc+y); if (x!=0 || y!=0) Set(xc-x, yc-y); if (x!=0 && y!=0) { Set(xc+x, yc-y); Set(xc-x, yc+y); } if (t + b2*x <= crit1 || /* e(x+1,y-1/2) <= 0 */ t + a2*y <= crit3) /* e(x+1/2,y) <= 0 */ incx(); else if (t - a2*y > crit2) /* e(x+1/2,y-1) > 0 */ incy(); else { incx(); incy(); } } } else // even version { while (y>=0 && x<=a) { Set(xc+x+1, yc+y); Set(xc+x+1, yc-y); Set(xc-x, yc-y); Set(xc-x, yc+y); if (t + b2*x <= crit1 || /* e(x+1,y-1/2) <= 0 */ t + a2*y <= crit3) /* e(x+1/2,y) <= 0 */ incx(); else if (t - a2*y > crit2) /* e(x+1/2,y-1) > 0 */ incy(); else { incx(); incy(); } } } Update(GDC_BITS_CHANGE); } void GSurface::FilledEllipse(double Cx, double Cy, double Width, double Height) { #if 0 double Width2 = Width * Width; double Height2 = Height * Height; int end_y = (int) ceil(Cy + Height); for (int y = (int) floor(Cy - Height); y <= end_y; y++) { double Y = (double) y + 0.5; double p2 = (Y - Cy); p2 = (p2 * p2) / Height2; double p3 = Width2 * (1 - p2); if (p3 > 0.0) { double X = sqrt( p3 ); Line((int) floor(Cx - x + 0.5), y, (int) ceil(Cx + x - 0.5), y); } } #else // TODO: fix this primitive for odd widths and heights int cx = (int)Cx; int cy = (int)Cy; /* a = floor(a); b = floor(b); */ long aSq = (long) (Width * Width); long bSq = (long) (Height * Height); long two_aSq = aSq+aSq; long two_bSq = bSq+bSq; long x=0, y=(long)Height, two_xBsq = 0, two_yAsq = y * two_aSq, error = -y * aSq; if (aSq && bSq && error) { while (two_xBsq <= two_yAsq) { x++; two_xBsq += two_bSq; error += two_xBsq - bSq; if (error >= 0) { Line((int) (cx+x-1), (int) (cy+y), (int) (cx-x+1), (int) (cy+y)); if (y != 0) Line((int) (cx+x-1), (int) (cy-y), (int) (cx-x+1), (int) (cy-y)); y--; two_yAsq -= two_aSq; error -= two_yAsq; } } x=(long)Width; y=0; two_xBsq = x * two_bSq; two_yAsq = 0; error = -x * bSq; while (two_xBsq > two_yAsq) { Line( (int) (cx+x), (int) (cy+y), (int) (cx-x), (int) (cy+y)); if (y != 0) Line((int) (cx+x), (int) (cy-y), (int) (cx-x), (int) (cy-y)); y++; two_yAsq += two_aSq; error += two_yAsq - aSq; if (error >= 0) { x--; two_xBsq -= two_bSq; error -= two_xBsq; } } } #endif Update(GDC_BITS_CHANGE); } struct EDGE { int yMin, yMax, x, dWholeX, dX, dY, frac; }; int nActive, nNextEdge; void GSurface::Polygon(int nPoints, GdcPt2 *aPoints) { GdcPt2 p0, p1; int i, j, gap, x0, x1, y, nEdges; EDGE *ET, **GET, **AET; /******************************************************************** * Add entries to the global edge table. The global edge table has a * bucket for each scan line in the polygon. Each bucket contains all * the edges whose yMin == yScanline. Each bucket contains the yMax, * the x coordinate at yMax, and the denominator of the slope (dX) */ // allocate the tables ET = new EDGE[nPoints]; GET = new EDGE*[nPoints]; AET = new EDGE*[nPoints]; if (ET && GET && AET) { for ( i = 0, nEdges = 0; i < nPoints; i++) { p0 = aPoints[i]; p1 = aPoints[(i+1) % nPoints]; // ignore if this is a horizontal edge if (p0.y == p1.y) continue; // swap points if necessary to ensure p0 contains yMin if (p0.y > p1.y) { p0 = p1; p1 = aPoints[i]; } // create the new edge ET[nEdges].yMin = p0.y; ET[nEdges].yMax = p1.y; ET[nEdges].x = p0.x; ET[nEdges].dX = p1.x - p0.x; ET[nEdges].dY = p1.y - p0.y; ET[nEdges].frac = 0; GET[nEdges] = &ET[nEdges]; nEdges++; } // sort the GET on yMin for (gap = 1; gap < nEdges; gap = 3*gap + 1); for (gap /= 3; gap > 0; gap /= 3) for (i = gap; i < nEdges; i++) for (j = i-gap; j >= 0; j -= gap) { if (GET[j]->yMin <= GET[j+gap]->yMin) break; EDGE *t = GET[j]; GET[j] = GET[j+gap]; GET[j+gap] = t; } // initialize the active edge table, and set y to first entering edge nActive = 0; nNextEdge = 0; y = GET[nNextEdge]->yMin; /* Now process the edges using the scan line algorithm. Active edges will be added to the Active Edge Table (AET), and inactive edges will be deleted. X coordinates will be updated with incremental integer arithmetic using the slope (dY / dX) of the edges. */ while (nNextEdge < nEdges || nActive) { /* Move from the ET bucket y to the AET those edges whose yMin == y (entering edges) */ while (nNextEdge < nEdges && GET[nNextEdge]->yMin == y) AET[nActive++] = GET[nNextEdge++]; /* Remove from the AET those entries for which yMax == y (leaving edges) */ i = 0; while (i < nActive) { if (AET[i]->yMax == y) memmove(&AET[i], &AET[i+1], sizeof(AET[0]) * (--nActive - i)); else i++; } /* Now sort the AET on x. Since the list is usually quite small, the sort is implemented as a simple non-recursive shell sort */ for (gap = 1; gap < nActive; gap = 3*gap + 1); for (gap /= 3; gap > 0; gap /= 3) for (i = gap; i < nActive; i++) for (j = i-gap; j >= 0; j -= gap) { if (AET[j]->x <= AET[j+gap]->x) break; EDGE *t = AET[j]; AET[j] = AET[j+gap]; AET[j+gap] = t; } /* Fill in desired pixels values on scan line y by using pairs of x coordinates from the AET */ for (i = 0; i < nActive; i += 2) { x0 = AET[i]->x; x1 = AET[i+1]->x; /* Left edge adjustment for positive fraction. 0 is interior. */ if (AET[i]->frac > 0) x0++; // Right edge adjustment for negative fraction. 0 is exterior. */ if (AET[i+1]->frac <= 0) x1--; // Draw interior spans if (x1 >= x0) HLine(x0, x1, y); } /* Update all the x coordinates. Edges are scan converted using a modified midpoint algorithm (Bresenham's algorithm reduces to the midpoint algorithm for two dimensional lines) */ for (i = 0; i < nActive; i++) { EDGE *e = AET[i]; // update the fraction by dX e->frac += e->dX; if (e->dX < 0) while ( -(e->frac) >= e->dY) { e->frac += e->dY; e->x--; } else while (e->frac >= e->dY) { e->frac -= e->dY; e->x++; } } y++; } } // Release tables DeleteArray(ET); DeleteArray(GET); DeleteArray(AET); } void GSurface::Blt(int x, int y, GSurface *Src, GRect *a) { OrgXy(x, y); if (Src && Src->pMem && Src->pMem->Base) { GRect S; if (a) S = *a; else S.ZOff(Src->X()-1, Src->Y()-1); S.Offset(Src->OriginX, Src->OriginY); GRect SClip = S; SClip.Bound(&Src->Clip); if (SClip.Valid()) { GRect D = SClip; D.Offset(x-S.x1, y-S.y1); GRect DClip = D; DClip.Bound(&Clip); GRect Re = DClip; Re.Offset(S.x1-x, S.y1-y); SClip.Bound(&Re); if (DClip.Valid() && SClip.Valid()) { GBmpMem Bits, Alpha; int PixelBytes = GColourSpaceToBits(Src->pMem->Cs) >> 3; Bits.Base = Src->pMem->Base + (SClip.y1 * Src->pMem->Line) + (SClip.x1 * PixelBytes); Bits.x = MIN(SClip.X(), DClip.X()); Bits.y = MIN(SClip.Y(), DClip.Y()); Bits.Line = Src->pMem->Line; Bits.Cs = Src->GetColourSpace(); Bits.PreMul(Src->pMem->PreMul()); if (Src->pAlphaDC && !Src->DrawOnAlpha()) { GBmpMem *ASurface = Src->pAlphaDC->pMem; Alpha = Bits; Alpha.Cs = CsIndex8; Alpha.Line = ASurface->Line; Alpha.Base = ASurface->Base + (SClip.y1 * ASurface->Line) + (SClip.x1); } pApp->SetPtr(DClip.x1, DClip.y1); GPalette *SrcPal = Src->DrawOnAlpha() ? NULL : Src->Palette(); // printf("\t%p::Blt pApp=%p, %s\n", this, pApp, pApp->GetClass()); pApp->Blt(&Bits, SrcPal, Alpha.Base ? &Alpha : NULL); Update(GDC_BITS_CHANGE); if (pApp->GetFlags() & GDC_UPDATED_PALETTE) { Palette(new GPalette(pApp->GetPal())); } } } } } #define sqr(a) ((a)*(a)) #define Distance(a, b) (sqrt(sqr(a.x-b.x)+sqr(a.y-b.y))) class FPt2 { public: double x, y; }; typedef FPt2 BPt; void GSurface::Bezier(int Threshold, GdcPt2 *Pt) { if (Pt) { int OldPts = 3; int NewPts = 0; BPt *BufA = new BPt[1024]; BPt *BufB = new BPt[1024]; BPt *Old = BufA; BPt *New = BufB; Threshold = MAX(Threshold, 1); if (!Old || !New) return; for (int n=0; n Threshold); if (Threshold > 1) { for (int i=0; i= Size) { SetSize(Size+1024); } if (Stack) { Stack[Used].x = x; Stack[Used].y = y; Used++; } } void Pop(int &x, int &y) { if (Stack && Used > 0) { Used--; x = Stack[Used].x; y = Stack[Used].y; } } }; // This should return true if 'Pixel' is in the region being filled. typedef bool (*FillMatchProc)(COLOUR Seed, COLOUR Pixel, COLOUR Border, int Bits); bool FillMatch_Diff(COLOUR Seed, COLOUR Pixel, COLOUR Border, int Bits) { return Seed == Pixel; } bool FillMatch_Near(COLOUR Seed, COLOUR Pixel, COLOUR Border, int Bits) { COLOUR s24 = CBit(24, Seed, Bits); COLOUR p24 = CBit(24, Pixel, Bits); int Dr = R24(s24) - R24(p24); int Dg = G24(s24) - G24(p24); int Db = B24(s24) - B24(p24); return ((unsigned)abs(Dr) < Border) && ((unsigned)abs(Dg) < Border) && ((unsigned)abs(Db) < Border); } void GSurface::FloodFill(int StartX, int StartY, int Mode, COLOUR Border, GRect *FillBounds) { COLOUR Seed = Get(StartX, StartY); if (Seed == 0xffffffff) return; // Doesn't support get pixel PointStack Ps; GRect Bounds; FillMatchProc Proc = 0; int Bits = GetBits(); Bounds.x1 = X(); Bounds.y1 = Y(); Bounds.x2 = 0; Bounds.y2 = 0; Ps.Push(StartX, StartY); switch (Mode) { case GDC_FILL_TO_DIFFERENT: { Proc = FillMatch_Diff; break; } case GDC_FILL_TO_BORDER: { break; } case GDC_FILL_NEAR: { Proc = FillMatch_Near; break; } } if (Proc) { COLOUR Start = Colour(); if (!Proc(Seed, Start, Border, Bits)) { while (Ps.GetSize() > 0) { bool Above = true; bool Below = true; int Ox, Oy; Ps.Pop(Ox, Oy); int x = Ox, y = Oy; // move right loop COLOUR c = Get(x, y); while (x < X() && Proc(Seed, c, Border, Bits)) { Set(x, y); Bounds.Union(x, y); if (y > 0) { c = Get(x, y - 1); if (Above) { if (Proc(Seed, c, Border, Bits)) { Ps.Push(x, y - 1); Above = false; } } else if (!Proc(Seed, c, Border, Bits)) { Above = true; } } if (y < Y() - 1) { c = Get(x, y + 1); if (Below) { if (Proc(Seed, c, Border, Bits)) { Ps.Push(x, y + 1); Below = false; } } else if (!Proc(Seed, c, Border, Bits)) { Below = true; } } x++; c = Get(x, y); } // move left loop x = Ox; Above = !((y > 0) && (Get(x, y - 1) == Seed)); Below = !((y < Y() - 1) && (Get(x, y + 1) == Seed)); x--; c = Get(x, y); while (x >= 0 && Proc(Seed, c, Border, Bits)) { Set(x, y); Bounds.Union(x, y); if (y > 0) { c = Get(x, y - 1); if (Above) { if (Proc(Seed, c, Border, Bits)) { Ps.Push(x, y - 1); Above = false; } } else if (!Proc(Seed, c, Border, Bits)) { Above = true; } } if (y < Y() - 1) { c = Get(x, y + 1); if (Below) { if (Proc(Seed, c, Border, Bits)) { Ps.Push(x, y + 1); Below = false; } } else if (!Proc(Seed, c, Border, Bits)) { Below = true; } } x--; c = Get(x, y); } } } } if (FillBounds) { *FillBounds = Bounds; } Update(GDC_BITS_CHANGE); } void GSurface::Arc(double cx, double cy, double radius, double start, double end) {} void GSurface::FilledArc(double cx, double cy, double radius, double start, double end) {} void GSurface::StretchBlt(GRect *d, GSurface *Src, GRect *s) {} bool GSurface::HasAlpha(bool b) { DrawOnAlpha(false); if (b) { if (!pAlphaDC) { pAlphaDC = new GMemDC; } if (pAlphaDC && pMem) { if (!pAlphaDC->Create(pMem->x, pMem->y, CsIndex8)) { DeleteObj(pAlphaDC); } else { ClearFlag(Flags, GDC_DRAW_ON_ALPHA); } } } else { DeleteObj(pAlphaDC); } return (b == HasAlpha()); } bool GSurface::DrawOnAlpha(bool Draw) { bool Prev = DrawOnAlpha(); bool Swap = false; if (Draw) { if (!Prev && pAlphaDC && pMem) { Swap = true; SetFlag(Flags, GDC_DRAW_ON_ALPHA); PrevOp = Op(GDC_SET); } } else { if (Prev && pAlphaDC && pMem) { Swap = true; ClearFlag(Flags, GDC_DRAW_ON_ALPHA); Op(PrevOp); } } if (Swap) { GBmpMem *Temp = pMem; pMem = pAlphaDC->pMem; pAlphaDC->pMem = Temp; #if WINNATIVE OsBitmap hTmp = hBmp; hBmp = pAlphaDC->hBmp; pAlphaDC->hBmp = hTmp; OsPainter hP = hDC; hDC = pAlphaDC->hDC; pAlphaDC->hDC = hP; #endif } return Prev; } GApplicator *GSurface::CreateApplicator(int Op, GColourSpace Cs) { GApplicator *pA = NULL; if (!Cs) { if (pMem) { if (DrawOnAlpha()) { Cs = CsIndex8; } else if (pMem->Cs) { Cs = pMem->Cs; } else { LgiAssert(!"Memory context has no colour space..."); } } else if (IsScreen()) { Cs = GdcD->GetColourSpace(); } else { LgiAssert(!"No memory context to read colour space from."); } } pA = GApplicatorFactory::NewApp(Cs, Op); if (pA) { if (DrawOnAlpha()) { pA->SetSurface(pMem); } else { pA->SetSurface(pMem, pPalette, (pAlphaDC) ? pAlphaDC->pMem : 0); } pA->SetOp(Op); } else { GApplicatorFactory::NewApp(Cs, Op); const char *CsStr = GColourSpaceToString(Cs); LgiTrace("Error: GDeviceContext::CreateApplicator(%i, %x, %s) failed.\n", Op, Cs, CsStr); LgiAssert(!"No applicator"); } return pA; } bool GSurface::Applicator(GApplicator *pApplicator) { bool Status = false; if (pApplicator) { if (Flags & GDC_OWN_APPLICATOR) { DeleteObj(pApp) Flags &= ~GDC_OWN_APPLICATOR; } Flags &= ~GDC_CACHED_APPLICATOR; pApp = pApplicator; if (DrawOnAlpha()) { pApp->SetSurface(pMem); } else { pApp->SetSurface(pMem, pPalette, pAlphaDC->pMem); } pApp->SetPtr(0, 0); Status = true; } return Status; } GApplicator *GSurface::Applicator() { return pApp; } GRect GSurface::ClipRgn(GRect *Rgn) { GRect Old = Clip; if (Rgn) { Clip.x1 = MAX(0, Rgn->x1 - OriginX); Clip.y1 = MAX(0, Rgn->y1 - OriginY); Clip.x2 = MIN(X()-1, Rgn->x2 - OriginX); Clip.y2 = MIN(Y()-1, Rgn->y2 - OriginY); } else { Clip.x1 = 0; Clip.y1 = 0; Clip.x2 = X()-1; Clip.y2 = Y()-1; } return Old; } GRect GSurface::ClipRgn() { return Clip; } GColour GSurface::Colour(GColour c) { LgiAssert(pApp != NULL); GColour cPrev(pApp->c, GetBits()); - uint32 c32 = c.c32(); + uint32_t c32 = c.c32(); GColourSpace Cs = pApp->GetColourSpace(); switch (Cs) { case CsIndex8: if (c.GetColourSpace() == CsIndex8) { pApp->c = c.c8(); } else { GPalette *p = Palette(); if (p) // Colour pApp->c = p->MatchRgb(Rgb32To24(c32)); else // Grey scale pApp->c = c.GetGray(); } break; case CsRgb15: case CsBgr15: pApp->c = Rgb32To15(c32); break; case CsRgb16: case CsBgr16: pApp->c = Rgb32To16(c32); break; case CsRgba32: case CsBgra32: case CsArgb32: case CsAbgr32: case CsRgbx32: case CsBgrx32: case CsXrgb32: case CsXbgr32: case CsRgba64: case CsBgra64: case CsArgb64: case CsAbgr64: pApp->p32.r = R32(c32); pApp->p32.g = G32(c32); pApp->p32.b = B32(c32); pApp->p32.a = A32(c32); break; case CsRgb24: case CsBgr24: case CsRgb48: case CsBgr48: pApp->p24.r = R32(c32); pApp->p24.g = G32(c32); pApp->p24.b = B32(c32); break; default: LgiAssert(0); break; } #if defined(MAC) && !defined(LGI_SDL) { // Update the current colour of the drawing context if present. OsPainter Hnd = Handle(); if (Hnd) { float r = (float)c.r()/255.0; float g = (float)c.g()/255.0; float b = (float)c.b()/255.0; float a = (float)c.a()/255.0; CGContextSetRGBFillColor(Hnd, r, g, b, a); CGContextSetRGBStrokeColor(Hnd, r, g, b, a); } } #endif return cPrev; } COLOUR GSurface::Colour(COLOUR c, int Bits) { GColour n(c, Bits ? Bits : GetBits()); GColour Prev = Colour(n); switch (GetBits()) { case 8: return Prev.c8(); case 24: return Prev.c24(); case 32: return Prev.c32(); } return Prev.c32(); } int GSurface::Op(int NewOp, NativeInt Param) { int PrevOp = (pApp) ? pApp->GetOp() : GDC_SET; if (!pApp || PrevOp != NewOp) { COLOUR cCurrent = (pApp) ? Colour() : 0; if (Flags & GDC_OWN_APPLICATOR) { DeleteObj(pApp); } if (NewOp < GDC_CACHE_SIZE && !DrawOnAlpha()) { pApp = (pAppCache[NewOp]) ? pAppCache[NewOp] : pAppCache[NewOp] = CreateApplicator(NewOp, GetColourSpace()); Flags &= ~GDC_OWN_APPLICATOR; Flags |= GDC_CACHED_APPLICATOR; } else { pApp = CreateApplicator(NewOp, GetColourSpace()); Flags &= ~GDC_CACHED_APPLICATOR; Flags |= GDC_OWN_APPLICATOR; } if (pApp) { Colour(cCurrent); if (Param >= 0) pApp->SetVar(GAPP_ALPHA_A, Param); } else { printf("Error: Couldn't create applicator, Op=%i\n", NewOp); LgiAssert(0); } } return PrevOp; } GPalette *GSurface::Palette() { if (!pPalette && pMem && (pMem->Flags & GDC_ON_SCREEN) && pMem->Cs == CsIndex8) { pPalette = GdcD->GetSystemPalette(); // printf("Setting sys palette: %p\n", pPalette); if (pPalette) { Flags &= ~GDC_OWN_PALETTE; } } return pPalette; } void GSurface::Palette(GPalette *pPal, bool bOwnIt) { if (pPal == pPalette) { LgiTrace("%s:%i - Palette setting itself.\n", _FL); return; } // printf("GSurface::Palette %p %i\n", pPal, bOwnIt); if (pPalette && (Flags & GDC_OWN_PALETTE) != 0) { // printf("\tdel=%p\n", pPalette); delete pPalette; } pPalette = pPal; if (pPal && bOwnIt) { Flags |= GDC_OWN_PALETTE; // printf("\tp=%p own\n", pPalette); } else { Flags &= ~GDC_OWN_PALETTE; // printf("\tp=%p not-own\n", pPalette); } if (pApp) { pApp->SetSurface(pMem, pPalette, (pAlphaDC) ? pAlphaDC->pMem : 0); } } bool GSurface::GetVariant(const char *Name, GVariant &Dst, char *Array) { switch (LgiStringToDomProp(Name)) { case SurfaceX: // Type: Int32 { Dst = X(); break; } case SurfaceY: // Type: Int32 { Dst = Y(); break; } case SurfaceBits: // Type: Int32 { Dst = GetBits(); break; } case SurfaceColourSpace: // Type: Int32 { Dst = GetColourSpace(); break; } case SurfaceIncludeCursor: // Type: Int32 { Dst = TestFlag(Flags, GDC_CAPTURE_CURSOR); break; } default: { return false; } } return true; } bool GSurface::SetVariant(const char *Name, GVariant &Value, char *Array) { switch (LgiStringToDomProp(Name)) { case SurfaceIncludeCursor: { if (Value.CastInt32()) SetFlag(Flags, GDC_CAPTURE_CURSOR); else ClearFlag(Flags, GDC_CAPTURE_CURSOR); break; } default: break; } return false; } bool GSurface::CallMethod(const char *Name, GVariant *ReturnValue, GArray &Args) { return false; } bool GSurface::IsPreMultipliedAlpha() { return pMem ? pMem->PreMul() : false; } template void ConvertToPreMul(Px *src, int x) { REGISTER uchar *DivLut = Div255Lut; REGISTER Px *s = src; REGISTER Px *e = s + x; while (s < e) { s->r = DivLut[s->r * s->a]; s->g = DivLut[s->g * s->a]; s->b = DivLut[s->b * s->a]; s++; } } template void ConvertFromPreMul(Px *src, int x) { // REGISTER uchar *DivLut = Div255Lut; REGISTER Px *s = src; REGISTER Px *e = s + x; while (s < e) { if (s->a > 0 && s->a < 255) { s->r = (int) s->r * 255 / s->a; s->g = (int) s->g * 255 / s->a; s->b = (int) s->b * 255 / s->a; } s++; } } bool GSurface::ConvertPreMulAlpha(bool ToPreMul) { if (!pMem || !pMem->Base) return false; for (int y=0; yy; y++) { - uint8 *src = pMem->Base + (y * pMem->Line); + uint8_t *src = pMem->Base + (y * pMem->Line); if (ToPreMul) { switch (pMem->Cs) { #define ToPreMulCase(px) \ case Cs##px: ConvertToPreMul((G##px*)src, pMem->x); break ToPreMulCase(Rgba32); ToPreMulCase(Bgra32); ToPreMulCase(Argb32); ToPreMulCase(Abgr32); #undef ToPreMulCase default: break; } } else { switch (pMem->Cs) { #define FromPreMulCase(px) \ case Cs##px: ConvertFromPreMul((G##px*)src, pMem->x); break FromPreMulCase(Rgba32); FromPreMulCase(Bgra32); FromPreMulCase(Argb32); FromPreMulCase(Abgr32); #undef FromPreMulCase default: break; } } } pMem->PreMul(ToPreMul); return true; } template void MakeOpaqueRop32(Px *in, int len) { REGISTER Px *s = in; REGISTER Px *e = s + len; while (s < e) { s->a = 0xff; s++; } } template void MakeOpaqueRop64(Px *in, int len) { REGISTER Px *s = in; REGISTER Px *e = s + len; while (s < e) { s->a = 0xffff; s++; } } bool GSurface::MakeOpaque() { if (!pMem || !pMem->Base) return false; for (int y=0; yy; y++) { - uint8 *src = pMem->Base + (y * pMem->Line); + uint8_t *src = pMem->Base + (y * pMem->Line); switch (pMem->Cs) { #define OpaqueCase(px, sz) \ case Cs##px: MakeOpaqueRop##sz((G##px*)src, pMem->x); break OpaqueCase(Rgba32, 32); OpaqueCase(Bgra32, 32); OpaqueCase(Argb32, 32); OpaqueCase(Abgr32, 32); OpaqueCase(Rgba64, 64); OpaqueCase(Bgra64, 64); OpaqueCase(Argb64, 64); OpaqueCase(Abgr64, 64); #undef OpaqueCase default: break; } } return true; } diff --git a/src/common/Gdc2/GdcCommon.cpp b/src/common/Gdc2/GdcCommon.cpp --- a/src/common/Gdc2/GdcCommon.cpp +++ b/src/common/Gdc2/GdcCommon.cpp @@ -1,1174 +1,1174 @@ #include "Lgi.h" #include "GPalette.h" ///////////////////////////////////////////////////////////////////////////// // Mem ops void Add64(ulong *DestH, ulong *DestL, ulong SrcH, ulong SrcL) { *DestH += SrcH + (*DestL & SrcL & 0x80000000) ? 1 : 0; *DestL += SrcL; } void MemSet(void *d, int c, uint s) { if (d && s > 0) memset(d, c, s); } void MemCpy(void *d, void *s, uint l) { if (d && s && l > 0) memcpy(d, s, l); } void MemAnd(void *d, void *s, uint l) { uchar *D = (uchar*) d; uchar *S = (uchar*) s; if (D && S) { for (; l > 0; l--) { *D++ &= *S++; } } } void MemXor(void *d, void *s, uint l) { uchar *D = (uchar*) d; uchar *S = (uchar*) s; if (D && S) { for (; l > 0; l--) { *D++ ^= *S++; } } } void MemOr(void *d, void *s, uint l) { uchar *D = (uchar*) d; uchar *S = (uchar*) s; if (D && S) { for (; l > 0; l--) { *D++ |= *S++; } } } ////////////////////////////////////////////////////////////////////// bool LgiFindBounds(GSurface *pDC, GRect *rc) { if (!pDC || ! rc) return false; LgiAssert(pDC->GetColourSpace() == System32BitColourSpace); // Move top border down to image while (rc->y1 < rc->y2) { System32BitPixel *p = (System32BitPixel*)(*pDC)[rc->y1]; if (!p) return false; p += rc->x1; System32BitPixel *e = p + rc->X(); bool IsTrans = true; while (p < e) { if (p->a != 0) { IsTrans = false; break; } p++; } if (IsTrans) rc->y1++; else break; } // Move bottom border up to image while (rc->y2 >= rc->y1) { System32BitPixel *p = (System32BitPixel*)(*pDC)[rc->y2]; if (!p) return false; p += rc->x1; System32BitPixel *e = p + rc->X(); bool IsTrans = true; while (p < e) { if (p->a != 0) { IsTrans = false; break; } p++; } if (IsTrans) rc->y2--; else break; } // Do the left and right edges too int x1 = rc->x2; int x2 = rc->x1; for (int y=rc->y1; y<=rc->y2; y++) { System32BitPixel *p = (System32BitPixel*)(*pDC)[y]; if (!p) return false; System32BitPixel *px1 = p + rc->x1; System32BitPixel *px2 = p + rc->x2; while (px1 < px2 && px1->a == 0) px1++; x1 = MIN(x1, (int) (px1 - p)); while (px2 >= px1 && px2->a == 0) px2--; x2 = MAX(x2, (int) (px2 - p)); } rc->x1 = x1; rc->x2 = x2; if (rc->Valid()) return true; // No data? rc->ZOff(-1, -1); return false; } ////////////////////////////////////////////////////////////////////// // Drawing functions void LgiDrawBox(GSurface *pDC, GRect &r, bool Sunken, bool Fill) { if (Fill) { pDC->Colour(LC_MED, 24); pDC->Rectangle(r.x1+1, r.y1+1, r.x2-1, r.y2-1); } pDC->Colour((Sunken) ? LC_LIGHT : LC_LOW, 24); pDC->Line(r.x2, r.y2, r.x2, r.y1); pDC->Line(r.x2, r.y2, r.x1, r.y2); pDC->Colour((Sunken) ? LC_LOW : LC_LIGHT, 24); pDC->Line(r.x1, r.y1, r.x1, r.y2); pDC->Line(r.x1, r.y1, r.x2, r.y1); } void LgiWideBorder(GSurface *pDC, GRect &r, LgiEdge Type) { if (!pDC) return; COLOUR Old = pDC->Colour(); COLOUR VLow = LC_SHADOW; COLOUR Low = LC_LOW; COLOUR High = LC_HIGH; COLOUR VHigh = LC_LIGHT; switch (Type) { case EdgeXpSunken: { // XP theme pDC->Colour(Low, 24); pDC->Line(r.x1, r.y1, r.x2-1, r.y1); pDC->Line(r.x1, r.y1, r.x1, r.y2-1); pDC->Colour(VLow, 24); pDC->Line(r.x1+1, r.y1+1, r.x2-2, r.y1+1); pDC->Line(r.x1+1, r.y1+1, r.x1+1, r.y2-2); pDC->Colour(High, 24); pDC->Line(r.x2-1, r.y2-1, r.x2-1, r.y1+1); pDC->Line(r.x2-1, r.y2-1, r.x1+1, r.y2-1); pDC->Colour(VHigh, 24); pDC->Line(r.x2, r.y2, r.x2, r.y1); pDC->Line(r.x2, r.y2, r.x1, r.y2); break; } case EdgeXpRaised: { pDC->Colour(VHigh, 24); pDC->Line(r.x1, r.y1, r.x2-1, r.y1); pDC->Line(r.x1, r.y1, r.x1, r.y2-1); pDC->Colour(High, 24); pDC->Line(r.x1+1, r.y1+1, r.x2-1, r.y1+1); pDC->Line(r.x1+1, r.y1+1, r.x1+1, r.y2-1); pDC->Colour(Low, 24); pDC->Line(r.x2-1, r.y2-1, r.x2-1, r.y1+1); pDC->Line(r.x2-1, r.y2-1, r.x1+1, r.y2-1); pDC->Colour(VLow, 24); pDC->Line(r.x2, r.y2, r.x2, r.y1); pDC->Line(r.x2, r.y2, r.x1, r.y2); break; } case EdgeXpChisel: { pDC->Colour(Low, 24); pDC->Line(r.x1, r.y1, r.x2-1, r.y1); pDC->Line(r.x1, r.y1, r.x1, r.y2-1); pDC->Colour(VHigh, 24); pDC->Line(r.x1+1, r.y1+1, r.x2-2, r.y1+1); pDC->Line(r.x1+1, r.y1+1, r.x1+1, r.y2-2); pDC->Colour(Low, 24); pDC->Line(r.x2-1, r.y2-1, r.x2-1, r.y1+1); pDC->Line(r.x2-1, r.y2-1, r.x1+1, r.y2-1); pDC->Colour(VHigh, 24); pDC->Line(r.x2, r.y2, r.x2, r.y1); pDC->Line(r.x2, r.y2, r.x1, r.y2); break; } case EdgeWin7Sunken: case EdgeWin7FocusSunken: { bool Focus = Type == EdgeWin7FocusSunken; // Win7 theme GColour Ws(LC_WORKSPACE, 24); GColour Corner1, Corner2, Corner3, Corner4, Corner5; GColour Top, Left, Right, Bottom; if (Focus) { Corner1.Rgb(0xad, 0xc4, 0xd7); Corner2.Rgb(0x5c, 0x93, 0xbc); Corner3.Rgb(0xc6, 0xde, 0xee); Corner4.Rgb(0xda, 0xe4, 0xed); Corner5.Rgb(0xc6, 0xde, 0xee); Left.Rgb(0xb5, 0xcf, 0xe7); Top.Rgb(0x3d, 0x7b, 0xad); Right.Rgb(0xa4, 0xc9, 0xe3); Bottom.Rgb(0xb7, 0xd9, 0xed); } else { Corner1.Rgb(0xd6, 0xd7, 0xd9); Corner2.Rgb(0xbb, 0xbd, 0xc2); Corner3.Rgb(0xe9, 0xec, 0xf0); Corner4.Rgb(0xeb, 0xeb, 0xee); Corner5.Rgb(0xe9, 0xec, 0xf0); Left.Rgb(0xe2, 0xe3, 0xea); Top.Rgb(0xab, 0xad, 0xb3); Right.Rgb(0xdb, 0xdf, 0xe6); Bottom.Rgb(0xe3, 0xe9, 0xef); } // Top corners pDC->Colour(Corner1); pDC->Set(r.x1, r.y1); pDC->Set(r.x2, r.y1); pDC->Colour(Corner2); pDC->Set(r.x1+1, r.y1); pDC->Set(r.x2-1, r.y1); pDC->Colour(Corner3); pDC->Set(r.x1+1, r.y1+1); pDC->Set(r.x2-1, r.y1+1); // Bottom corners. pDC->Colour(Corner4); pDC->Set(r.x1, r.y2); pDC->Set(r.x2, r.y2); pDC->Colour(Corner5); pDC->Set(r.x1+1, r.y2-1); pDC->Set(r.x2-1, r.y2-1); // Left edge pDC->Colour(Left); pDC->Line(r.x1, r.y1+1, r.x1, r.y2-1); pDC->Colour(Ws); pDC->Line(r.x1+1, r.y1+2, r.x1+1, r.y2-2); // Top edge pDC->Colour(Top); pDC->Line(r.x1+2, r.y1, r.x2-2, r.y1); pDC->Colour(Ws); pDC->Line(r.x1+2, r.y1+1, r.x2-2, r.y1+1); // Right edge pDC->Colour(Right); pDC->Line(r.x2, r.y1+1, r.x2, r.y2-1); pDC->Colour(Ws); pDC->Line(r.x2-1, r.y1+2, r.x2-1, r.y2-2); // Bottom edge pDC->Colour(Bottom); pDC->Line(r.x1+1, r.y2, r.x2-1, r.y2); pDC->Colour(Ws); pDC->Line(r.x1+2, r.y2-1, r.x2-2, r.y2-1); break; } default: { return; } } r.Size(2, 2); pDC->Colour(Old); } void LgiThinBorder(GSurface *pDC, GRect &r, LgiEdge Type) { if (!pDC) return; COLOUR Old = pDC->Colour(); switch (Type) { case EdgeXpSunken: case EdgeWin7FocusSunken: case EdgeWin7Sunken: { pDC->Colour(LC_LIGHT, 24); pDC->Line(r.x2, r.y2, r.x2, r.y1); pDC->Line(r.x2, r.y2, r.x1, r.y2); pDC->Colour(LC_LOW, 24); pDC->Line(r.x1, r.y1, r.x1, r.y2); pDC->Line(r.x1, r.y1, r.x2, r.y1); r.Size(1, 1); break; } case EdgeXpRaised: { pDC->Colour(LC_LOW, 24); pDC->Line(r.x2, r.y2, r.x2, r.y1); pDC->Line(r.x2, r.y2, r.x1, r.y2); pDC->Colour(LC_LIGHT, 24); pDC->Line(r.x1, r.y1, r.x1, r.y2); pDC->Line(r.x1, r.y1, r.x2, r.y1); r.Size(1, 1); break; } default: { LgiAssert(0); break; } } pDC->Colour(Old); } void LgiFlatBorder(GSurface *pDC, GRect &r, int Width) { pDC->Colour(LC_MED, 24); if (Width < 1 || r.X() < (2 * Width) || r.Y() < (2 * Width)) { pDC->Rectangle(&r); r.ZOff(-1, -1); } else { pDC->Rectangle(r.x1, r.y1, r.x2, r.y1+Width-1); pDC->Rectangle(r.x1, r.y2-Width+1, r.x2, r.y2); pDC->Rectangle(r.x1, r.y1+Width, r.x1+Width-1, r.y2-Width); pDC->Rectangle(r.x2-Width+1, r.y1+Width, r.x2, r.y2-Width); r.Size(Width, Width); } } void LgiFillGradient(GSurface *pDC, GRect &r, bool Vert, GArray &Stops) { int CurStop = 0; GColourStop *This = Stops.Length() > CurStop ? &Stops[CurStop] : 0; CurStop++; GColourStop *Next = Stops.Length() > CurStop ? &Stops[CurStop] : 0; int Limit = Vert ? r.Y() : r.X(); for (int n=0; nPos) { // Just this c = This->Colour; } else if (p > This->Pos && p < Next->Pos) { // Interpolate between this and next float d = Next->Pos - This->Pos; float t = (Next->Pos - p) / d; float n = (p - This->Pos) / d; - uint8 r = (uint8) ((R32(This->Colour) * t) + (R32(Next->Colour) * n)); - uint8 g = (uint8) ((G32(This->Colour) * t) + (G32(Next->Colour) * n)); - uint8 b = (uint8) ((B32(This->Colour) * t) + (B32(Next->Colour) * n)); - uint8 a = (uint8) ((A32(This->Colour) * t) + (A32(Next->Colour) * n)); + uint8_t r = (uint8_t) ((R32(This->Colour) * t) + (R32(Next->Colour) * n)); + uint8_t g = (uint8_t) ((G32(This->Colour) * t) + (G32(Next->Colour) * n)); + uint8_t b = (uint8_t) ((B32(This->Colour) * t) + (B32(Next->Colour) * n)); + uint8_t a = (uint8_t) ((A32(This->Colour) * t) + (A32(Next->Colour) * n)); c = Rgba32(r, g, b, a); } else if (p >= Next->Pos) { // Get next colour stops This = Stops.Length() > CurStop ? &Stops[CurStop] : 0; CurStop++; Next = Stops.Length() > CurStop ? &Stops[CurStop] : 0; goto DoStop; } pDC->Colour(c, 32); if (Vert) pDC->Line(r.x1, r.y1 + n, r.x2, r.y1 + n); else pDC->Line(r.x1 + n, r.y1, r.x1 + n, r.y2); } } } ////////////////////////////////////////////////////////////////////////////////// // Other Gdc Stuff GSurface *ConvertDC(GSurface *pDC, int Bits) { GSurface *pNew = new GMemDC; if (pNew && pNew->Create(pDC->X(), pDC->Y(), GBitsToColourSpace(Bits))) { pNew->Blt(0, 0, pDC); DeleteObj(pDC); return pNew; } return pDC; } GColour GdcMixColour(GColour c1, GColour c2, float HowMuchC1) { float HowMuchC2 = 1.0f - HowMuchC1; - uint8 r = (uint8) ((c1.r()*HowMuchC1) + (c2.r()*HowMuchC2)); - uint8 g = (uint8) ((c1.g()*HowMuchC1) + (c2.g()*HowMuchC2)); - uint8 b = (uint8) ((c1.b()*HowMuchC1) + (c2.b()*HowMuchC2)); - uint8 a = (uint8) ((c1.a()*HowMuchC1) + (c2.a()*HowMuchC2)); + uint8_t r = (uint8_t) ((c1.r()*HowMuchC1) + (c2.r()*HowMuchC2)); + uint8_t g = (uint8_t) ((c1.g()*HowMuchC1) + (c2.g()*HowMuchC2)); + uint8_t b = (uint8_t) ((c1.b()*HowMuchC1) + (c2.b()*HowMuchC2)); + uint8_t a = (uint8_t) ((c1.a()*HowMuchC1) + (c2.a()*HowMuchC2)); return GColour(r, g, b, a); } COLOUR GdcMixColour(COLOUR c1, COLOUR c2, float HowMuchC1) { double HowMuchC2 = 1.0 - HowMuchC1; - uint8 r = (uint8) ((R24(c1)*HowMuchC1) + (R24(c2)*HowMuchC2)); - uint8 g = (uint8) ((G24(c1)*HowMuchC1) + (G24(c2)*HowMuchC2)); - uint8 b = (uint8) ((B24(c1)*HowMuchC1) + (B24(c2)*HowMuchC2)); + uint8_t r = (uint8_t) ((R24(c1)*HowMuchC1) + (R24(c2)*HowMuchC2)); + uint8_t g = (uint8_t) ((G24(c1)*HowMuchC1) + (G24(c2)*HowMuchC2)); + uint8_t b = (uint8_t) ((B24(c1)*HowMuchC1) + (B24(c2)*HowMuchC2)); return Rgb24(r, g, b); } COLOUR GdcGreyScale(COLOUR c, int Bits) { COLOUR c24 = CBit(24, c, Bits); int r = R24(c24) * 76; int g = G24(c24) * 150; int b = B24(c24) * 29; return (r + g + b) >> 8; } COLOUR CBit(int DstBits, COLOUR c, int SrcBits, GPalette *Pal) { if (SrcBits == DstBits) { return c; } else { switch (SrcBits) { case 8: { GdcRGB Grey, *p = 0; if (!Pal || !(p = (*Pal)[c])) { Grey.r = Grey.g = Grey.b = c & 0xFF; p = &Grey; } switch (DstBits) { case 16: { return Rgb16(p->r, p->g, p->b); } case 24: { return Rgb24(p->r, p->g, p->b); } case 32: { return Rgb32(p->r, p->g, p->b); } } break; } case 15: { int R = R15(c); int G = G15(c); int B = B15(c); R = R | (R >> 5); G = G | (G >> 5); B = B | (B >> 5); switch (DstBits) { case 8: { if (Pal) { return Pal->MatchRgb(Rgb24(R, G, B)); } break; } case 16: { return Rgb16(R, G, B); } case 24: { return Rgb24(R, G, B); } case 32: { return Rgb32(R, G, B); } } break; } case 16: { /* int R = ((c>>8)&0xF8) | ((c>>13)&0x7); int G = ((c>>3)&0xFC) | ((c>>9)&0x3); int B = ((c<<3)&0xF8) | ((c>>2)&0x7); */ int R = (c >> 8) & 0xF8; int G = (c >> 3) & 0xFC; int B = (c << 3) & 0xF8; switch (DstBits) { case 8: { if (Pal) { return Pal->MatchRgb(Rgb24(R, G, B)); } break; } case 15: { return Rgb16To15(c); } case 24: { return Rgb24(R, G, B); } case 32: { return Rgb32(R, G, B); } } break; } case 24: { switch (DstBits) { case 8: { if (Pal) { return Pal->MatchRgb(c); } break; } case 15: { return Rgb24To15(c); } case 16: { if (LgiGetOs() == LGI_OS_WIN32 || LgiGetOs() == LGI_OS_WIN64) { return Rgb24To16(c); } int r = MAX(R24(c) - 1, 0) >> 3; int g = MAX(G24(c) - 1, 0) >> 2; int b = MAX(B24(c) - 1, 0) >> 3; return (r << 11) | (g << 5) | (b); } case 24: case 48: { return c; } case 32: case 64: { return Rgb24To32(c); } default: LgiAssert(0); break; } break; } case 32: { switch (DstBits) { case 8: { if (Pal) { return Pal->MatchRgb(Rgb32To24(c)); } break; } case 15: { return Rgb32To15(c); } case 16: { return Rgb32To16(c); } case 24: { return Rgb32To24(c); } } break; } } } return c; } ///////////////////////////////////////////////////////////////////////////// const char *GColourSpaceToString(GColourSpace cs) { #define CS_STR_BUF 4 static int Cur = 0; static char Buf[CS_STR_BUF][16]; static const char *CompTypes[] = { "N", // None "I", // Index "R", // Red "G", // Green "B", // Blue "A", // Alpha "X", // Pad "H", // Hue "S", // Sat "L", // Lum "C", // Cyan "M", // Magenta "Y", // Yellow "B", // Black "?", "?", }; char *start = Buf[Cur++], *s = start; int total = 0; bool first = true; if (Cur >= CS_STR_BUF) Cur = 0; *s++ = 'C'; *s++ = 's'; // printf("Converting Cs to String: 0x%x\n", cs); for (int i=3; i>=0; i--) { - int c = (((uint32)cs) >> (i << 3)) & 0xff; + int c = (((uint32_t)cs) >> (i << 3)) & 0xff; // printf(" c[%i] = 0x%x\n", i, c); if (c) { GComponentType type = (GComponentType)(c >> 4); int size = c & 0xf; if (first) { *s++ = CompTypes[type][0]; first = false; } else { *s++ = tolower(CompTypes[type][0]); } total += size ? size : 16; } } s += sprintf_s(s, 4, "%i", total); return start; } int GColourSpaceChannels(GColourSpace Cs) { int Channels = 0; while (Cs) { - uint8 c = Cs & 0xff; + uint8_t c = Cs & 0xff; if (!c) break; Channels++; ((int&)Cs) >>= 8; } return Channels; } bool GColourSpaceHasAlpha(GColourSpace Cs) { while (Cs) { - uint8 c = Cs & 0xff; + uint8_t c = Cs & 0xff; GComponentType type = (GComponentType)(c >> 4); if (type == CtAlpha) return true; ((int&)Cs) >>= 8; } return false; } int GColourSpaceToBits(GColourSpace ColourSpace) { - uint32 c = ColourSpace; + uint32_t c = ColourSpace; int bits = 0; while (c) { if (c & 0xf0) { int n = c & 0xf; bits += n ? n : 16; } c >>= 8; } return bits; } GColourSpace GStringToColourSpace(const char *c) { if (!c) return CsNone; if (!_strnicmp(c, "Cs", 2)) { // "Cs" style colour space c += 2; const char *n = c; while (*n && !IsDigit(*n)) n++; int Depth = ::atoi(n); if (!_strnicmp(c, "Index", 5)) { if (Depth == 8) return CsIndex8; } else { GArray Comp; while (*c) { switch (tolower(*c)) { case 'r': Comp.Add(CtRed); break; case 'g': Comp.Add(CtGreen); break; case 'b': Comp.Add(CtBlue); break; case 'a': Comp.Add(CtAlpha); break; case 'x': Comp.Add(CtPad); break; case 'c': Comp.Add(CtCyan); break; case 'm': Comp.Add(CtMagenta); break; case 'y': Comp.Add(CtYellow); break; case 'k': Comp.Add(CtBlack); break; case 'h': Comp.Add(CtHue); break; case 'l': Comp.Add(CtLuminance); break; case 's': Comp.Add(CtSaturation); break; } c++; } GColourSpaceBits a; ZeroObj(a); if (Comp.Length() == 3) { a[0].Type(Comp[0]); a[1].Type(Comp[1]); a[2].Type(Comp[2]); if (Depth == 24) { a[0].Size(8); a[1].Size(8); a[2].Size(8); } else if (Depth == 16) { a[0].Size(5); a[1].Size(6); a[2].Size(5); } else if (Depth == 15) { a[0].Size(5); a[0].Size(5); a[0].Size(5); } else if (Depth == 48) { a[0].Size(0); a[1].Size(0); a[2].Size(0); } else return CsNone; } else if (Comp.Length() == 4) { a[0].Type(Comp[0]); a[1].Type(Comp[1]); a[2].Type(Comp[2]); a[3].Type(Comp[3]); if (Depth == 32) { a[0].Size(8); a[1].Size(8); a[2].Size(8); a[3].Size(8); } else if (Depth == 64) { a[0].Size(0); a[1].Size(0); a[2].Size(0); a[3].Size(0); } else return CsNone; } GColourSpace Cs = (GColourSpace)a.All; return Cs; } } else if (!_strnicmp(c, "System", 6)) { // "System" colour space c += 6; int Depth = ::atoi(c); if (Depth) return GBitsToColourSpace(Depth); } return CsNone; } GColourSpace GBitsToColourSpace(int Bits) { switch (Bits) { case 8: return CsIndex8; case 15: return System15BitColourSpace; case 16: return System16BitColourSpace; case 24: return System24BitColourSpace; case 32: return System32BitColourSpace; default: LgiAssert(!"Unknown colour space."); break; } return CsNone; } bool GColourSpaceTest() { union { - uint8 b4[4]; - uint32 u32; + uint8_t b4[4]; + uint32_t u32; GBgrx32 bgrx32; GXrgb32 xrgb32; GRgba32 rgba32; }; b4[0] = 1; b4[1] = 2; b4[2] = 3; b4[3] = 4; #if 0 // def LGI_SDL printf("LeastSigBit=%i, LeastSigByte=%i, u32=%08x\n", LeastSigBit, LeastSigByte, u32); printf("bgrx32=%i,%i,%i,%i\n", bgrx32.b, bgrx32.g, bgrx32.r, bgrx32.pad); printf("xrgb32=%i,%i,%i,%i\n", xrgb32.pad, xrgb32.r, xrgb32.g, xrgb32.b); #endif if (bgrx32.b != 1 || bgrx32.pad != 4 || xrgb32.pad != 1 || xrgb32.b != 4) { LgiAssert(!"32bit colour space byte ordering wrong. Flip the value of LEAST_SIG_BYTE_FIRST"); return false; } union { uint16 u16; GRgb16 rgb16; }; rgba32.r = 0xff; rgba32.g = 0; rgba32.b = 0; rgba32.a = 0; u16 = 0xf800; #if 0 // def LGI_SDL printf("rgba32=%i,%i,%i,%i or %08x\n", rgba32.r, rgba32.g, rgba32.b, rgba32.a, u32); printf("rgb16=%i,%i,%i or %04x\n", rgb16.r, rgb16.g, rgb16.b, u16); #endif if (rgb16.r != 0x1f || rgb16.b != 0x0) { LgiAssert(!"16bit colour space bit ordering wrong. Flip the value of LEAST_SIG_BIT_FIRST"); return false; } return true; } //////////////////////////////////////////////////////////////////////// -GSurface *GInlineBmp::Create(uint32 TransparentPx) +GSurface *GInlineBmp::Create(uint32_t TransparentPx) { GSurface *pDC = new GMemDC; if (pDC->Create(X, Y, System32BitColourSpace, GSurface::SurfaceRequireExactCs)) { GBmpMem Src, Dst; - Src.Base = (uint8*)Data; + Src.Base = (uint8_t*)Data; Src.Line = X * Bits >> 3; Src.x = X; Src.y = Y; switch (Bits) { case 8: Src.Cs = CsIndex8; break; case 15: Src.Cs = CsRgb15; break; case 16: Src.Cs = CsRgb16; break; case 24: Src.Cs = CsRgb24; break; case 32: Src.Cs = CsRgba32; break; default: Src.Cs = CsNone; break; } Dst.Base = (*pDC)[0]; Dst.Line = pDC->GetRowStep(); Dst.x = pDC->X(); Dst.y = pDC->Y(); Dst.Cs = pDC->GetColourSpace(); LgiRopUniversal(&Dst, &Src, false); if (TransparentPx != 0xffffffff) { for (int y=0; y> 3) { case 1: { for (int x=0; xr == r && px->g == g && px->b == b) { *d = 0; } d++; } break; } case 4: { for (int x=0; x %s\n", _FL, GColourSpaceToString(SrcCs), GColourSpaceToString(DstCs)); LgiAssert(!"Unsupported pixel conversion."); return false; } } return true; } /// Universal bit blt method bool LgiRopUniversal(GBmpMem *Dst, GBmpMem *Src, bool Composite) { if (!Dst || !Src) return false; // Work out conversion area... int Cx = MIN(Dst->x, Src->x); int Cy = MIN(Dst->y, Src->y); // Size of src and dst pixels: int SrcBits = GColourSpaceToBits(Src->Cs); int DstBits = GColourSpaceToBits(Dst->Cs); if (Dst->Cs == Src->Cs && !Composite) { // No conversion idiom - uint8 *d = Dst->Base; - uint8 *s = Src->Base; + uint8_t *d = Dst->Base; + uint8_t *s = Src->Base; int BytesPerPx = (SrcBits + 7) / 8; int Bytes = BytesPerPx * Cx; for (int y=0; yLine; s += Src->Line; } return true; } if (SrcBits > 8 && DstBits > 8) { - uint8 *d = Dst->Base; - uint8 *s = Src->Base; + uint8_t *d = Dst->Base; + uint8_t *s = Src->Base; for (int y=0; yCs, s, Src->Cs, Cx, Composite)) return false; d += Dst->Line; s += Src->Line; } return true; } else { } // LgiAssert(!"Unsupported pixel conversion."); return false; } //////////////////////////////////////////////////////////////////////////////// int LgiScreenDpi() { return 96; // A reasonable default. } diff --git a/src/common/Gdc2/Path/GPath.cpp b/src/common/Gdc2/Path/GPath.cpp --- a/src/common/Gdc2/Path/GPath.cpp +++ b/src/common/Gdc2/Path/GPath.cpp @@ -1,2186 +1,2186 @@ #include #include #include #include "Lgi.h" #include "GPath.h" #include "GdiLeak.h" #include "GDisplayString.h" #define CIRCLE_STEPS 24 #define BEZIER_STEPS 24 #if 0 // 8x8 sub pixel anti-aliasing (really accurate/really slow) #define SUB_SAMPLE 8 #define SUB_SHIFT 3 #define SUB_MASK 7 #elif 1 // 4x4 sub pixel anti-aliasing (best quality/performance tradeoff) #define SUB_SAMPLE 4 #define SUB_SHIFT 2 #define SUB_MASK 3 #elif 0 // 2x2 sub pixel anti-aliasing (fast and nasty) #define SUB_SAMPLE 2 #define SUB_SHIFT 1 #define SUB_MASK 1 #endif #define DEBUG_DRAW_VECTORS 0 #define DEBUG_DRAW_SEGMENTS 0 #define DEBUG_ODD_EDGES 0 // #define DEBUG_LOG Log.Print #ifndef LGI_SKIN // #define DEBUG_LOG Log.Print #endif #ifdef DEBUG_LOG GFile Log; #endif #ifdef _MSC_VER #ifdef _DEBUG #ifdef DEBUG_LOG #define PathAssert(b) if (!(b)) { Log.Close(); \ _asm int 3 \ } #else #ifdef _WIN64 #define PathAssert(b) LgiAssert(b) #else #define PathAssert(b) if (!(b)) _asm int 3 #endif #endif #else #define PathAssert(b) #endif #else #define PathAssert(b) #endif #define DEBUG_SCALE 1.0 #define FixedToDbl(i) ( ( (double) ((long*)&i)[0]) / 65536) #define ToInt(d) ((int)( (d+(0.5/SUB_SAMPLE)+0.00000001) * SUB_SAMPLE)) #define ToDbl(i) ((double)i / SUB_SAMPLE) double GPointF::Threshold = 0.00000001; bool _Disable_ActiveList = false; bool _Disable_XSort = false; bool _Disable_Alpha = false; bool _Disable_Rops = false; /////////////////////////////////////////////////////////// class AAEdge { public: double x[SUB_SAMPLE]; int Min, Max; }; class GVector { public: bool Up; int Points; GPointF *p; GRectF Bounds; int Index; int y1, y2; double *x; double Cx; int aay1, aay2; AAEdge *aax; GVector() { Index = -1; x = 0; aax = 0; aay1 = aay2 = y1 = y2 = -1; } ~GVector() { DeleteArray(x); DeleteArray(aax); } bool Rasterize(bool aa) { Bounds = p[0]; for (int n=1; ny); if (End > Cur) { PathAssert(End >= Cur); double k = b->x - a->x; double Rx; if (k == 0.0) { // vertical line for (int s=Cur; s<=End; s++) { int Yi = (s>>SUB_SHIFT) - y1; PathAssert(Yi < n); aax[Yi].x[s&SUB_MASK] = Rx = a->x; Bounds.x1 = MIN(Bounds.x1, Rx); Bounds.x2 = MAX(Bounds.x2, Rx); /* PathAssert( aax[Yi].x[s&SUB_MASK] >= Bounds.x1 - GPointF::Threshold && aax[Yi].x[s&SUB_MASK] <= Bounds.x2 + GPointF::Threshold); */ } } else { // angled line double Lm = (b->y - a->y) / k; double Lb = b->y - (Lm * b->x); double Ry; for (int s=Cur; s<=End; s++) { int Yi = (s>>SUB_SHIFT) - y1; PathAssert(Yi < n); Ry = ToDbl(s); aax[Yi].x[s&SUB_MASK] = Rx = (Ry - Lb) / Lm; Bounds.x1 = MIN(Bounds.x1, Rx); Bounds.x2 = MAX(Bounds.x2, Rx); /* PathAssert( aax[Yi].x[s&SUB_MASK] >= Bounds.x1 - GPointF::Threshold && aax[Yi].x[s&SUB_MASK] <= Bounds.x2 + GPointF::Threshold); */ } } Cur = End + 1; } else { Bounds.x1 = MIN(Bounds.x1, b->x); Bounds.x2 = MAX(Bounds.x2, b->x); } PathAssert(a >= p); a += Inc; b += Inc; } } #ifdef DEBUG_LOG DEBUG_LOG("\n", c); DEBUG_LOG("%i%c\n", p[0].x - 2.5, p[0].y, c, Index, Up?'u':'d'); /* DEBUG_LOG("Vector[%i] Up=%i Bounds=%g,%g,%g,%g aay=%i,%i\n", Index, Up, Bounds.x1, Bounds.y1, Bounds.x2, Bounds.y2, aay1, aay2); for (int i=0; iy > Ry ? 1 : 0; LSide = Last->y > Ry ? 1 : 0; while (!CSide ^ LSide) { Last = Cur; Cur += Up ? -1 : 1; CSide = Cur->y > Ry ? 1 : 0; LSide = Last->y > Ry ? 1 : 0; } if (Cur->x == Last->x) { x[y - y1] = Cur->x; } else { double m = (Cur->y - Last->y) / (Cur->x - Last->x); double b = Cur->y - (m * Cur->x); x[y - y1] = (Ry - b) / m; } } } return x != 0; } } }; int VectCompareY(GVector *a, GVector *b, NativeInt Data) { double d = a->Bounds.y1 - b->Bounds.y1; return (d < 0) ? -1 : 1; } int VectCompareX(GVector *a, GVector *b, NativeInt Data) { NativeInt i = Data >> SUB_SHIFT; NativeInt r = Data & SUB_MASK; double d = a->aax[i - a->y1].x[r] - b->aax[i - b->y1].x[r]; return (d < 0) ? -1 : 1; } /////////////////////////////////////////////////////////// enum GSegType { SegMove, SegLine, SegQuad, SegCube }; class GSeg { public: GSegType Type; int Points; GPointF *Point; GSeg(GSegType t) { Type = t; switch (Type) { case SegMove: case SegLine: Points = 1; break; case SegQuad: Points = 2; break; case SegCube: Points = 3; break; default: Points = 0; break; } Point = Points ? new GPointF[Points] : 0; } ~GSeg() { DeleteArray(Point); } void Translate(double Dx, double Dy) { for (int i=0; i 0 && Point) { return Point; } return 0; } GPointF *Last() { if (Points > 0 && Point) { return Point + Points - 1; } return 0; } }; /////////////////////////////////////////////////////////// void Round(double &n) { int i = (int)floor(n+0.5); double d = i - n; if (d < 0) d = -d; if (d < 0.0000001) { n = i; } } void GRectF::Normalize() { Round(x1); Round(y1); Round(x2); Round(y2); if (x1 > x2) { double d = x1; x1 = x2; x2 = d; } if (y1 > y2) { double d = y1; y1 = y2; y2 = d; } } void GRectF::Intersect(GRectF &p) { if (Defined) { x1 = MAX(x1, p.x1); y1 = MAX(y1, p.y1); x2 = MIN(x2, p.x2); y2 = MIN(y2, p.y2); } } void GRectF::Union(GPointF &p) { if (Defined) { x1 = MIN(x1, p.x); y1 = MIN(y1, p.y); x2 = MAX(x2, p.x); y2 = MAX(y2, p.y); } else { x1 = x2 = p.x; y1 = y2 = p.y; Defined = true; } } void GRectF::Union(GRectF &p) { if (Defined) { p.Normalize(); x1 = MIN(x1, p.x1); y1 = MIN(y1, p.y1); x2 = MAX(x2, p.x2); y2 = MAX(y2, p.y2); } else { *this = p; } } bool GRectF::Overlap(GPointF &p) { return (p.x >= x1) && (p.y >= y1) && (p.x <= x2) && (p.y <= y2); } bool GRectF::Overlap(GRectF &p) { return (p.x1 <= x2) && (p.x2 >= x1) && (p.y1 <= y2) && (p.y2 >= y1); } void GRectF::Offset(double x, double y) { x1 += x; y1 += y; x2 += x; y2 += y; } void GRectF::Size(double dx, double dy) { x1 += dx; y1 += dy; x2 -= dx; y2 -= dy; } GRectF &GRectF::operator =(GRect &f) { x1 = f.x1; y1 = f.y1; x2 = f.x2 + 1; y2 = f.y2 + 1; Defined = true; return *this; } GRectF &GRectF::operator =(GRectF &f) { x1 = f.x1; y1 = f.y1; x2 = f.x2; y2 = f.y2; Defined = true; return *this; } GRectF &GRectF::operator =(GPointF &p) { x1 = x2 = p.x; y1 = y2 = p.y; Defined = true; return *this; } char *GRectF::Describe() { static char s[64]; sprintf_s(s, sizeof(s), "%f,%f,%f,%f", x1, y1, x2, y2); return s; } /////////////////////////////////////////////////////////// void FlattenQuadratic(GPointF *&Out, GPointF &p1, GPointF &p2, GPointF &p3, int numsteps) { double xdelta, xdelta2; double ydelta, ydelta2; double deltastep, deltastep2; double ax, ay; double bx, by; double i; GPointF v1, v2, *o; if (!Out) { Out = new GPointF[numsteps]; } if (Out) { o = Out; deltastep = 1.0 / (double) (numsteps - 1); deltastep2 = deltastep*deltastep; ax = p1.x + -2.0*p2.x + p3.x; ay = p1.y + -2.0*p2.y + p3.y; bx = -2.0*p1.x + 2.0*p2.x; by = -2.0*p1.y + 2.0*p2.y; xdelta = ax*deltastep2 + bx*deltastep; ydelta = ay*deltastep2 + by*deltastep; xdelta2 = 2.0*ax*deltastep2; ydelta2 = 2.0*ay*deltastep2; v1.x = p1.x; v1.y = p1.y; o->x = v1.x; o->y = v1.y; o++; for (i=0; ix = v2.x; o->y = v2.y; o++; v1 = v2; } } } void FlattenCubic(GPointF *&Out, GPointF &p1, GPointF &p2, GPointF &p3, GPointF &p4, int numsteps) { double xdelta, xdelta2, xdelta3; double ydelta, ydelta2, ydelta3; double deltastep, deltastep2, deltastep3; double ax, ay; double bx, by; double cx, cy; double i; GPointF v1, v2, *o; if (!Out) { Out = new GPointF[numsteps]; } if (Out) { o = Out; deltastep = 1.0 / (double) (numsteps - 1); deltastep2 = deltastep*deltastep; deltastep3 = deltastep2*deltastep; ax = -p1.x + 3.0*p2.x - 3.0*p3.x + p4.x; ay = -p1.y + 3.0*p2.y - 3.0*p3.y + p4.y; bx = 3.0*p1.x - 6.0*p2.x + 3.0*p3.x; by = 3.0*p1.y - 6.0*p2.y + 3.0*p3.y; cx = -3.0*p1.x + 3.0*p2.x; cy = -3.0*p1.y + 3.0*p2.y; xdelta = ax*deltastep3 + bx*deltastep2 + cx*deltastep; ydelta = ay*deltastep3 + by*deltastep2 + cy*deltastep; xdelta2 = 6.0*ax*deltastep3 + 2.0*bx*deltastep2; ydelta2 = 6.0*ay*deltastep3 + 2.0*by*deltastep2; xdelta3 = 6.0*ax*deltastep3; ydelta3 = 6.0*ay*deltastep3; v1.x = p1.x; v1.y = p1.y; o->x = v1.x; o->y = v1.y; o++; for (i=0; ix = v2.x; o->y = v2.y; o++; v1 = v2; } } } /////////////////////////////////////////////////////////// GPath::GPath(bool aa) { Aa = aa; Points = 0; Point = 0; FillRule = FILLRULE_ODDEVEN; Bounds.x1 = Bounds.y1 = Bounds.x2 = Bounds.y2 = 0.0; #ifdef DEBUG_LOG Log.Open("c:\\temp\\gpath-log.txt", O_WRITE); Log.SetPos(Log.GetSize()); #endif } GPath::~GPath() { #ifdef DEBUG_LOG Log.Close(); #endif Unflatten(); Segs.DeleteObjects(); } void GPath::GetBounds(GRectF *b) { if (b) { if (!IsFlattened()) { Flatten(); } *b = Bounds; } } void GPath::Transform(Matrix &m) { Mat = m; } void GPath::MoveTo(double x, double y) { Close(); GPointF p(x, y); MoveTo(p); } void GPath::MoveTo(GPointF &pt) { GSeg *s = new GSeg(SegMove); if (s) { s->Point[0] = pt; Segs.Insert(s); } } void GPath::LineTo(double x, double y) { GPointF p(x, y); LineTo(p); } void GPath::LineTo(GPointF &pt) { GSeg *s = new GSeg(SegLine); if (s) { s->Point[0] = pt; Segs.Insert(s); } } void GPath::QuadBezierTo(double cx, double cy, double px, double py) { GPointF c(cx, cy); GPointF p(px, py); QuadBezierTo(c, p); } void GPath::QuadBezierTo(GPointF &c, GPointF &p) { GSeg *s = new GSeg(SegQuad); if (s) { s->Point[0] = c; s->Point[1] = p; Segs.Insert(s); } } void GPath::CubicBezierTo(double c1x, double c1y, double c2x, double c2y, double px, double py) { GPointF c1(c1x, c1y); GPointF c2(c2x, c2y); GPointF p(px, py); CubicBezierTo(c1, c2, p); } void GPath::CubicBezierTo(GPointF &c1, GPointF &c2, GPointF &p) { GSeg *s = new GSeg(SegCube); if (s) { s->Point[0] = c1; s->Point[1] = c2; s->Point[2] = p; Segs.Insert(s); } } void GPath::Rectangle(double x1, double y1, double x2, double y2) { MoveTo(x1, y1); LineTo(x2, y1); LineTo(x2, y2); LineTo(x1, y2); LineTo(x1, y1); // is this last one necessary? } void GPath::Rectangle(GPointF &tl, GPointF &rb) { Rectangle(tl.x, tl.y, rb.x, rb.y); } void GPath::Rectangle(GRectF &r) { MoveTo(r.x1, r.y1); LineTo(r.x2, r.y1); LineTo(r.x2, r.y2); LineTo(r.x1, r.y2); LineTo(r.x1, r.y1); } void GPath::RoundRect(GRectF &b, double r) { double k = 0.5522847498 * r; MoveTo(b.x1 + r, b.y1); GPointF c(b.x2 - r, b.y1 + r); LineTo(c.x, b.y1); CubicBezierTo( c.x + k, c.y - r, c.x + r, c.y - k, c.x + r, c.y); c.Set(b.x2 - r, b.y2 - r); LineTo(b.x2, c.y); CubicBezierTo( c.x + r, c.y + k, c.x + k, c.y + r, c.x, c.y + r); c.Set(b.x1 + r, b.y2 - r); LineTo(c.x, b.y2); CubicBezierTo( c.x - k, c.y + r, c.x - r, c.y + k, c.x - r, c.y); c.Set(b.x1 + r, b.y1 + r); LineTo(b.x1, c.y); CubicBezierTo( c.x - r, c.y - k, c.x - k, c.y - r, c.x, c.y - r); } void GPath::Circle(double cx, double cy, double radius) { GPointF c(cx, cy); Circle(c, radius); } void GPath::Circle(GPointF &c, double r) { /* double theta; for (int i = 0; i < CIRCLE_STEPS + 1; i++) { theta = (i * LGI_PI * 2.0 / CIRCLE_STEPS); GSeg *s = new GSeg(i ? SegLine : SegMove); if (s) { s->Point[0].x = c.x + (radius * cos(theta)); s->Point[0].y = c.y - (radius * sin(theta)); Segs.Insert(s); } } */ double k = 0.5522847498 * r; MoveTo(c.x, c.y - r); CubicBezierTo( c.x + k, c.y - r, c.x + r, c.y - k, c.x + r, c.y); CubicBezierTo( c.x + r, c.y + k, c.x + k, c.y + r, c.x, c.y + r); CubicBezierTo( c.x - k, c.y + r, c.x - r, c.y + k, c.x - r, c.y); CubicBezierTo( c.x - r, c.y - k, c.x - k, c.y - r, c.x, c.y - r); } void GPath::Ellipse(double cx, double cy, double x, double y) { GPointF c(cx, cy); Ellipse(c, x, y); } void GPath::Ellipse(GPointF &c, double x, double y) { double dx = x * 0.552; double dy = y * 0.552; MoveTo(c.x + x, c.y); CubicBezierTo( c.x + x, c.y - dy, c.x + dx, c.y - y, c.x, c.y - y); CubicBezierTo( c.x - dx, c.y - y, c.x - x, c.y - dy, c.x - x, c.y); CubicBezierTo( c.x - x, c.y + dy, c.x - dx, c.y + y, c.x, c.y + y); CubicBezierTo( c.x + dx, c.y + y, c.x + x, c.y + dy, c.x + x, c.y); } #ifdef WIN32 GPointF &PointConv(POINTFX pt, double x, double y, double Scale) { static GPointF f; f.x = x + (FixedToDbl(pt.x) * Scale); f.y = y - (FixedToDbl(pt.y) * Scale); return f; } #endif void GPath::Append(GPath &p) { Unflatten(); for (GSeg *s = p.Segs.First(); s; s = p.Segs.Next()) { Segs.Insert(s); } p.Segs.Empty(); } bool GPath::Text( GFont *Font, double x, double y, char *Utf8, int Bytes) { bool Error = false; if (Font && Utf8) { char16 *Utf16 = Utf8ToWide(Utf8, Bytes); if (Utf16) { GPath Temp; GDisplayString Sp(Font, " "); #ifdef WIN32 double _x = 0, _y = 0; HDC hDC = CreateCompatibleDC(0); if (hDC) { HFONT Old = (HFONT)SelectObject(hDC, Font->Handle()); GLYPHMETRICS Metrics; MAT2 Mat; ZeroObj(Mat); Mat.eM11.value = 1; Mat.eM22.value = 1; double Scale = DEBUG_SCALE; // Font->Height(); for (char16 *In = Utf16; *In; In++) { if (*In == ' ') { _x += Sp.X() * Scale; continue; } int Format = GGO_NATIVE; // | GGO_UNHINTED; DWORD BufSize = GetGlyphOutline(hDC, *In, Format, &Metrics, 0, 0, &Mat); if (BufSize != -1) { uchar *Buf = new uchar[BufSize]; if (Buf) { if (GetGlyphOutline(hDC, *In, Format, &Metrics, BufSize, Buf, &Mat) > 0) { GPointF Cur; TTPOLYGONHEADER *tt = (TTPOLYGONHEADER*)Buf; while ((void*)tt < (void*)((char*)Buf + BufSize)) { if (tt->dwType == TT_POLYGON_TYPE) { Cur = PointConv(tt->pfxStart, _x, _y, Scale); GPointF Start = Cur; Temp.MoveTo(Cur); #ifdef DEBUG_LOG DEBUG_LOG("ttf.move %f,%f\n", Cur.x, Cur.y); #endif TTPOLYCURVE *c = (TTPOLYCURVE*) (tt + 1); - while ((uint8*)c < ((uint8*)tt) + tt->cb) + while ((uint8_t*)c < ((uint8_t*)tt) + tt->cb) { switch (c->wType) { case TT_PRIM_LINE: { for (int i=0; icpfx; i++) { Cur = PointConv(c->apfx[i], _x, _y, Scale); Temp.LineTo(Cur); #ifdef DEBUG_LOG DEBUG_LOG("ttf.line %.2f,%.2f\n", Cur.x, Cur.y); #endif } break; } case TT_PRIM_QSPLINE: // quadratic { for (int i = 0; i < c->cpfx;) { GPointF c1 = PointConv(c->apfx[i], _x, _y, Scale); GPointF e; i++; if (i == (c->cpfx - 1)) { e = PointConv(c->apfx[i], _x, _y, Scale); i++; } else { GPointF b = PointConv(c->apfx[i], _x, _y, Scale); e.x = (c1.x + b.x) / 2; e.y = (c1.y + b.y) / 2; } Temp.QuadBezierTo(c1, e); #ifdef DEBUG_LOG DEBUG_LOG("ttf.quad %.2f,%.2f %.2f,%.2f\n", c1.x, c1.y, e.x, e.y); #endif Cur = e; } break; } default: case TT_PRIM_CSPLINE: // cubic { PathAssert(0); break; } } int Len = sizeof(c->cpfx) + sizeof(c->wType) + (sizeof(c->apfx) * c->cpfx); c = (TTPOLYCURVE*)&c->apfx[c->cpfx]; } #ifdef DEBUG_LOG if (Cur != Start) { Temp.LineTo(Start); DEBUG_LOG("Warning: Not closed!! -> ttf.line %.2f,%.2f\n", Start.x, Start.y); } #endif tt = (TTPOLYGONHEADER*)c; } else break; } _x += Metrics.gmCellIncX * Scale; } else Error = true; DeleteArray(Buf); } } } SelectObject(hDC, Old); DeleteDC(hDC); } else Error = true; #endif DeleteArray(Utf16); #ifdef DEBUG_LOG DEBUG_LOG("\n"); #endif GRectF b; Temp.GetBounds(&b); double Dx = x - b.x1; double Dy = y - b.y1 - Font->Ascent(); for (GSeg *s=Temp.Segs.First(); s; s = Temp.Segs.Next()) { s->Translate(Dx, Dy); } Append(Temp); } else Error = true; } else Error = true; return !Error; } int GPath::Segments() { return (int)Segs.Length(); } void GPath::DeleteSeg(int i) { Segs.DeleteAt(i); } bool GPath::IsClosed() { GSeg *f = Segs.First(); GSeg *l = Segs.Last(); if (f && l) { GPointF *Start = f->First(); GPointF *End = l->Last(); if (Start && End) { return *Start == *End; } } return false; } void GPath::Close() { if (!IsClosed()) { // Find last moveto GSeg *f = Segs.Last(); while (f && f->Type != SegMove) { f = Segs.Prev(); } // If there is a moveto... if (f) { // Then close that shape LineTo(f->Point[0]); } } } bool GPath::IsEmpty() { return Segs.Length() == 0; } void GPath::Empty() { Unflatten(); Segs.DeleteObjects(); Bounds.x1 = Bounds.y1 = Bounds.x2 = Bounds.y2 = 0.0; } bool GPath::IsFlattened() { return Outline.Length() > 0; } bool GPath::Flatten() { bool Status = false; Unflatten(); for (GSeg *s=Segs.First(); s; s=Segs.Next()) { switch (s->Type) { default: case SegMove: { Points += 2; // start AND end break; } case SegLine: { Points++; break; } case SegQuad: case SegCube: { Points += BEZIER_STEPS - 1; break; } } } Point = new GPointF[Points]; if (Point) { int n = 0; GPointF *c = Point; GPointF *OutlineStart = Point; for (GSeg *s=Segs.First(); s; s=Segs.Next(), n++) { switch (s->Type) { default: case SegMove: { // Close the shape back to the start of the object if (n && c[-1] != *OutlineStart) { *c++ = *OutlineStart; } // Get the index into the array of point int k = (int)(c - Point) - 1; if (k >= 0) { #ifdef DEBUG_LOG DEBUG_LOG("Outline at %i\n", k); #endif // Store the index pointing to the start of the shape Outline.Add(k); } // Keep a pointer to the start of the shape (for closing it later) OutlineStart = c; // Store the point at the start of the shape *c++ = s->Point[0]; #ifdef DEBUG_LOG DEBUG_LOG("Move[%i]: %g,%g\n", n, s->Point[0].x, s->Point[0].y); #endif break; } case SegLine: { *c++ = s->Point[0]; #ifdef DEBUG_LOG DEBUG_LOG("Line[%i]: %g,%g\n", n, s->Point[0].x, s->Point[0].y); #endif break; } case SegQuad: { c--; FlattenQuadratic(c, *c, s->Point[0], s->Point[1], BEZIER_STEPS); c += BEZIER_STEPS; #ifdef DEBUG_LOG DEBUG_LOG("Quad[%i]: %g,%g %g,%g\n", n, s->Point[0].x, s->Point[0].y, s->Point[1].x, s->Point[1].y); #endif break; } case SegCube: { c--; FlattenCubic(c, *c, s->Point[0], s->Point[1], s->Point[2], BEZIER_STEPS); c += BEZIER_STEPS; #ifdef DEBUG_LOG DEBUG_LOG("Quad[%i]: %g,%g %g,%g %g,%g\n", n, s->Point[0].x, s->Point[0].y, s->Point[1].x, s->Point[1].y, s->Point[2].x, s->Point[2].y); #endif break; } } } // Close the last shape if (c[-1] != *OutlineStart) { *c++ = *OutlineStart; } // Calculate the number of points in the array Points = (int) (c - Point); // Set the end of the points to be a full shape Outline.Add(Points); Status = true; } #ifdef DEBUG_LOG DEBUG_LOG("\n"); #endif // Create vector list bool Up = false; int Start = -1; int VecIndex = 0; int NextOutline = 0; for (int i=1; iy - Prev->y; dy = dy < 0 ? -dy : dy; // Start the next segment if no start defined and there is some change in y if (Start < 0 && dy > GPointF::Threshold) { // Store the index of the start for this vector Start = i - 1; // Calculate which direction we are travelling in Up = Cur->y < Prev->y; } if (Start >= 0) { bool DirChange = false; if (Next) { // Work out if we have crossed an apex and are travelling in // the other direction: DirChange = Up ? Next->y >= Cur->y : Next->y <= Cur->y; } // Do we need to start another vector? if (DirChange || Outline[NextOutline] == i || !Next) { // Create a new vector GVector *v = new GVector; if (v) { // Store it Vecs.Insert(v); // Bump it's index v->Index = VecIndex++; // Store whether it's up or down v->Up = Up; // Calculate how many points it has v->Points = i - Start + 1; // Check it's valid PathAssert(v->Points > 1); // Store the pointer to the start of the vector v->p = Point + Start; // Rasterize it into a bunch of cor-ordinates for each sub-pixel scanline v->Rasterize(Aa); // Work out the bounds Bounds.Union(v->Bounds); } // Set that we are starting a new vector Start = -1; // Seek to the next outline start index if (Outline[NextOutline] == i) { i = Outline[NextOutline++] + 1; } else if (Outline[NextOutline] == i + 1) { // i = Outline[NextOutline++] + 1; } } } else { if (i >= Outline[NextOutline]) { i = Outline[NextOutline++] + 1; } } } // Sort the vectors on their top edge Vecs.Sort(VectCompareY); #ifdef DEBUG_LOG DEBUG_LOG("\n"); #endif Bounds.Normalize(); return Status; } void GPath::Unflatten() { Points = 0; Outline.Length(0); DeleteArray(Point); Vecs.DeleteObjects(); } void GPath::Fill(GSurface *pDC, GBrush &c) { if (!GdcD || !pDC || !(*pDC)[0]) { PathAssert(0); return; } if (!Point) { Flatten(); } if (Point) { #ifdef DEBUG_LOG DEBUG_LOG("\n"); int *ko = &Outline[0]; for (int k=0; k In; for (GVector *v=Vecs.First(); v; v=Vecs.Next()) { In.Insert(v); } List Active; size_t VectorCount = In.Length(); double *x = new double[VectorCount]; GVector **xv = new GVector*[VectorCount]; int x1 = (int)floor(Bounds.x1); int x2 = (int)ceil(Bounds.x2); int Width = x2 - x1 + 1; uchar *Alpha = new uchar[Width]; #if 0 #define CHECK_XV(idx) PathAssert((idx) >= 0 && (idx) < VectorCount) #define CHECK_ALPHA(idx) PathAssert((idx) >= 0 && (idx) < Width) #else #define CHECK_XV(idx) #define CHECK_ALPHA(idx) #endif GRectF Doc = Bounds; int Ox = 0, Oy = 0; pDC->GetOrigin(Ox, Oy); double OffsetX = Mat[2][0] - Ox; double OffsetY = Mat[2][1] - Oy; Doc.Offset(OffsetX, OffsetY); GRectF Page(0, 0, pDC->X(), pDC->Y()); GRect DcClip = pDC->ClipRgn(); if (DcClip.Valid()) Page = DcClip; GRectF Clip = Doc; Clip.Intersect(Page); // Convert the doc and clip back to path coords. Doc.Offset(-OffsetX, -OffsetY); Clip.Offset(-OffsetX, -OffsetY); GBrush::GRopArgs a; a.pDC = pDC; a.Pixels = 0; a.Alpha = 0; a.Cs = pDC->GetColourSpace(); a.BytesPerPixel = pDC->GetBits() >> 3; a.x = (int)(Clip.x1 - Mat[2][0]); int RopLength = (int)ceil(Clip.x2) - (int)floor(Clip.x1); a.Len = RopLength; a.EndOfMem = (*pDC)[pDC->Y()-1] + (pDC->X() * pDC->GetBits() / 8); if (x && Alpha && c.Start(a)) { if (Aa) { #define ADD_EVENT 0x01 #define REMOVE_EVENT 0x02 int y1 = ToInt(Doc.y1); int y2 = ToInt(Doc.y2); int aax1 = x1 << SUB_SHIFT; int Height = (y2>>SUB_SHIFT) - (y1>>SUB_SHIFT) + 1; uchar *Events = new uchar[Height]; if (Events) { // Setup events memset(Events, 0, Height); for (GVector *New=In.First(); New; New=In.Next()) { int Aay1 = (New->aay1 - y1) >> SUB_SHIFT; int Aay2 = (New->aay2 - y1) >> SUB_SHIFT; PathAssert(Aay1 >= 0 && Aay1 < Height); PathAssert(Aay2 >= 0 && Aay2 < Height); Events[Aay1] |= ADD_EVENT; Events[Aay2] |= REMOVE_EVENT; } // For each scan line for (int y=y1; y<=y2; ) { // Anti-aliased edges memset(Alpha, 0, Width); // Loop subpixel for (int s = y&SUB_MASK; s>SUB_SHIFT] & ADD_EVENT) { // Add new active vecs for (GVector *New=In.First(); New;) { if (New->aay1 == y) { Active.Insert(New); In.Delete(New); New = In.Current(); } else { New = In.Next(); } } } if (Events[(y-y1)>>SUB_SHIFT] & REMOVE_EVENT) { // Remove old active vecs for (GVector *Old=Active.First(); Old; ) { PathAssert(Old->aay2 >= y); if (Old->aay2 == y) { Active.Delete(Old); Old = Active.Current(); } else { Old = Active.Next(); } } } } int Xs = 0; if (!_Disable_XSort) { // Sort X values from active vectors if (FillRule == FILLRULE_NONZERO) { GVector *a=Active.First(); if (a) { // bool Up = a->Up; int n = 0; for (; a; a=Active.Next()) { a->Cx = a->aax[(y>>SUB_SHIFT) - a->y1].x[s]; #if defined(_DEBUG) && DEBUG_NEGITIVE_X if (a->Cx < 1.0) { PathAssert(0); } #endif CHECK_XV(n-1); if (n == 0 || a->Cx >= xv[n-1]->Cx) { // Insert at end CHECK_XV(n); xv[n++] = a; } else { // int Oldn = n; // Insert in the middle or start for (int i=0; iCx < xv[i]->Cx) { for (int k = n - 1; k >= i; k--) { CHECK_XV(k+1); CHECK_XV(k); xv[k+1] = xv[k]; } CHECK_XV(i); xv[i] = a; n++; break; } } } } int Depth = 0; for (int i=0; iCx; } Depth += xv[i]->Up ? -1 : 1; if (Depth == 0) { x[Xs++] = xv[i]->Cx; } } } } else { for (GVector *a=Active.First(); a; a=Active.Next()) { double Cx = a->aax[(y>>SUB_SHIFT) - a->y1].x[s]; PathAssert(Cx >= Bounds.x1 - GPointF::Threshold && Cx <= Bounds.x2 + GPointF::Threshold); if (Xs == 0 || (Xs > 0 && Cx >= x[Xs-1])) { // Insert at end x[Xs++] = Cx; } else { // int OldXs = Xs; // Insert in the middle or start for (int i=0; i= i; n--) { x[n+1] = x[n]; } x[i] = Cx; Xs++; break; } } } } } } #if defined(_DEBUG) && DEBUG_ODD_EDGES if (Xs % 2 == 1) { PathAssert(0); } #endif #ifdef DEBUG_LOG if (Xs % 2 == 1) { DEBUG_LOG("ERROR: Odd number of edges (%i) on this line:\n", Xs); } DEBUG_LOG("Y=%i Xs=%i ", y, Xs); for (int n=0; n>SUB_SHIFT) == (Ex>>SUB_SHIFT)) { CHECK_ALPHA(Sx>>SUB_SHIFT); Alpha[Sx>>SUB_SHIFT] += Ex-Sx; } else { int Before = SUB_SAMPLE - (Sx & SUB_MASK); if (Before) { CHECK_ALPHA(Sx>>SUB_SHIFT); Alpha[Sx>>SUB_SHIFT] += Before; Sx += Before; } for (int k=Sx>>SUB_SHIFT; k<(Ex>>SUB_SHIFT); k++) { CHECK_ALPHA(k); Alpha[k] += SUB_SAMPLE; } int After = Ex & SUB_MASK; if (After) { CHECK_ALPHA(Ex>>SUB_SHIFT); Alpha[Ex>>SUB_SHIFT] += After; } } } } } if (!_Disable_Rops) { // Draw pixels.. int DocX = (int)(Clip.x1 - Doc.x1); a.y = ((y + SUB_SHIFT - 1) >> SUB_SHIFT) + (int)Mat[2][1] - 1; if (a.y >= floor(Clip.y1) && a.y <= ceil(Clip.y2)) { a.Pixels = (*pDC)[a.y-Oy]; if (a.Pixels) { int AddX = DocX + (int)Doc.x1; a.y -= (int)Mat[2][1]; a.Len = RopLength; a.Pixels += a.BytesPerPixel * (AddX - Ox); // memset(a.Pixels, 0xff, 12); a.Alpha = Alpha + DocX; c.Rop(a); } } } } } DeleteArray(Events); DeleteArray(Alpha); } else // Not anti-aliased { int y1 = (int)floor(Bounds.y1); int y2 = (int)ceil(Bounds.y2); // For each scan line for (int y=y1; y<=y2; y++) { memset(Alpha, 0, Width); // Add new active vecs for (GVector *New=In.First(); New; New=In.First()) { if (New->y1 == y) { Active.Insert(New); In.Delete(New); } else break; } // Sort X values from active vectors int Xs = 0; for (GVector *v=Active.First(); v; v=Active.Next()) { if (y < v->y1 || y > v->y2) { PathAssert(0); } double Cx = v->x[y - v->y1]; if (Xs == 0 || Cx > x[Xs-1]) { // Insert at end x[Xs++] = Cx; } else { // Insert in the middle or start for (int i=0; i 1) { a.y = y; a.Pixels = (*pDC)[a.y]; if (a.Pixels) { a.Pixels += a.BytesPerPixel * a.x; a.Alpha = Alpha + a.x - x1; c.Rop(a); } } // Remove old active vecs for (GVector *Old=Active.First(); Old; ) { if (Old->y2 == y) { Active.Delete(Old); Old = Active.Current(); } else { Old = Active.Next(); } } } } } // Clean up DeleteArray(x); DeleteArray(xv); #if DEBUG_DRAW_VECTORS for (GVector *h = Vecs.First(); h; h = Vecs.Next()) { pDC->Colour(h->Up ? Rgb24(255, 0, 0) : Rgb24(0, 0, 255), 24); for (int i=0; iPoints-1; i++) { pDC->Line( h->p[i].x, h->p[i].y, h->p[i+1].x, h->p[i+1].y); } } #endif #if DEBUG_DRAW_SEGMENTS // Debug GPointF Cur; int n = 0; for (GSeg *s=Segs.First(); s; s=Segs.Next(), n++) { switch (s->Type) { case SegMove: { Cur = s->Point[0]; break; } case SegLine: { pDC->Colour(Rgb24(0, 0, 255), 24); pDC->Line(Cur.x, Cur.y, s->Point[0].x, s->Point[0].y); Cur = s->Point[0]; break; } case SegQuad: { pDC->Colour(Rgb24(255, 0, 0), 24); pDC->Line(Cur.x, Cur.y, s->Point[0].x, s->Point[0].y); pDC->Circle(Cur.x, Cur.y, 3); pDC->Line(s->Point[0].x, s->Point[0].y, s->Point[1].x, s->Point[1].y); Cur = s->Point[1]; pDC->Circle(Cur.x, Cur.y, 3); pDC->Circle(s->Point[0].x, s->Point[0].y, 2); break; } case SegCube: { pDC->Colour(Rgb24(255, 0, 0), 24); pDC->Line(Cur.x, Cur.y, s->Point[0].x, s->Point[0].y); pDC->Line(s->Point[0].x, s->Point[0].y, s->Point[1].x, s->Point[1].y); pDC->Line(s->Point[1].x, s->Point[1].y, s->Point[2].x, s->Point[2].y); pDC->Circle(Cur.x, Cur.y, 3); Cur = s->Point[2]; pDC->Circle(Cur.x, Cur.y, 3); pDC->Circle(s->Point[0].x, s->Point[0].y, 2); pDC->Circle(s->Point[1].x, s->Point[1].y, 2); break; } } if (n) { char s[256]; sprintf_s(s, sizeof(s), "%i", n); /* Debug.Fore(Rgb24(0x80, 0x80, 0x80)); Debug.Transparent(true); Debug.Text(pDC, Cur.x + 5, Cur.y - 4, s); */ } } #endif } } void GPath::Stroke(GSurface *pDC, GBrush &Brush, double Width) { if (!Point) { Flatten(); } if (Point) { // pDC->Colour(c, 24); int Ox, Oy; pDC->GetOrigin(Ox, Oy); pDC->SetOrigin((int)-Mat[2][0], (int)-Mat[2][1]); int i; GPointF *p = Point; for (i=0; iLine( (int)p[i].x, (int)p[i].y, (int)p[i+1].x, (int)p[i+1].y); } pDC->Line((int)p[0].x, (int)p[0].y, (int)p[i].x, (int)p[i].y); pDC->SetOrigin(Ox, Oy); } } /////////////////////////////////////////////////////////////////// void GBrush::MakeAlphaLut() { for (int i=0; i<65; i++) { AlphaLut[i] = (i * 255) / (SUB_SAMPLE * SUB_SAMPLE); } } /////////////////////////////////////////////////////////////////// // Compositing void GSolidBrush::Rop(GRopArgs &Args) { switch (Args.Cs) { default: LgiAssert(!"Impl me"); break; #define SolidCase(cs, bits) \ case Cs##cs: SolidRop##bits((G##cs*)Args.Pixels, Args.Len, Args.Alpha, c32); break; SolidCase(Rgb16, 16); SolidCase(Bgr16, 16); SolidCase(Rgb24, 24); SolidCase(Bgr24, 24); SolidCase(Xrgb32, 24); SolidCase(Xbgr32, 24); SolidCase(Rgbx32, 24); SolidCase(Bgrx32, 24); SolidCase(Rgba32, 32); SolidCase(Argb32, 32); SolidCase(Bgra32, 32); SolidCase(Abgr32, 32); } } //////////////////////////////////////////////////////////////////////////// int StopCompare(GBlendStop *a, GBlendStop *b, NativeInt Data) { return a->Pos > b->Pos ? 1 : -1; } bool GBlendBrush::Start(GRopArgs &Args) { List s; for (int n=0; n Next->Pos) { GBlendStop *n = s.Next(); if (n) { Prev = Next; Next = n; } } if (p < Prev->Pos) { Lut[i] = Prev->c32; } else if (p < Next->Pos) { double d = Next->Pos - Prev->Pos; uchar a = (int) (((p - Prev->Pos) / d) * 255); uchar oma = 255 - a; Lut[i]= Rgba32 ( (R32(Prev->c32) * oma / 255) + (R32(Next->c32) * a / 255), (G32(Prev->c32) * oma / 255) + (G32(Next->c32) * a / 255), (B32(Prev->c32) * oma / 255) + (B32(Next->c32) * a / 255), (A32(Prev->c32) * oma / 255) + (A32(Next->c32) * a / 255) ); } else if (p >= Next->Pos) { Lut[i] = Next->c32; } } return true; } return false; } //////////////////////////////////////////////////////////////////////////// bool GLinearBlendBrush::Start(GRopArgs &Args) { GBlendBrush::Start(Args); double dx = p[1].x - p[0].x; double dy = p[1].y - p[0].y; // bool Vert = (dx < 0 ? -dx : dx) < 0.0000001; double d = sqrt( (dx*dx) + (dy*dy) ); double a = atan2(dy, dx); GPointF Origin(0, 0); Origin.Translate(-p[0].x, -p[0].y); Origin.Rotate(-a); GPointF x(1, 0); x.Translate(-p[0].x, -p[0].y); x.Rotate(-a); GPointF y(0, 1); y.Translate(-p[0].x, -p[0].y); y.Rotate(-a); Base = Origin.x / d; IncX = (x.x - Origin.x) / d; IncY = (y.x - Origin.x) / d; return true; } void GLinearBlendBrush::Rop(GRopArgs &Args) { PathAssert(GdcD != NULL); switch (Args.Cs) { default: LgiAssert(!"Impl me"); break; #define LinearCase(cs, bits) \ case Cs##cs: Linear##bits((G##cs*)Args.Pixels, Args); break; LinearCase(Rgb16, 16); LinearCase(Bgr16, 16); LinearCase(Rgb24, 24); LinearCase(Bgr24, 24); LinearCase(Xrgb32, 24); LinearCase(Xbgr32, 24); LinearCase(Rgbx32, 24); LinearCase(Bgrx32, 24); LinearCase(Rgba32, 32); LinearCase(Argb32, 32); LinearCase(Bgra32, 32); LinearCase(Abgr32, 32); } } //////////////////////////////////////////////////////////////////////////// // The squaring of the root: // http://www.azillionmonkeys.com/qed/sqroot.html //////////////////////////////////////////////////////////////////////////// void GRadialBlendBrush::Rop(GRopArgs &Args) { switch (Args.Cs) { default: LgiAssert(!"Impl me"); break; #define RadialCase(cs, bits) \ case Cs##cs: Radial##bits((G##cs*)Args.Pixels, Args); break; RadialCase(Rgb16, 16); RadialCase(Bgr16, 16); RadialCase(Rgb24, 24); RadialCase(Bgr24, 24); RadialCase(Xrgb32, 24); RadialCase(Xbgr32, 24); RadialCase(Rgbx32, 24); RadialCase(Bgrx32, 24); RadialCase(Rgba32, 32); RadialCase(Argb32, 32); RadialCase(Bgra32, 32); RadialCase(Abgr32, 32); } } ////////////////////////////////////////////// void GEraseBrush::Rop(GRopArgs &Args) { uchar *DivLut = Div255Lut; uchar *r = Args.Alpha; switch (Args.Cs) { default: LgiAssert(!"Impl me"); break; case System24BitColourSpace: { break; } case System32BitColourSpace: { System32BitPixel *d = (System32BitPixel*) Args.Pixels; System32BitPixel *end = d + Args.Len; while (d < end) { d->a = DivLut[d->a * (255 - AlphaLut[*r++])]; d++; } break; } } } diff --git a/src/common/Gdc2/Tools/GColourReduce.cpp b/src/common/Gdc2/Tools/GColourReduce.cpp --- a/src/common/Gdc2/Tools/GColourReduce.cpp +++ b/src/common/Gdc2/Tools/GColourReduce.cpp @@ -1,611 +1,611 @@ #include #include "Gdc2.h" #include "GPalette.h" #define TABLE_COMPSIZE 6 #define TABLE_SIZE (1 << (3 * TABLE_COMPSIZE)) #define DIMENSIONS 3 struct ImgColour { - uint8 c[DIMENSIONS]; + uint8_t c[DIMENSIONS]; int count; }; struct Range { int Min, Max; int Len() { return Max - Min + 1; } }; struct Edge { int Dimension, Length; Edge(int d, int l) { Dimension = d; Length = l; } }; int Cmp0(ImgColour **a, ImgColour **b) { return (*a)->c[0] - (*b)->c[0]; } int Cmp1(ImgColour **a, ImgColour **b) { return (*a)->c[1] - (*b)->c[1]; } int Cmp2(ImgColour **a, ImgColour **b) { return (*a)->c[2] - (*b)->c[2]; } struct Box { GArray Cols; Range r[DIMENSIONS]; int Pixels; Box(ImgColour *c, int len) { LgiAssert(c && len > 0); if (!c || len <= 0) return; Cols.Length(len); for (int i=0; i &c, int Start, int End) { LgiAssert(End >= Start); Cols.Length(End - Start + 1); for (unsigned i=0; ic[d] * col->count; } } for (int d=0; d 0); Pixels = 0; for (int i=0; ic[i]; } Pixels += Cols[0]->count; for (unsigned n=0; nc[i]); r[i].Max = MAX(r[i].Max, c->c[i]); } Pixels += c->count; } } Edge LongestDimension() { int Max = -1; for (int i=0; i &Index, int Dim) { Index = Cols; if (Dim == 0) Index.Sort(Cmp0); else if (Dim == 1) Index.Sort(Cmp1); else if (Dim == 2) Index.Sort(Cmp2); else return false; return true; } }; int HueSort(GColour *a, GColour *b) { GColour A = *a; // GColour B = *b; return A.GetH() - b->GetH(); } int LumaSort(GColour *a, GColour *b) { GColour A = *a; return A.GetL() - b->GetL(); } template -uint32 ColourDistance16(ImgColour *a, B *b) +uint32_t ColourDistance16(ImgColour *a, B *b) { // calculate distance int dr = a->c[0] - G5bitTo8bit(b->r); int dg = a->c[1] - G6bitTo8bit(b->g); int db = a->c[2] - G5bitTo8bit(b->b); // final result return abs(dr) + abs(dg) + abs(db); } template -uint32 ColourDistance24(ImgColour *a, B *b) +uint32_t ColourDistance24(ImgColour *a, B *b) { // calculate distance int dr = a->c[0] - b->r; int dg = a->c[1] - b->g; int db = a->c[2] - b->b; // final result return abs(dr) + abs(dg) + abs(db); } class GPaletteReduce { int TableSize; ImgColour *Col; unsigned ColUsed; unsigned DestSize; public: GPaletteReduce(GPalette *Out, GSurface *In, unsigned destSize) { TableSize = TABLE_SIZE; ColUsed = 0; DestSize = destSize; int TableMemSize = sizeof(ImgColour) * TableSize; Col = new ImgColour[TableSize]; if (Col) { // scan image for colours memset(Col, 0, TableMemSize); } } ~GPaletteReduce() { DeleteArray(Col); } template void Scan16(GSurface *In, int y) { bool fuzzy = ColUsed > DestSize; Px *in, *end; in = (Px*) (*In)[y]; end = in + In->X(); while (in < end) { int hash = (((int)in->r << 1) << 10) | (((int)in->g) << 4) | (((int)in->b << 1) >> 2); int h; for (h = 0; h < TABLE_SIZE; h++) { int idx = (hash + h) % TABLE_SIZE; ImgColour *col = Col + idx; if (col->count == 0) { // Create entry for this RGB col->c[0] = G5bitTo8bit(in->r); col->c[1] = G6bitTo8bit(in->g); col->c[2] = G5bitTo8bit(in->b); col->count++; ColUsed++; break; } else { int dist = ColourDistance16(col, in); if (dist == 0 || (fuzzy && dist < 3)) { // Reuse the existing entry col->count++; break; } } } LgiAssert(h < TABLE_SIZE); in++; } } template void Scan24(GSurface *In, int y) { bool fuzzy = ColUsed > DestSize; Px *in, *end; in = (Px*) (*In)[y]; end = in + In->X(); while (in < end) { int hash = (((int)in->r & 0xfc) << 10) | (((int)in->g & 0xfc) << 4) | (((int)in->b & 0xfc) >> 2); int h; for (h = 0; h < TABLE_SIZE; h++) { int idx = (hash + h) % TABLE_SIZE; ImgColour *col = Col + idx; if (col->count == 0) { // Create entry for this RGB col->c[0] = in->r; col->c[1] = in->g; col->c[2] = in->b; col->count++; ColUsed++; break; } else { int dist = ColourDistance24(col, in); if (dist == 0 || (fuzzy && dist < 3)) { // Reuse the existing entry col->count++; break; } } } LgiAssert(h < TABLE_SIZE); in++; } } void Scan(GSurface *In) { for (int y=0; yY(); y++) { switch (In->GetColourSpace()) { #define ScanCase24(type) \ case Cs##type: \ Scan24(In, y); \ break case CsRgb16: Scan16(In, y); break; case CsBgr16: Scan16(In, y); break; ScanCase24(Rgb24); ScanCase24(Bgr24); ScanCase24(Rgba32); ScanCase24(Bgra32); ScanCase24(Argb32); ScanCase24(Abgr32); ScanCase24(Rgbx32); ScanCase24(Bgrx32); ScanCase24(Xrgb32); ScanCase24(Xbgr32); default: LgiAssert(!"Not impl."); break; } } } bool Reduce(GPalette *Out) { // remove unused colours unsigned Cols = 0, i; for (i=0; ir = Col[i].c[0]; p->g = Col[i].c[1]; p->b = Col[i].c[2]; } } else { // Reduce the size of the colour set to fit GArray Boxes; Box *b = new Box(Col, Cols); Boxes.Add(b); while (Boxes.Length() < DestSize) { // Find largest box... int MaxBox = -1; for (unsigned i=0; iPixels > Boxes[MaxBox]->Pixels) ) && Boxes[i]->Cols.Length() > 1 // otherwise we can't split the colours ) MaxBox = i; } Edge MaxEdge = Boxes[MaxBox]->LongestDimension(); // Split that box along the median Box *b = Boxes[MaxBox]; GArray Index; if (b->Sort(Index, MaxEdge.Dimension)) { // Find the median by picking an arbitrary mid-point unsigned Pos = (unsigned) Index.Length() >> 1; if (Index.Length() > 2) { int LowerSum = 0; int UpperSum = 0; for (size_t i=0; icount; else UpperSum += Index[i]->count; } // And then refining it's position iteratively while (LowerSum != UpperSum) { ImgColour *c; int Offset; if (LowerSum < UpperSum) { // Move pos up c = Index[Pos]; Offset = 1; } else { // Move pos down c = Index[Pos-1]; Offset = -1; } int NewLower = LowerSum + (Offset * c->count); int NewUpper = UpperSum + (-Offset * c->count); if ( (LowerSum < UpperSum) ^ (NewLower < NewUpper) ) { break; } LowerSum = NewLower; UpperSum = NewUpper; Pos += Offset; } } // Create new boxes out of the 2 parts LgiAssert(Pos > 0); Boxes.Add(new Box(Index, 0, Pos - 1)); Boxes.Add(new Box(Index, Pos, (unsigned) Index.Length() - 1)); // Delete the old 'big' box Boxes.Delete(b); DeleteObj(b); } else return false; } GArray Colours; for (unsigned i=0; iAverage(avg); GColour p(avg[0], avg[1], avg[2]); Colours.Add(p); } Colours.Sort(LumaSort); Out->SetSize(DestSize); for (unsigned i=0; ir = in.r(); out->g = in.g(); out->b = in.b(); } else LgiAssert(0); } } return true; } }; bool CreatePalette(GPalette *Out, GSurface *In, int DestSize) { if (!Out || !In || In->GetBits() <= 8) return false; GPaletteReduce Pr(Out, In, DestSize); Pr.Scan(In); Pr.Reduce(Out); return true; } bool GReduceBitDepth(GSurface *pDC, int Bits, GPalette *Pal, GReduceOptions *Reduce) { bool Status = false; GSurface *pTemp = new GMemDC; if (pDC && pTemp && pTemp->Create(pDC->X(), pDC->Y(), GBitsToColourSpace(Bits))) { if (Bits <= 8 && Pal) { pTemp->Palette(new GPalette(Pal)); } if ((Bits <= 8) && (pDC->GetBits() > 8) && Reduce) { // dithering // palette if (!pTemp->Palette()) { // currently has no palette switch (Reduce->PalType) { default: LgiAssert(0); break; case CR_PAL_CUBE: { // RGB cube palette pTemp->Palette(Pal = new GPalette); if (Pal) { Pal->CreateCube(); } break; } case CR_PAL_OPT: { // optimal palette pTemp->Palette(Pal = new GPalette(0, Reduce->Colours)); if (Pal) { CreatePalette(Pal, pDC, Reduce->Colours); } break; } } } // colour conversion int ReduceType = REDUCE_NEAREST; switch (Reduce->MatchType) { default: { break; } case CR_MATCH_HALFTONE: { Pal->CreateCube(); ReduceType = REDUCE_HALFTONE; break; } case CR_MATCH_ERROR: { ReduceType = REDUCE_ERROR_DIFFUSION; break; } } int OldType = GdcD->GetOption(GDC_REDUCE_TYPE); GdcD->SetOption(GDC_REDUCE_TYPE, ReduceType); pTemp->Blt(0, 0, pDC); GdcD->SetOption(GDC_REDUCE_TYPE, OldType); } else { // direct convert pTemp->Blt(0, 0, pDC); } if (pDC->Create(pTemp->X(), pTemp->Y(), pTemp->GetColourSpace())) { if (Pal) pDC->Palette(new GPalette(Pal)); pDC->Blt(0, 0, pTemp); Status = true; } } DeleteObj(pTemp); return Status; } diff --git a/src/common/General/GContainers.cpp b/src/common/General/GContainers.cpp --- a/src/common/General/GContainers.cpp +++ b/src/common/General/GContainers.cpp @@ -1,856 +1,856 @@ /** * \file * \author Matthew Allen * \date 27/7/1995 * \brief Container classes.\n Copyright (C) 1995-2003, Matthew Allen */ #ifdef LINUX #define _ISOC99_SOURCE 1 #include #endif #include #include #include #include #include "GMem.h" #include "GContainers.h" #include "GString.h" #include "LgiCommon.h" ///////////////////////////////////////////////////////////////////////////////////////////////// int ACmp(GString *a, GString *b) { return stricmp(*a, *b); } int LCmp(char *a, char *b, NativeInt d) { return stricmp(a, b); } void UnitTest_CreateList(GArray &a, List &l, int sz = 100) { a.Empty(); for (int i=0; i &a, List &l) { if (a.Length() != l.Length()) return UnitTest_Err("Wrong size."); int n = 0; for (auto s : l) { GString t = s; if (t.Equals(a[n])) ;//printf("%i: %s\n", n, s); else return UnitTest_Err("Wrong value."); n++; } return true; } #define CHECK(val) if (!(val)) return false bool UnitTest_ListClass() { GArray a; List l; // Create test.. UnitTest_CreateList(a, l, 100); CHECK(UnitTest_Check(a, l)); // Delete tests.. l.Delete(a[3].Get()); a.DeleteAt(3, true); CHECK(UnitTest_Check(a, l)); while (a.Length() > 60) { a.DeleteAt(60, true); l.DeleteAt(60); } CHECK(UnitTest_Check(a, l)); // Sort test.. a.Sort(ACmp); l.Sort(LCmp); CHECK(UnitTest_Check(a, l)); return true; } //////////////////////////////////////////////////////////////////////////////////////// GMemQueue::GMemQueue(int prealloc) { PreAlloc = prealloc; } GMemQueue::~GMemQueue() { Empty(); } GMemQueue &GMemQueue::operator=(GMemQueue &p) { Empty(); PreAlloc = p.PreAlloc; for (Block *b = p.Mem.First(); b; b = p.Mem.Next()) { int Alloc = sizeof(Block) + b->Size; Alloc = LGI_ALLOC_ALIGN(Alloc); Block *n = (Block*) malloc(Alloc); if (n) { memcpy(n, b, sizeof(Block) + b->Size); Mem.Insert(n); } else break; } return *this; } void GMemQueue::Empty() { for (Block *b = Mem.First(); b; b = Mem.First()) { Mem.Delete(b); free(b); } } int64 GMemQueue::GetSize() { int Size = 0; for (Block *b = Mem.First(); b; b = Mem.Next()) { Size += b->Used - b->Next; } return Size; } #if 0 int GMemQueue::Find(char *Str) { if (Str) { int Pos = 0; uint8 *UStr = (uint8*) Str; int SLen = strlen(Str); if (SLen > 0) { for (List::I Blocks = Mem.Start(); Blocks.In(); Blocks++) { Block *m = *Blocks; uint8 *Base = ((uint8*)m->Ptr()) + m->Next; uint8 *End = Base + (m->Used - m->Next); for (uint8 *Ptr = Base; Ptr < End; Ptr++) { if (*Ptr == *UStr) { // Check rest of string if (Ptr + SLen > End) { // Setup separate iterator to get to the next block List::I Match = Blocks; Block *Cur = *Match; if (Cur == m) { uint8 *b = Base; uint8 *e = End; uint8 *p = Ptr; bool m = true; for (int i=1; i= e) { Match++; Cur = *Match; if (Cur) { b = ((uint8*)Cur->Ptr()) + Cur->Next; e = b + (Cur->Used - Cur->Next); p = b - i; } else break; } if (p[i] != UStr[i]) { m = false; break; } } if (m) { return Pos + (Ptr - Base); } } else { LgiAssert(0); } } else { // String is entirely in this block... bool m = true; for (int i=1; iUsed - m->Next; } } } return -1; } int64 GMemQueue::Peek(GStreamI *Str, int Size) { int64 Status = 0; if (Str && Size <= GetSize()) { Block *b = 0; for (b = Mem.First(); b && Size > 0; b = Mem.Next()) { int Copy = min(Size, b->Size - b->Next); if (Copy > 0) { Str->Write(b->Ptr() + b->Next, Copy); Size -= Copy; Status += Copy; } } } return Status; } int GMemQueue::Pop(short &i) { short n; if (Read((uchar*) &n, sizeof(n))) { i = n; return sizeof(n); } return 0; } int GMemQueue::Pop(int &i) { int n; if (Read((uchar*) &n, sizeof(n))) { i = n; return sizeof(n); } return 0; } int GMemQueue::Pop(double &i) { double n; if (Read((uchar*) &n, sizeof(n))) { i = n; return sizeof(n); } return 0; } #endif int64 GMemQueue::Peek(uchar *Ptr, int Size) { int64 Status = 0; if (Ptr && Size <= GetSize()) { Block *b = 0; for (b = Mem.First(); b && Size > 0; b = Mem.Next()) { int Copy = MIN(Size, b->Size - b->Next); if (Copy > 0) { memcpy(Ptr, b->Ptr() + b->Next, Copy); Ptr += Copy; Size -= Copy; Status += Copy; } } } return Status; } void *GMemQueue::New(int AddBytes) { int64 Len = GetSize(); uchar *Data = Len > 0 ? new uchar[Len+AddBytes] : 0; if (Data) { ssize_t Rd = Read(Data, Len); if (Rd >= 0 && AddBytes) { memset(Data+Len, 0, AddBytes); } } return Data; } ssize_t GMemQueue::Read(void *Ptr, ssize_t Size, int Flags) { int Status = 0; if (Ptr && Size > 0) { Block *b = 0; for (b = Mem.First(); b && Size > 0; b = Mem.Next()) { int Copy = (int) MIN(Size, b->Used - b->Next); if (Copy > 0) { memcpy(Ptr, b->Ptr() + b->Next, Copy); ((uchar*&)Ptr) += Copy; Size -= Copy; b->Next += Copy; Status += Copy; } } for (b = Mem.First(); b && b->Next >= b->Used; b = Mem.First()) { Mem.Delete(b); free(b); } } return Status; } ssize_t GMemQueue::Write(const void *Ptr, ssize_t Size, int Flags) { ssize_t Status = 0; /* char m[256]; sprintf_s(m, sizeof(m), "%p::Write(%p, %i, %i)\n", this, Ptr, Size, Flags); OutputDebugStringA(m); */ if (Ptr && Size > 0) { if (PreAlloc > 0) { Block *Last = Mem.Last(); if (Last) { int Len = (int) MIN(Size, Last->Size - Last->Used); if (Len > 0) { memcpy(Last->Ptr() + Last->Used, Ptr, Len); Last->Used += Len; Size -= Len; ((uchar*&)Ptr) += Len; Status += Len; } } } if (Size > 0) { ssize_t Bytes = MAX(PreAlloc, Size); ssize_t Alloc = sizeof(Block) + Bytes; Alloc = LGI_ALLOC_ALIGN(Alloc); Block *b = (Block*) malloc(Alloc); if (b) { void *p = b->Ptr(); memcpy(p, Ptr, Size); b->Size = (int)Bytes; b->Used = (int)Size; b->Next = 0; Mem.Insert(b); Status += Size; } } } return Status; } ////////////////////////////////////////////////////////////////////////// GString GStringPipe::NewGStr() { GString s; int64 Sz = GetSize(); if (Sz > 0) { if (s.Length(Sz)) { ssize_t Rd = Read(s.Get(), s.Length()); if (Rd > 0 && Rd <= Sz) { s.Get()[Rd] = 0; } } else s.Empty(); } return s; } ssize_t GStringPipe::LineChars() { ssize_t Len = 0; for (Block *m = Mem.First(); m; m = Mem.Next()) { - uint8 *p = m->Ptr(); + uint8_t *p = m->Ptr(); for (int i = m->Next; i < m->Used; i++) { if (p[i] == '\n') return Len; Len++; } } return -1; } ssize_t GStringPipe::SaveToBuffer(char *Start, ssize_t BufSize, ssize_t Chars) { char *Str = Start; char *End = Str + BufSize; // Not including NULL Block *m = Mem.First(); while (m) { for ( char *MPtr = (char*)m->Ptr(); m->Next < m->Used; m->Next++) { if (Str < End) *Str++ = MPtr[m->Next]; if (MPtr[m->Next] == '\n') { m->Next++; goto EndPop; } } if (m->Next >= m->Used) { Mem.Delete(m); free(m); m = Mem.First(); } } EndPop: *Str = 0; return Str - Start; } ssize_t GStringPipe::Pop(GArray &Buf) { ssize_t Chars = LineChars(); if (Chars > 0) { Chars++; // For the '\n' if ((ssize_t)Buf.Length() < Chars + 1) if (!Buf.Length(Chars + 1)) return -1; SaveToBuffer(Buf.AddressOf(), Chars, Chars); } else return 0; return Chars; } GString GStringPipe::Pop() { GString s; ssize_t Chars = LineChars(); if (Chars > 0 && s.Length(Chars)) { SaveToBuffer(s.Get(), Chars, Chars); } return s; } ssize_t GStringPipe::Pop(char *Str, ssize_t BufSize) { if (!Str) return 0; ssize_t Chars = LineChars(); if (Chars <= 0) return 0; return SaveToBuffer(Str, BufSize-1 /* for the NULL */, Chars); } ssize_t GStringPipe::Push(const char *Str, ssize_t Chars) { if (!Str) return 0; if (Chars < 0) Chars = strlen(Str); return Write((void*)Str, Chars); } ssize_t GStringPipe::Push(const char16 *Str, ssize_t Chars) { if (!Str) return 0; if (Chars < 0) Chars = StrlenW(Str); return Write((void*)Str, Chars * sizeof(char16)); } #ifdef _DEBUG bool GStringPipe::UnitTest() { char Buf[16]; memset(Buf, 0x1, sizeof(Buf)); GStringPipe p(8); const char s[] = "1234567890abc\n" "abcdefghijklmn\n"; p.Write(s, sizeof(s)-1); ssize_t rd = p.Pop(Buf, 10); int cmp = memcmp(Buf, "123456789\x00\x01\x01\x01\x01\x01\x01", 16); if (cmp) return false; rd = p.Pop(Buf, 10); cmp = memcmp(Buf, "abcdefghi\x00\x01\x01\x01\x01\x01\x01", 16); if (cmp) return false; p.Empty(); p.Write(s, sizeof(s)-1); GString r; r = p.Pop(); if (!r.Equals("1234567890abc")) return false; r = p.Pop(); if (!r.Equals("abcdefghijklmn")) return false; return true; } #endif ////////////////////////////////////////////////////////////////////////////////////////////////// GMemFile::GMemFile(int BlkSize) { CurPos = 0; Blocks = 0; BlockSize = BlkSize < 16 ? 16 : BlkSize; ZeroObj(Local); } GMemFile::~GMemFile() { Empty(); } GMemFile::Block *GMemFile::Get(int Index) { if (Blocks == 0 || Index < 0) return NULL; if (Index < GMEMFILE_BLOCKS) return Local[Index]; if (Index - GMEMFILE_BLOCKS >= Extra.Length()) { LgiAssert(!"Index beyond last block"); return NULL; } return Extra[Index - GMEMFILE_BLOCKS]; } GMemFile::Block *GMemFile::Create() { int Alloc = sizeof(Block) + BlockSize - 1; Alloc = LGI_ALLOC_ALIGN(Alloc); Block *b = (Block*) malloc(Alloc); if (!b) return NULL; b->Used = 0; b->Offset = Blocks * BlockSize; if (Blocks < GMEMFILE_BLOCKS) Local[Blocks] = b; else Extra.Add(b); Blocks++; return b; } void GMemFile::Empty() { CurPos = 0; for (int i=0; iUsed; } bool GMemFile::FreeBlock(Block *b) { if (!b) return false; ssize_t Idx = b->Offset / BlockSize; if (Idx < GMEMFILE_BLOCKS) { // Local block if (Local[Idx] != b || Idx < Blocks-1) { LgiAssert(!"Block index error."); return false; } free(b); Local[Idx] = NULL; Blocks--; return true; } // Extra block ssize_t Off = Idx - GMEMFILE_BLOCKS; if (Off != Extra.Length() - 1) { LgiAssert(!"Block index error."); return false; } free(b); Extra.DeleteAt(Off, true); Blocks--; return true; } int64 GMemFile::SetSize(int64 Size) { if (Size <= 0) { Empty(); } else { int64 CurSize = GetSize(); if (Size > CurSize) { // Increase size... int64 Diff = Size - CurSize; Block *b = GetLast(); if (b->Used < BlockSize) { // Add size to last incomplete block ssize_t Remaining = BlockSize - b->Used; ssize_t Add = MIN(Diff, Remaining); b->Used += Add; Diff -= Add; } while (Diff > 0) { // Add new blocks to cover the size... ssize_t Add = MIN(BlockSize, Diff); b = Create(); b->Used = Add; Diff -= Add; } } else { // Decrease size... uint64 Diff = CurSize - Size; while (Diff > 0 && Blocks > 0) { Block *b = GetLast(); if (!b) break; ssize_t Sub = MIN(b->Used, Diff); b->Used -= Sub; Diff -= Sub; if (b->Used == 0) FreeBlock(b); } } } return GetSize(); } int64 GMemFile::GetPos() { return CurPos; } int64 GMemFile::SetPos(int64 Pos) { if (Pos <= 0) return CurPos = 0; // Off the start of the structure ssize_t BlockIndex = Pos / BlockSize; if (BlockIndex >= Blocks) return CurPos = GetSize(); // Off the end of the structure if (BlockIndex >= 0 && BlockIndex < Blocks - 1) return CurPos = Pos; // Inside a full block Block *Last = GetLast(); uint64 Offset = Pos - Last->Offset; if (Offset >= Last->Used) return CurPos = Last->Offset + Last->Used; // End of last block return CurPos = Pos; // Inside the last block } ssize_t GMemFile::Read(void *Ptr, ssize_t Size, int Flags) { if (!Ptr || Size < 1) return 0; - uint8 *p = (uint8*) Ptr; - uint8 *end = p + Size; + uint8_t *p = (uint8_t*) Ptr; + uint8_t *end = p + Size; while (p < end) { int Cur = CurBlock(); if (Cur >= Blocks) break; Block *b = Get(Cur); // Where are we in the current block? ssize_t BlkOffset = CurPos - b->Offset; LgiAssert(b && BlkOffset >= 0 && BlkOffset <= (ssize_t)b->Used); ssize_t Remaining = b->Used - BlkOffset; if (Remaining > 0) { ssize_t Common = MIN(Remaining, end - p); memcpy(p, b->Data + BlkOffset, Common); CurPos += Common; p += Common; } else break; LgiAssert(p <= end); if (p >= end) // End of read buffer reached? break; // Exit loop } - return p - (uint8*) Ptr; + return p - (uint8_t*) Ptr; } ssize_t GMemFile::Write(const void *Ptr, ssize_t Size, int Flags) { if (!Ptr || Size < 1) return 0; - uint8 *p = (uint8*) Ptr; + uint8_t *p = (uint8_t*) Ptr; ssize_t len = Size; Block *b = GetLast(); if (b && b->Used < BlockSize) { // Any more space in the last block? ssize_t Remaining = BlockSize - b->Used; ssize_t Common = MIN(Remaining, Size); if (Common > 0) { memcpy(b->Data + b->Used, p, Common); p += Common; len -= Common; b->Used += Common; } } // Store remaining data into new blocks while (len > 0) { b = Create(); if (!b) break; ssize_t Common = MIN(BlockSize, len); memcpy(b->Data, p, Common); b->Used = Common; p += Common; len -= Common; } return Size - len; } diff --git a/src/common/General/GExeCheck.cpp b/src/common/General/GExeCheck.cpp --- a/src/common/General/GExeCheck.cpp +++ b/src/common/General/GExeCheck.cpp @@ -1,261 +1,261 @@ #include #ifdef WIN32 #include #include #endif #include "Lgi.h" ////////////////////////////////////////////////////////////////////////////////// /* typedef uint32 Elf32_Addr; typedef uint16 Elf32_Half; typedef uint32 Elf32_Off; typedef int32 Elf32_Sword; typedef uint32 Elf32_Word; */ struct ElfProgramHeader { uchar Magic[4]; // == { 0x7f, 'E', 'L', 'F' } - uint8 Class; // 1 == 32bit, 2 == 64bit - uint8 DataEnc; // 1 == LSB, 2 == MSB - uint8 FileVer; // 1 == current + uint8_t Class; // 1 == 32bit, 2 == 64bit + uint8_t DataEnc; // 1 == LSB, 2 == MSB + uint8_t FileVer; // 1 == current char Pad[8]; - uint8 IdentLen; // size of struct + uint8_t IdentLen; // size of struct uint16 Type; uint16 Machine; // > 0 AND <= 8 - uint32 Version; // == 1 - uint32 Entry; // Entry point or 0 if none - uint32 PhOff; - uint32 ShOff; - uint32 Flags; + uint32_t Version; // == 1 + uint32_t Entry; // Entry point or 0 if none + uint32_t PhOff; + uint32_t ShOff; + uint32_t Flags; uint16 EhSize; uint16 PhEntSize; uint16 PhNum; uint16 ShEntSize; uint16 ShNum; uint16 ShstRndx; }; class GExecuteCheck { char *Name; bool OwnFile; GStreamI *File; int64 Start, Len; int64 GetPos() { if (File) { return File->GetPos() - Start; } return -1; } bool SetPos(int64 to) { if (File) { int64 o = Start + to; if (File->SetPos(o) == o) { int64 p = GetPos(); return p >= 0 && p < Len; } } return false; } bool Read(void *d, int l) { if (File) { return File->Read(d, l) == l; } return false; } bool PeCheck() { char Buf[16]; // Check stub if (SetPos(0) && Read(Buf, 2) && Buf[0] == 'M' && Buf[1] == 'Z') { int NewHeaderPos; // Move to PE header if (SetPos(0x3c) && Read(&NewHeaderPos, 4) && SetPos(NewHeaderPos)) { #ifdef WIN32 // Read header IMAGE_NT_HEADERS Header; if (Read(&Header, sizeof(Header)) && Header.Signature == IMAGE_NT_SIGNATURE) { // Check header members if ( ( Header.FileHeader.Machine == IMAGE_FILE_MACHINE_I386 || Header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64 || Header.FileHeader.Machine == IMAGE_FILE_MACHINE_IA64 || Header.FileHeader.Machine == IMAGE_FILE_MACHINE_ARM ) && ( Header.OptionalHeader.Magic == 0x010B || Header.OptionalHeader.Magic == 0x020B ) && Header.OptionalHeader.SizeOfCode > 0 ) { /// Looks pretty good.. return true; } } #endif } } return false; } bool ElfCheck() { if (SetPos(0)) { ElfProgramHeader h; Read(&h, sizeof(h)); /* printf("Magic=%i,%c,%c,%c\n", h.Magic[0], h.Magic[1], h.Magic[2], h.Magic[3]); printf("Class=%i\n", h.Class); printf("DataEnc=%i\n", h.DataEnc); printf("FileVer=%i\n", h.FileVer); printf("IdentLen=%i\n", h.IdentLen); printf("Type=%i\n", h.Type); printf("Machine=%i\n", h.Machine); printf("Version=%i\n", h.Version); printf("Entry=%i\n", h.Entry); printf("PhOff=%i\n", h.PhOff); printf("ShOff=%i\n", h.ShOff); printf("Flags=%i\n", h.Flags); printf("EhSize=%i\n", h.EhSize); printf("PhEntSize=%i\n", h.PhEntSize); printf("PhNum=%i\n", h.PhNum); printf("ShEntSize=%i\n", h.ShEntSize); printf("ShNum=%i\n", h.ShNum); printf("ShstRndx=%i\n", h.ShstRndx); */ if (h.Magic[0] == 0x7f && h.Magic[1] == 'E' && h.Magic[2] == 'L' && h.Magic[3] == 'F' && (h.Class == 1 || h.Class == 2) && (h.DataEnc == 1 || h.DataEnc == 2) && h.FileVer == 1 && (h.Machine > 0 && h.Machine <= 8) && h.Version == 1) { return true; } } return false; } bool ScriptCheck() { if (Name) { char *Ext = LgiGetExtension(Name); if (Ext) { #ifdef WIN32 if (stricmp(Ext, "vbs") == 0 || stricmp(Ext, "pif") == 0 || stricmp(Ext, "bat") == 0) { return true; } #endif } } return false; } public: GExecuteCheck(const char *FileName) { Name = NewStr(FileName); OwnFile = true; Start = Len = 0; File = new GFile; if (File && FileName) { if (File->Open(FileName, O_READ)) { Len = File->GetSize(); } } } GExecuteCheck(const char *name, GStreamI *f, int64 start, int64 len) { Name = NewStr(name); OwnFile = false; File = f; Start = start; Len = len < 0 ? f->GetSize() : len; } ~GExecuteCheck() { DeleteArray(Name) if (OwnFile) { DeleteObj(File); } } bool IsExecutable() { return PeCheck() || ElfCheck() || ScriptCheck(); } }; LgiFunc bool LgiIsFileNameExecutable(const char *Str) { GExecuteCheck c(Str); return c.IsExecutable(); } LgiFunc bool LgiIsFileExecutable(const char *name, GStreamI *f, int64 Start, int64 Len) { GExecuteCheck c(name, f, Start, Len); return c.IsExecutable(); } diff --git a/src/common/General/GFileCommon.cpp b/src/common/General/GFileCommon.cpp --- a/src/common/General/GFileCommon.cpp +++ b/src/common/General/GFileCommon.cpp @@ -1,394 +1,394 @@ // // GFileCommon.cpp // // Created by Matthew Allen on 4/05/14. // Copyright (c) 2014 Memecode. All rights reserved. // #include "Lgi.h" #include "GVariant.h" ///////////////////////////////////////////////////////////////////////////////// bool GFileSystem::SetCurrentFolder(const char *PathName) { #ifdef WINDOWS bool Status = false; GAutoWString w(Utf8ToWide(PathName)); if (w) Status = ::SetCurrentDirectoryW(w) != 0; return Status; #else return chdir(PathName) == 0; #endif } GString GFileSystem::GetCurrentFolder() { GString Cwd; #ifdef WINDOWS char16 w[DIR_PATH_SIZE+1]; if (::GetCurrentDirectoryW(DIR_PATH_SIZE, w) > 0) Cwd = w; #else char p[MAX_PATH]; if (getcwd(p, sizeof(p))) Cwd = p; #endif return Cwd; } ///////////////////////////////////////////////////////////////////////////////// bool GFile::GetVariant(const char *Name, GVariant &Value, char *Array) { GDomProperty p = LgiStringToDomProp(Name); switch (p) { case ObjType: // Type: String Value = "File"; break; case ObjName: // Type: String Value = GetName(); break; case ObjLength: // Type: Int64 Value = GetSize(); break; case FilePos: // Type: Int64 Value = GetPos(); break; default: return false; } return true; } bool GFile::SetVariant(const char *Name, GVariant &Value, char *Array) { GDomProperty p = LgiStringToDomProp(Name); switch (p) { case ObjLength: SetSize(Value.CastInt64()); break; case FilePos: SetPos(Value.CastInt64()); break; default: return false; } return true; } bool GFile::CallMethod(const char *Name, GVariant *Dst, GArray &Arg) { GDomProperty p = LgiStringToDomProp(Name); switch (p) { case ObjLength: // Type: ([NewLength]) { if (Arg.Length() == 1) *Dst = SetSize(Arg[0]->CastInt64()); else *Dst = GetSize(); break; } case FilePos: // Type: ([NewPosition]) { if (Arg.Length() == 1) *Dst = SetPos(Arg[0]->CastInt64()); else *Dst = GetPos(); break; } case ObjType: { *Dst = "File"; break; } case FileOpen: // Type: (Path[, Mode]) { if (Arg.Length() >= 1) { int Mode = O_READ; if (Arg.Length() == 2) { char *m = Arg[1]->CastString(); if (m) { bool Rd = strchr(m, 'r') != NULL; bool Wr = strchr(m, 'w') != NULL; if (Rd && Wr) Mode = O_READWRITE; else if (Wr) Mode = O_WRITE; else Mode = O_READ; } } *Dst = Open(Arg[0]->CastString(), Mode); } break; } case FileClose: { *Dst = Close(); break; } case FileRead: // Type: ([ReadLength[, ReadType = 0 - string, 1 - integer]]) { int64 RdLen = 0; int RdType = 0; // 0 - string, 1 - int Dst->Empty(); switch (Arg.Length()) { default: case 0: RdLen = GetSize() - GetPos(); break; case 2: RdType = Arg[1]->CastInt32(); // fall thru case 1: RdLen = Arg[0]->CastInt64(); break; } if (RdType) { // Int type switch (RdLen) { case 1: { - uint8 i; + uint8_t i; if (Read(&i, sizeof(i)) == sizeof(i)) *Dst = i; break; } case 2: { uint16 i; if (Read(&i, sizeof(i)) == sizeof(i)) *Dst = i; break; } case 4: { - uint32 i; + uint32_t i; if (Read(&i, sizeof(i)) == sizeof(i)) *Dst = (int)i; break; } case 8: { int64 i; if (Read(&i, sizeof(i)) == sizeof(i)) *Dst = i; break; } } } else if (RdLen > 0) { // String type if ((Dst->Value.String = new char[RdLen + 1])) { ssize_t r = Read(Dst->Value.String, (int)RdLen); if (r > 0) { Dst->Type = GV_STRING; Dst->Value.String[r] = 0; } else { DeleteArray(Dst->Value.String); } } } else *Dst = -1; break; } case FileWrite: // Type: (Data[, WriteLength]) { GVariant *v; if (Arg.Length() < 1 || Arg.Length() > 2 || !(v = Arg[0])) { *Dst = 0; return true; } switch (Arg.Length()) { case 1: { // Auto-size write length to the variable. switch (v->Type) { case GV_INT32: *Dst = Write(&v->Value.Int, sizeof(v->Value.Int)); break; case GV_INT64: *Dst = Write(&v->Value.Int64, sizeof(v->Value.Int64)); break; case GV_STRING: *Dst = Write(&v->Value.String, strlen(v->Value.String)); break; case GV_WSTRING: *Dst = Write(&v->Value.WString, StrlenW(v->Value.WString) * sizeof(char16)); break; default: *Dst = 0; return true; } break; } case 2: { int64 WrLen = Arg[1]->CastInt64(); switch (v->Type) { case GV_INT32: { if (WrLen == 1) { - uint8 i = v->Value.Int; + uint8_t i = v->Value.Int; *Dst = Write(&i, sizeof(i)); } else if (WrLen == 2) { uint16 i = v->Value.Int; *Dst = Write(&i, sizeof(i)); } else { *Dst = Write(&v->Value.Int, sizeof(v->Value.Int)); } break; } case GV_INT64: { if (WrLen == 1) { - uint8 i = (uint8) v->Value.Int64; + uint8_t i = (uint8_t) v->Value.Int64; *Dst = Write(&i, sizeof(i)); } else if (WrLen == 2) { uint16 i = (uint16) v->Value.Int64; *Dst = Write(&i, sizeof(i)); } else if (WrLen == 4) { - uint32 i = (uint32)v->Value.Int64; + uint32_t i = (uint32_t)v->Value.Int64; *Dst = Write(&i, sizeof(i)); } else { *Dst = Write(&v->Value.Int64, sizeof(v->Value.Int64)); } break; } case GV_STRING: { size_t Max = strlen(v->Value.String) + 1; *Dst = Write(&v->Value.String, MIN(Max, (size_t)WrLen)); break; } case GV_WSTRING: { size_t Max = (StrlenW(v->Value.WString) + 1) * sizeof(char16); *Dst = Write(&v->Value.WString, MIN(Max, (size_t)WrLen)); break; } default: { *Dst = 0; return true; } } break; } default: { *Dst = 0; return true; } } break; } default: return false; } return true; } const char *LgiGetLeaf(const char *Path) { if (!Path) return NULL; const char *l = NULL; for (const char *s = Path; *s; s++) { if (*s == '/' || *s == '\\') l = s; } return l ? l + 1 : Path; } char *LgiGetLeaf(char *Path) { if (!Path) return NULL; char *l = NULL; for (char *s = Path; *s; s++) { if (*s == '/' || *s == '\\') l = s; } return l ? l + 1 : Path; } GString LGetPhysicalDevice(const char *Path) { GString Ph; #ifdef WINDOWS GAutoWString w(Utf8ToWide(Path)); char16 VolPath[256]; if (GetVolumePathNameW(w, VolPath, CountOf(VolPath))) { char16 Name[256] = L"", FsName[256] = L""; DWORD VolumeSerialNumber = 0, MaximumComponentLength = 0, FileSystemFlags = 0; if (GetVolumeInformationW(VolPath, Name, CountOf(Name), &VolumeSerialNumber, &MaximumComponentLength, &FileSystemFlags, FsName, CountOf(FsName))) { if (VolumeSerialNumber) Ph.Printf("/volume/%x", VolumeSerialNumber); else Ph = VolPath; } } #else struct stat s; ZeroObj(s); if (!lstat(Path, &s)) { Ph.Printf("/dev/%i", s.st_dev); // Find dev in '/proc/partitions'? } #endif return Ph; } diff --git a/src/common/INet/GUri.cpp b/src/common/INet/GUri.cpp --- a/src/common/INet/GUri.cpp +++ b/src/common/INet/GUri.cpp @@ -1,344 +1,344 @@ #include "Lgi.h" #include "INet.h" #include "GRegKey.h" ///////////////////////////////////////////////////////////////////////////////// static const char *Ws = " \t\r\n"; #define SkipWs(s) while (*s && strchr(Ws, *s)) s++; GUri::GUri(const char *uri) { Protocol = 0; User = 0; Pass = 0; Host = 0; Port = 0; Path = 0; Anchor = 0; if (uri) Set(uri); } GUri::~GUri() { Empty(); } GUri &GUri::operator =(const GUri &u) { Empty(); Protocol = NewStr(u.Protocol); User = NewStr(u.User); Pass = NewStr(u.Pass); Host = NewStr(u.Host); Path = NewStr(u.Path); Anchor = NewStr(u.Anchor); return *this; } void GUri::Empty() { Port = 0; DeleteArray(Protocol); DeleteArray(User); DeleteArray(Pass); DeleteArray(Host); DeleteArray(Path); DeleteArray(Anchor); } GAutoString GUri::GetUri() { const char *Empty = ""; GStringPipe p; if (Protocol) { p.Print("%s://", Protocol); } if (User || Pass) { GAutoString UserEnc = Encode(User, "@:"); GAutoString PassEnc = Encode(Pass, "@:"); p.Print("%s:%s@", UserEnc?UserEnc:Empty, PassEnc?PassEnc:Empty); } if (Host) { p.Write(Host, (int)strlen(Host)); } if (Port) { p.Print(":%i", Port); } if (Path) { GAutoString e = Encode(Path); char *s = e ? e : Path; p.Print("%s%s", *s == '/' ? "" : "/", s); } if (Anchor) p.Print("#%s", Anchor); GAutoString a(p.NewStr()); return a; } bool GUri::Set(const char *uri) { if (!uri) return false; Empty(); const char *s = uri; SkipWs(s); // Scan ahead and check for protocol... const char *p = s; while (*s && IsAlpha(*s)) s++; if (s[0] == ':' && (s - p) > 1) { Protocol = NewStr(p, s - p); s++; if (*s == '/') s++; if (*s == '/') s++; } else { // No protocol, so assume it's a host name or path s = p; } // Check for path bool HasPath = false; if ( (s[0] && s[1] == ':') || (s[0] == '/') || (s[0] == '\\') ) { HasPath = true; } else { // Scan over the host name p = s; while ( *s && *s > ' ' && *s < 127 && *s != '/' && *s != '\\') { s++; } Host = NewStr(p, s - p); if (Host) { char *At = strchr(Host, '@'); if (At) { *At++ = 0; char *Col = strchr(Host, ':'); if (Col) { *Col++ = 0; Pass = NewStr(Col); } User = NewStr(Host); memmove(Host, At, strlen(At) + 1); } char *Col = strchr(Host, ':'); if (Col) { *Col++ = 0; Port = atoi(Col); } } HasPath = *s == '/'; } if (HasPath) { const char *Start = s; while (*s && *s != '#') s++; if (*s) { Path = NewStr(Start, s - Start); Anchor = NewStr(s + 1); } else { Path = NewStr(Start); } #if 0 // This decodes the path from %## encoding to raw characters. // However sometimes we need the encoded form. So instead of // doing the conversion here the caller has to do it now. char *i = Path, *o = Path; while (*i) { if (*i == '%' && i[1] && i[2]) { char h[3] = {i[1], i[2], 0}; *o++ = htoi(h); i+=2; } else { *o++ = *i; } i++; } *o = 0; #endif } return Host || Path; } GAutoString GUri::Encode(const char *s, const char *ExtraCharsToEncode) { GStringPipe p(256); if (s) { while (*s) { if (*s == ' ' || (ExtraCharsToEncode && strchr(ExtraCharsToEncode, *s))) { char h[4]; - sprintf_s(h, sizeof(h), "%%%2.2X", (uint32)(uchar)*s++); + sprintf_s(h, sizeof(h), "%%%2.2X", (uint32_t)(uchar)*s++); p.Write(h, 3); } else { p.Write(s++, 1); } } } return GAutoString(p.NewStr()); } GUri::StrMap GUri::Params() { StrMap m; if (Path) { const char *q = strchr(Path, '?'); if (q++) { auto Parts = GString(q).SplitDelimit("&"); for (auto p : Parts) { auto Var = p.Split("=", 1); if (Var.Length() == 2) m.Add(Var[0], Var[1]); } } } return m; } GAutoString GUri::Decode(char *s) { GStringPipe p(256); if (s) { while (*s) { if (s[0] == '%' && s[1] && s[2]) { char h[3] = { s[1], s[2], 0 }; char c = htoi(h); p.Write(&c, 1); s += 3; } else { p.Write(s++, 1); } } } return GAutoString(p.NewStr()); } #ifdef MAC int CFNumberRefToInt(CFNumberRef r, int Default = 0) { int i = Default; if (r && CFGetTypeID(r) == CFNumberGetTypeID()) { CFNumberGetValue(r, kCFNumberIntType, &r); } return i; } #endif GProxyUri::GProxyUri() { #if defined(WIN32) GRegKey k(false, "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"); if (k.IsOk()) { - uint32 Enabled = 0; + uint32_t Enabled = 0; if (k.GetInt("ProxyEnable", Enabled) && Enabled) { char *p = k.GetStr("ProxyServer"); if (p) { Set(p); } } } #elif defined LINUX char *HttpProxy = getenv("http_proxy"); if (HttpProxy) { Set(HttpProxy); } #elif defined MAC // CFDictionaryRef Proxies = SCDynamicStoreCopyProxies(0); // if (!Proxies) // LgiTrace("%s:%i - SCDynamicStoreCopyProxies failed.\n", _FL); // else // { // int enable = CFNumberRefToInt((CFNumberRef) CFDictionaryGetValue(Proxies, kSCPropNetProxiesHTTPEnable)); // if (enable) // { // #ifdef COCOA // LgiAssert(!"Fixme"); // #else // Host = CFStringToUtf8((CFStringRef) CFDictionaryGetValue(Proxies, kSCPropNetProxiesHTTPProxy)); // #endif // Port = CFNumberRefToInt((CFNumberRef) CFDictionaryGetValue(Proxies, kSCPropNetProxiesHTTPPort)); // } // // CFRelease(Proxies); // } #else #warning "Impl getting OS proxy here." #endif } diff --git a/src/common/INet/INet.cpp b/src/common/INet/INet.cpp --- a/src/common/INet/INet.cpp +++ b/src/common/INet/INet.cpp @@ -1,1827 +1,1827 @@ /*hdr ** FILE: INet.cpp ** AUTHOR: Matthew Allen ** DATE: 28/5/98 ** DESCRIPTION: Internet sockets ** ** Copyright (C) 1998, Matthew Allen ** fret@memecode.com */ #define _WINSOCK_DEPRECATED_NO_WARNINGS 1 #ifdef LINUX #include #include #include #endif #include #include "GFile.h" #include "INet.h" #include "GToken.h" #include "GString.h" #include "LgiCommon.h" #include "LgiOsClasses.h" #include "GRegKey.h" #ifdef MAC #include #include #include #include #include #endif #define USE_BSD_SOCKETS 1 #define DEBUG_CONNECT 0 #define ETIMEOUT 400 #define PROTO_UDP 0x100 #define PROTO_BROADCAST 0x200 #if defined WIN32 #include #include #include #include typedef HOSTENT HostEnt; typedef int socklen_t; typedef unsigned long in_addr_t; typedef BOOL option_t; #define MSG_NOSIGNAL 0 #ifndef EWOULDBLOCK #define EWOULDBLOCK WSAEWOULDBLOCK #endif #ifndef EISCONN #define EISCONN WSAEISCONN #endif #define OsAddr S_un.S_addr #elif defined BEOS #include #include #include #include #include #include #define SOCKET_ERROR -1 #define MSG_NOSIGNAL 0 typedef struct hostent HostEnt; typedef int option_t; // typedef int socklen_t; #define OsAddr s_addr #elif defined POSIX #include #include #include #include #include #include #include #include #include #include "LgiCommon.h" #include #define SOCKET_ERROR -1 typedef hostent HostEnt; typedef int option_t; #define OsAddr s_addr #endif #include "LgiNetInc.h" /////////////////////////////////////////////////////////////////////////////// #ifdef BEOS uint32 inet_addr(char *HostAddr) { uint32 s = 0; GToken Ip(HostAddr, "."); if (Ip.Length() == 4) { uchar *IpAddress = (uchar*)&s; for (int i=0; i<4; i++) { IpAddress[i] = atoi(Ip[i]); } } return s; } #endif /////////////////////////////////////////////////////////////////////////////// #ifdef WIN32 static bool SocketsOpen = false; #endif bool StartNetworkStack() { #ifdef WIN32 #ifndef WINSOCK_VERSION #define WINSOCK_VERSION MAKEWORD(2,2) #endif if (!SocketsOpen) { // Start sockets WSADATA WsaData; int Result = WSAStartup(WINSOCK_VERSION, &WsaData); if (!Result) { if (WsaData.wVersion == WINSOCK_VERSION) { SocketsOpen = Result == 0; } else { WSACleanup(); return false; } } } #endif return true; } void StopNetworkStack() { #ifdef WIN32 if (SocketsOpen) { WSACleanup(); SocketsOpen = false; } #endif } #ifdef WIN32 #include "..\..\Win32\INet\MibAccess.h" #endif bool GSocket::EnumInterfaces(GArray &Out) { bool Status = false; StartNetworkStack(); #ifdef WIN32 MibII m; m.Init(); MibInterface Intf[16]; int Count = m.GetInterfaces(Intf, CountOf(Intf)); if (Count) { for (int i=0; iifa_next) { if (a->ifa_addr->sa_family == AF_INET) { sockaddr_in *in = (sockaddr_in*)a->ifa_addr; sockaddr_in *mask = (sockaddr_in*)a->ifa_netmask; auto &Intf = Out.New(); Intf.Ip4 = ntohl(in->sin_addr.s_addr); Intf.Netmask4 = ntohl(mask->sin_addr.s_addr); Intf.Name = a->ifa_name; Status = true; } } freeifaddrs(addrs); } #endif return Status; } //////////////////////////////////////////////////////////////////////////////////////////////// class GSocketImplPrivate : public LCancel { public: // Data int Blocking : 1; int NoDelay : 1; int Udp : 1; int Broadcast : 1; int LogType; char *LogFile; int Timeout; OsSocket Socket; int LastError; GString ErrStr; LCancel *Cancel; GSocketImplPrivate() { Blocking = true; NoDelay = false; Udp = false; Cancel = this; LogType = NET_LOG_NONE; LogFile = 0; Timeout = -1; Socket = INVALID_SOCKET; LastError = 0; } ~GSocketImplPrivate() { DeleteArray(LogFile); } }; GSocket::GSocket(GStreamI *logger, void *unused_param) { StartNetworkStack(); BytesWritten = 0; BytesRead = 0; d = new GSocketImplPrivate; } GSocket::~GSocket() { Close(); DeleteObj(d); } bool GSocket::IsOK() { return #ifndef __llvm__ this != 0 && #endif d != 0; } LCancel *GSocket::GetCancel() { return d->Cancel; } void GSocket::SetCancel(LCancel *c) { d->Cancel = c; printf("d->Cancel=%p\n", c); } void GSocket::OnDisconnect() { } OsSocket GSocket::ReleaseHandle() { auto h = d->Socket; d->Socket = INVALID_SOCKET; return h; } OsSocket GSocket::Handle(OsSocket Set) { if (Set != INVALID_SOCKET) { d->Socket = Set; } return d->Socket; } bool GSocket::IsOpen() { if (ValidSocket(d->Socket) && !d->Cancel->IsCancelled()) { return true; } return false; } int GSocket::GetTimeout() { return d->Timeout; } void GSocket::SetTimeout(int ms) { d->Timeout = ms; } bool GSocket::IsReadable(int TimeoutMs) { // Assign to local var to avoid a thread changing it // on us between the validity check and the select. // Which is important because a socket value of -1 // (ie invalid) will crash the FD_SET macro. OsSocket s = d->Socket; if (ValidSocket(s) && !d->Cancel->IsCancelled()) { #ifdef LINUX // Because Linux doesn't return from select() when the socket is // closed elsewhere we have to do something different... damn Linux, // why can't you just like do the right thing? struct pollfd fds; fds.fd = s; fds.events = POLLIN | POLLRDHUP | POLLERR; fds.revents = 0; int r = poll(&fds, 1, TimeoutMs); if (r > 0) { return fds.revents != 0; } else if (r < 0) { Error(); } #else struct timeval t = {TimeoutMs / 1000, (TimeoutMs % 1000) * 1000}; fd_set r; FD_ZERO(&r); FD_SET(s, &r); int v = select((int)s+1, &r, 0, 0, &t); if (v > 0 && FD_ISSET(s, &r)) { return true; } else if (v < 0) { Error(); } #endif } else LgiTrace("%s:%i - Not a valid socket.\n", _FL); return false; } bool GSocket::IsWritable(int TimeoutMs) { // Assign to local var to avoid a thread changing it // on us between the validity check and the select. // Which is important because a socket value of -1 // (ie invalid) will crash the FD_SET macro. OsSocket s = d->Socket; if (ValidSocket(s) && !d->Cancel->IsCancelled()) { struct timeval t = {TimeoutMs / 1000, (TimeoutMs % 1000) * 1000}; fd_set set; FD_ZERO(&set); FD_SET(s, &set); int ret = select((int)s+1, 0, &set, 0, &t); // printf("select ret=%i isset=%i\n", ret, FD_ISSET(s, &set)); if (ret > 0 && FD_ISSET(s, &set)) { return true; } } return false; } bool GSocket::CanAccept(int TimeoutMs) { return IsReadable(TimeoutMs); } bool GSocket::IsBlocking() { return d->Blocking != 0; } void GSocket::IsBlocking(bool block) { if (d->Blocking ^ block) { d->Blocking = block; #if defined WIN32 ulong NonBlocking = !block; ioctlsocket(d->Socket, FIONBIO, &NonBlocking); #elif defined POSIX fcntl(d->Socket, F_SETFL, d->Blocking ? 0 : O_NONBLOCK); #elif defined BEOS uint32 b = d->Blocking ? 0 : -1; setsockopt(d->Socket, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b)); #else #error Impl me. #endif } } bool GSocket::IsDelayed() { return !d->NoDelay; } void GSocket::IsDelayed(bool Delay) { bool NoDelay = !Delay; if (d->NoDelay ^ NoDelay) { d->NoDelay = NoDelay; option_t i = d->NoDelay != 0; setsockopt(d->Socket, IPPROTO_TCP, TCP_NODELAY, (const char*)&i, sizeof(i)); } } int GSocket::GetLocalPort() { struct sockaddr_in addr; socklen_t size; ZeroObj(addr); size = sizeof(addr); if ((getsockname(Handle(), (sockaddr*)&addr, &size)) >= 0) { return ntohs(addr.sin_port); } return 0; } bool GSocket::GetLocalIp(char *IpAddr) { if (IpAddr) { struct sockaddr_in addr; socklen_t size; size = sizeof(addr); if ((getsockname(Handle(), (sockaddr*)&addr, &size)) < 0) return false; if (addr.sin_addr.s_addr == INADDR_ANY) return false; uchar *a = (uchar*)&addr.sin_addr.s_addr; sprintf_s( IpAddr, 16, "%i.%i.%i.%i", a[0], a[1], a[2], a[3]); return true; } return false; } -bool GSocket::GetRemoteIp(uint32 *IpAddr) +bool GSocket::GetRemoteIp(uint32_t *IpAddr) { if (IpAddr) { struct sockaddr_in a; socklen_t addrlen = sizeof(a); if (!getpeername(Handle(), (sockaddr*)&a, &addrlen)) { *IpAddr = ntohl(a.sin_addr.s_addr); return true; } } return false; } bool GSocket::GetRemoteIp(char *IpAddr) { if (!IpAddr) return false; - uint32 Ip = 0; + uint32_t Ip = 0; if (!GetRemoteIp(&Ip)) return false; sprintf_s( IpAddr, 16, "%u.%u.%u.%u", (Ip >> 24) & 0xff, (Ip >> 16) & 0xff, (Ip >> 8) & 0xff, (Ip) & 0xff); return false; } int GSocket::GetRemotePort() { struct sockaddr_in a; socklen_t addrlen = sizeof(a); if (!getpeername(Handle(), (sockaddr*)&a, &addrlen)) { return a.sin_port; } return 0; } int GSocket::Open(const char *HostAddr, int Port) { int Status = -1; Close(); if (HostAddr) { BytesWritten = 0; BytesRead = 0; sockaddr_in RemoteAddr; HostEnt *Host = 0; in_addr_t IpAddress = 0; ZeroObj(RemoteAddr); #ifdef WIN32 d->Socket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, WSA_FLAG_OVERLAPPED); #else d->Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); #endif if (ValidSocket(d->Socket)) { GArray Buf(512); #if !defined(MAC) && !defined(BEOS) option_t i; int sz = sizeof(i); int r = getsockopt(d->Socket, IPPROTO_TCP, TCP_NODELAY, (char*)&i, &sz); if (d->NoDelay ^ i) { i = d->NoDelay != 0; setsockopt(d->Socket, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i)); } #endif if (IsDigit(*HostAddr) && strchr(HostAddr, '.')) { // Ip address // IpAddress = inet_addr(HostAddr); if (!inet_pton(AF_INET, HostAddr, &IpAddress)) { Error(); return 0; } /* This seems complete unnecessary? -fret Dec 2018 #if defined(WIN32) || defined(BEOS) Host = c((const char*) &IpAddress, 4, AF_INET); if (!Host) Error(); #else Host = gethostbyaddr ( #ifdef MAC HostAddr, #else &IpAddress, #endif 4, AF_INET ); #endif */ } else { // Name address #ifdef LINUX Host = new HostEnt; if (Host) { memset(Host, 0, sizeof(*Host)); HostEnt *Result = 0; int Err = 0; int Ret; while ( ( !GetCancel() || !GetCancel()->IsCancelled() ) && ( Ret = gethostbyname_r(HostAddr, Host, &Buf[0], Buf.Length(), &Result, &Err) ) == ERANGE ) { Buf.Length(Buf.Length() << 1); } if (Ret) { char *ErrStr = GetErrorName(Err); printf("%s:%i - gethostbyname_r('%s') returned %i, %i, %s\n", _FL, HostAddr, Ret, Err, ErrStr); DeleteObj(Host); } } #if DEBUG_CONNECT printf("%s:%i - Host=%p\n", __FILE__, __LINE__, Host); #endif #else Host = gethostbyname(HostAddr); #endif if (!Host) { Error(); Close(); return false; } } if (1) { RemoteAddr.sin_family = AF_INET; RemoteAddr.sin_port = htons(Port); if (Host) { if (Host->h_addr_list && Host->h_addr_list[0]) { memcpy(&RemoteAddr.sin_addr, Host->h_addr_list[0], sizeof(in_addr) ); } else return false; } else { memcpy(&RemoteAddr.sin_addr, &IpAddress, sizeof(IpAddress) ); } #ifdef WIN32 if (d->Timeout < 0) { // Do blocking connect Status = connect(d->Socket, (sockaddr*) &RemoteAddr, sizeof(sockaddr_in)); } else #endif { #define CONNECT_LOGGING 0 // Setup the connect bool Block = IsBlocking(); if (Block) { #if CONNECT_LOGGING printf("%p - Setting non blocking\n", d->Socket); #endif IsBlocking(false); } // Do initial connect to kick things off.. #if CONNECT_LOGGING printf("%p - Doing initial connect to %s:%i\n", d->Socket, HostAddr, Port); #endif Status = connect(d->Socket, (sockaddr*) &RemoteAddr, sizeof(sockaddr_in)); #if CONNECT_LOGGING printf("%p - Initial connect=%i Block=%i\n", d->Socket, Status, Block); #endif // Wait for the connect to finish? if (Status && Block) { Error(Host); #ifdef WIN32 // yeah I know... wtf? (http://itamarst.org/writings/win32sockets.html) #define IsWouldBlock() (d->LastError == EWOULDBLOCK || d->LastError == WSAEINVAL) #else #define IsWouldBlock() (d->LastError == EWOULDBLOCK || d->LastError == EINPROGRESS) #endif #if CONNECT_LOGGING printf("%p - IsWouldBlock()=%i d->LastError=%i\n", d->Socket, IsWouldBlock(), d->LastError); #endif int64 End = LgiCurrentTime() + (d->Timeout > 0 ? d->Timeout : 30000); while ( !d->Cancel->IsCancelled() && ValidSocket(d->Socket) && IsWouldBlock()) { int64 Remaining = End - LgiCurrentTime(); #if CONNECT_LOGGING printf("%p - Remaining "LPrintfInt64"\n", d->Socket, Remaining); #endif printf("open d->Cancel=%p, %i\n", d->Cancel, d->Cancel->IsCancelled()); if (Remaining < 0) { #if CONNECT_LOGGING printf("%p - Leaving loop\n", d->Socket); #endif break; } if (IsWritable((int)MIN(Remaining, 1000))) { // Should be ready to connect now... #if CONNECT_LOGGING printf("%p - Secondary connect...\n", d->Socket); #endif Status = connect(d->Socket, (sockaddr*) &RemoteAddr, sizeof(sockaddr_in)); #if CONNECT_LOGGING printf("%p - Secondary connect=%i\n", d->Socket, Status); #endif if (Status != 0) { Error(Host); #if CONNECT_LOGGING printf("%p - Connect=%i Err=%i\n", d->Socket, Status, d->LastError); #endif if (IsWouldBlock()) continue; if (d->LastError == EISCONN) Status = 0; break; } else { #if CONNECT_LOGGING printf("%p - Connected...\n", d->Socket); #endif break; } } else { #if CONNECT_LOGGING printf("%p - Timout...\n", d->Socket); #endif } } } if (Block) IsBlocking(true); } if (!Status) { char Info[256]; sprintf_s(Info, sizeof(Info), "[INET] Socket Connect: %s [%i.%i.%i.%i], port: %i", HostAddr, (RemoteAddr.sin_addr.s_addr) & 0xFF, (RemoteAddr.sin_addr.s_addr >> 8) & 0xFF, (RemoteAddr.sin_addr.s_addr >> 16) & 0xFF, (RemoteAddr.sin_addr.s_addr >> 24) & 0xFF, Port); OnInformation(Info); } } if (Status) { #ifdef WIN32 closesocket(d->Socket); #else close(d->Socket); #endif d->Socket = INVALID_SOCKET; } } else { Error(); } } return Status == 0; } bool GSocket::Listen(int Port) { Close(); d->Socket = socket(AF_INET, SOCK_STREAM, 0); if (d->Socket >= 0) { BytesWritten = 0; BytesRead = 0; sockaddr Addr; sockaddr_in *a = (sockaddr_in*) &Addr; ZeroObj(Addr); a->sin_family = AF_INET; a->sin_port = htons(Port); a->sin_addr.OsAddr = INADDR_ANY; if (bind(d->Socket, &Addr, sizeof(Addr)) >= 0) { if (listen(d->Socket, SOMAXCONN) != SOCKET_ERROR) { return true; } else { Error(); } } else { Error(); } } else { Error(); } return false; } bool GSocket::Accept(GSocketI *c) { if (!c) { LgiAssert(0); return false; } OsSocket NewSocket = INVALID_SOCKET; sockaddr Address; /* int Length = sizeof(Address); NewSocket = accept(d->Socket, &Address, &Length); */ // int Loop = 0; socklen_t Length = sizeof(Address); uint64 Start = LgiCurrentTime(); while ( !d->Cancel->IsCancelled() && ValidSocket(d->Socket)) { if (IsReadable(100)) { NewSocket = accept(d->Socket, &Address, &Length); break; } else if (d->Timeout > 0) { uint64 Now = LgiCurrentTime(); if (Now - Start >= d->Timeout) { GString s; s.Printf("Accept timeout after %.1f seconds.", ((double)(Now-Start)) / 1000.0); OnInformation(s); return false; } } } if (!ValidSocket(NewSocket)) return false; return ValidSocket(c->Handle(NewSocket)); } int GSocket::Close() { if (ValidSocket(d->Socket)) { #if defined WIN32 closesocket(d->Socket); #else close(d->Socket); #endif d->Socket = INVALID_SOCKET; OnDisconnect(); } return true; } void GSocket::Log(const char *Msg, ssize_t Ret, const char *Buf, ssize_t Len) { if (d->LogFile) { GFile f; if (f.Open(d->LogFile, O_WRITE)) { f.Seek(f.GetSize(), SEEK_SET); switch (d->LogType) { case NET_LOG_HEX_DUMP: { char s[256]; f.Write(s, sprintf_s(s, sizeof(s), "%s = %i\r\n", Msg, (int)Ret)); for (int i=0; iSocket) || !Data || d->Cancel->IsCancelled()) return -1; int Status = 0; if (d->Timeout < 0 || IsWritable(d->Timeout)) { Status = (int)send ( d->Socket, (char*)Data, (int) Len, Flags #ifndef MAC | MSG_NOSIGNAL #endif ); } if (Status < 0) Error(); else if (Status == 0) OnDisconnect(); else { if (Status < Len) { // Just in case it's a string lets be safe ((char*)Data)[Status] = 0; } BytesWritten += Status; OnWrite((char*)Data, Status); } return Status; } ssize_t GSocket::Read(void *Data, ssize_t Len, int Flags) { if (!ValidSocket(d->Socket) || !Data || d->Cancel->IsCancelled()) return -1; ssize_t Status = -1; if (d->Timeout < 0 || IsReadable(d->Timeout)) { Status = recv(d->Socket, (char*)Data, (int) Len, Flags #ifdef MSG_NOSIGNAL | MSG_NOSIGNAL #endif ); } Log("Read", (int)Status, (char*)Data, Status>0 ? Status : 0); if (Status < 0) Error(); else if (Status == 0) OnDisconnect(); else { if (Status < Len) { // Just in case it's a string lets be safe ((char*)Data)[Status] = 0; } BytesRead += Status; OnRead((char*)Data, (int)Status); } return (int)Status; } void GSocket::OnError(int ErrorCode, const char *ErrorDescription) { d->ErrStr.Printf("Error(%i): %s", ErrorCode, ErrorDescription); } const char *GSocket::GetErrorString() { return d->ErrStr; } int GSocket::Error(void *Param) { // Get the most recent error. if (!(d->LastError = #ifdef WIN32 WSAGetLastError() #else errno #endif )) return 0; // These are not really errors... if (d->LastError == EWOULDBLOCK || d->LastError == EISCONN) return 0; class ErrorMsg { public: int Code; const char *Msg; } ErrorCodes[] = { {0, "Socket disconnected."}, #if defined WIN32 {WSAEACCES, "Permission denied."}, {WSAEADDRINUSE, "Address already in use."}, {WSAEADDRNOTAVAIL, "Cannot assign requested address."}, {WSAEAFNOSUPPORT, "Address family not supported by protocol family."}, {WSAEALREADY, "Operation already in progress."}, {WSAECONNABORTED, "Software caused connection abort."}, {WSAECONNREFUSED, "Connection refused."}, {WSAECONNRESET, "Connection reset by peer."}, {WSAEDESTADDRREQ, "Destination address required."}, {WSAEFAULT, "Bad address."}, {WSAEHOSTDOWN, "Host is down."}, {WSAEHOSTUNREACH, "No route to host."}, {WSAEINPROGRESS, "Operation now in progress."}, {WSAEINTR, "Interrupted function call."}, {WSAEINVAL, "Invalid argument."}, {WSAEISCONN, "Socket is already connected."}, {WSAEMFILE, "Too many open files."}, {WSAEMSGSIZE, "Message too long."}, {WSAENETDOWN, "Network is down."}, {WSAENETRESET, "Network dropped connection on reset."}, {WSAENETUNREACH, "Network is unreachable."}, {WSAENOBUFS, "No buffer space available."}, {WSAENOPROTOOPT, "Bad protocol option."}, {WSAENOTCONN, "Socket is not connected."}, {WSAENOTSOCK, "Socket operation on non-socket."}, {WSAEOPNOTSUPP, "Operation not supported."}, {WSAEPFNOSUPPORT, "Protocol family not supported."}, {WSAEPROCLIM, "Too many processes."}, {WSAEPROTONOSUPPORT,"Protocol not supported."}, {WSAEPROTOTYPE, "Protocol wrong type for socket."}, {WSAESHUTDOWN, "Cannot send after socket shutdown."}, {WSAESOCKTNOSUPPORT,"Socket type not supported."}, {WSAETIMEDOUT, "Connection timed out."}, {WSAEWOULDBLOCK, "Operation would block."}, {WSAHOST_NOT_FOUND, "Host not found."}, {WSANOTINITIALISED, "Successful WSAStartup not yet performed."}, {WSANO_DATA, "Valid name, no data record of requested type."}, {WSANO_RECOVERY, "This is a non-recoverable error."}, {WSASYSNOTREADY, "Network subsystem is unavailable."}, {WSATRY_AGAIN, "Non-authoritative host not found."}, {WSAVERNOTSUPPORTED,"WINSOCK.DLL version out of range."}, {WSAEDISCON, "Graceful shutdown in progress."}, #else {EACCES, "Permission denied."}, {EADDRINUSE, "Address already in use."}, {EADDRNOTAVAIL, "Cannot assign requested address."}, {EAFNOSUPPORT, "Address family not supported by protocol family."}, {EALREADY, "Operation already in progress."}, {ECONNABORTED, "Software caused connection abort."}, {ECONNREFUSED, "Connection refused."}, {ECONNRESET, "Connection reset by peer."}, {EFAULT, "Bad address."}, {EHOSTUNREACH, "No route to host."}, {EINPROGRESS, "Operation now in progress."}, {EINTR, "Interrupted function call."}, {EINVAL, "Invalid argument."}, {EISCONN, "Socket is already connected."}, {EMFILE, "Too many open files."}, {EMSGSIZE, "Message too long."}, {ENETDOWN, "Network is down."}, {ENETRESET, "Network dropped connection on reset."}, {ENETUNREACH, "Network is unreachable."}, {ENOBUFS, "No buffer space available."}, {ENOPROTOOPT, "Bad protocol option."}, {ENOTCONN, "Socket is not connected."}, {ENOTSOCK, "Socket operation on non-socket."}, {EOPNOTSUPP, "Operation not supported."}, {EPFNOSUPPORT, "Protocol family not supported."}, {EPROTONOSUPPORT, "Protocol not supported."}, {EPROTOTYPE, "Protocol wrong type for socket."}, {ESHUTDOWN, "Cannot send after socket shutdown."}, {ETIMEDOUT, "Connection timed out."}, {EWOULDBLOCK, "Resource temporarily unavailable."}, {HOST_NOT_FOUND, "Host not found."}, {NO_DATA, "Valid name, no data record of requested type."}, {NO_RECOVERY, "This is a non-recoverable error."}, {TRY_AGAIN, "Non-authoritative host not found."}, {ETIMEOUT, "Operation timed out."}, #ifndef BEOS {EDESTADDRREQ, "Destination address required."}, {EHOSTDOWN, "Host is down."}, {ESOCKTNOSUPPORT, "Socket type not supported."}, #endif #endif {-1, 0} }; ErrorMsg *Error = ErrorCodes; while (Error->Code >= 0 && Error->Code != d->LastError) { Error++; } if (d->LastError == 10060 && Param) { HostEnt *He = (HostEnt*)Param; char s[256]; sprintf_s(s, sizeof(s), "%s (gethostbyname returned '%s')", Error->Msg, He->h_name); OnError(d->LastError, s); } else #if defined(MAC) if (d->LastError != 36) #endif { OnError(d->LastError, (Error->Code >= 0) ? Error->Msg : ""); } switch (d->LastError) { case 0: #ifdef WIN32 case 183: // I think this is a XP 'disconnect' ??? case WSAECONNABORTED: case WSAECONNRESET: case WSAENETRESET: #else case ECONNABORTED: case ECONNRESET: case ENETRESET: #endif { Close(); break; } } return d->LastError; } /* void GSocket::SetLogFile(char *FileName, int Type) { DeleteArray(d->LogFile); if (FileName) { switch (Type) { case NET_LOG_HEX_DUMP: case NET_LOG_ALL_BYTES: { d->LogFile = NewStr(FileName); d->LogType = Type; break; } } } } */ bool GSocket::GetUdp() { return d->Udp != 0; } void GSocket::SetUdp(bool b) { if (d->Udp ^ b) { d->Udp = b; if (!ValidSocket(d->Socket)) { if (d->Udp) d->Socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); else d->Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); } if (d->Broadcast) { option_t enabled = d->Broadcast != 0; setsockopt(Handle(), SOL_SOCKET, SO_BROADCAST, (char*)&enabled, sizeof(enabled)); } } } void GSocket::SetBroadcast() { d->Broadcast = true; } -bool GSocket::AddMulticastMember(uint32 MulticastIp, uint32 LocalInterface) +bool GSocket::AddMulticastMember(uint32_t MulticastIp, uint32_t LocalInterface) { if (!MulticastIp) return false; struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = htonl(MulticastIp); // your multicast address mreq.imr_interface.s_addr = htonl(LocalInterface); // your incoming interface IP int r = setsockopt(Handle(), IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*) &mreq, sizeof(mreq)); if (!r) return true; Error(); return false; } -bool GSocket::SetMulticastInterface(uint32 Interface) +bool GSocket::SetMulticastInterface(uint32_t Interface) { if (!Interface) return false; struct sockaddr_in addr; addr.sin_addr.s_addr = Interface; auto r = setsockopt(Handle(), IPPROTO_IP, IP_MULTICAST_IF, (const char*) &addr, sizeof(addr)); if (!r) return true; Error(); return false; } bool GSocket::CreateUdpSocket() { if (!ValidSocket(d->Socket)) { d->Socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (ValidSocket(d->Socket)) { if (d->Broadcast) { option_t enabled = d->Broadcast != 0; auto r = setsockopt(Handle(), SOL_SOCKET, SO_BROADCAST, (char*)&enabled, sizeof(enabled)); if (r) Error(); } } } return ValidSocket(d->Socket); } -int GSocket::ReadUdp(void *Buffer, int Size, int Flags, uint32 *Ip, uint16 *Port) +int GSocket::ReadUdp(void *Buffer, int Size, int Flags, uint32_t *Ip, uint16_t *Port) { if (!Buffer || Size < 0) return -1; CreateUdpSocket(); sockaddr_in a; socklen_t AddrSize = sizeof(a); ZeroObj(a); a.sin_family = AF_INET; if (Port) a.sin_port = htons(*Port); #if defined(WINDOWS) a.sin_addr.S_un.S_addr = INADDR_ANY; #else a.sin_addr.s_addr = INADDR_ANY; #endif ssize_t b = recvfrom(d->Socket, (char*)Buffer, Size, Flags, (sockaddr*)&a, &AddrSize); if (b > 0) { OnRead((char*)Buffer, (int)b); if (Ip) { *Ip = ntohl(a.sin_addr.OsAddr); /* printf("ip=%i.%i.%i.%i osaddr=%i.%i.%i.%i\n", ((*Ip) >> 24) & 0xff, ((*Ip) >> 16) & 0xff, ((*Ip) >> 8) & 0xff, ((*Ip)) & 0xff, ((a.sin_addr.OsAddr) >> 24) & 0xff, ((a.sin_addr.OsAddr) >> 16) & 0xff, ((a.sin_addr.OsAddr) >> 8) & 0xff, ((a.sin_addr.OsAddr)) & 0xff ); */ } if (Port) *Port = ntohs(a.sin_port); } return (int)b; } -int GSocket::WriteUdp(void *Buffer, int Size, int Flags, uint32 Ip, uint16 Port) +int GSocket::WriteUdp(void *Buffer, int Size, int Flags, uint32_t Ip, uint16_t Port) { if (!Buffer || Size < 0) return -1; CreateUdpSocket(); sockaddr_in a; ZeroObj(a); a.sin_family = AF_INET; a.sin_port = htons(Port); a.sin_addr.OsAddr = htonl(Ip); ssize_t b = sendto(d->Socket, (char*)Buffer, Size, Flags, (sockaddr*)&a, sizeof(a)); if (b > 0) { OnWrite((char*)Buffer, (int)b); } else { printf("%s:%i - sendto failed with %i.\n", _FL, errno); } return (int)b; } ////////////////////////////////////////////////////////////////////////////// bool HaveNetConnection() { bool Status = false; // Check for dial up connection #if defined WIN32 typedef DWORD (__stdcall *RasEnumConnections_Proc)(LPRASCONN lprasconn, LPDWORD lpcb, LPDWORD lpcConnections); typedef DWORD (__stdcall *RasGetConnectStatus_Proc)(HRASCONN hrasconn, LPRASCONNSTATUS lprasconnstatus); HMODULE hRas = (HMODULE) LoadLibraryA("rasapi32.dll"); if (hRas) { RASCONN Con[10]; DWORD Connections = 0; DWORD Bytes = sizeof(Con); ZeroObj(Con); Con[0].dwSize = sizeof(Con[0]); RasEnumConnections_Proc pRasEnumConnections = (RasEnumConnections_Proc) GetProcAddress(hRas, "RasEnumConnectionsA"); RasGetConnectStatus_Proc pRasGetConnectStatus = (RasGetConnectStatus_Proc) GetProcAddress(hRas, "RasGetConnectStatusA"); if (pRasEnumConnections && pRasGetConnectStatus) { pRasEnumConnections(Con, &Bytes, &Connections); for (unsigned i=0; i>24)&0xff, (ip>>16)&0xff, (ip>>8)&0xff, (ip)&0xff); return s; } -uint32 LIpHostInt(GString str) +uint32_t LIpHostInt(GString str) { auto p = str.Split("."); if (p.Length() != 4) return 0; - uint32 ip = 0; + uint32_t ip = 0; for (auto &s : p) { ip <<= 8; auto n = s.Int(); if (n > 255) { LgiAssert(0); return 0; } - ip |= (uint8)s.Int(); + ip |= (uint8_t)s.Int(); } return ip; } bool WhatsMyIp(GAutoString &Ip) { bool Status = false; StartNetworkStack(); #if defined WIN32 char hostname[256]; HostEnt *e = NULL; if (gethostname(hostname, sizeof(hostname)) != SOCKET_ERROR) { e = gethostbyname(hostname); } if (e) { int Which = 0; for (; e->h_addr_list[Which]; Which++); Which--; char IpAddr[32]; sprintf_s(IpAddr, "%i.%i.%i.%i", (uchar)e->h_addr_list[Which][0], (uchar)e->h_addr_list[Which][1], (uchar)e->h_addr_list[Which][2], (uchar)e->h_addr_list[Which][3]); Status = Ip.Reset(NewStr(IpAddr)); } #elif defined BEOS struct sockaddr_in addr; int sock, size; memset((char *)&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = 0; if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return false; if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) return false; size = sizeof(addr); if ((getsockname(sock, (struct sockaddr *)&addr, &size)) < 0) return false; close(sock); if (addr.sin_addr.s_addr == INADDR_ANY) return false; uchar *a = (uchar*)&addr.sin_addr.s_addr; char IpAddr[32]; sprintf_s( IpAddr, sizeof(IpAddr), "%i.%i.%i.%i", a[0], a[1], a[2], a[3]); Ip.Reset(NewStr(IpAddr)); Status = true; #endif return Status; } ////////////////////////////////////////////////////////////////////// #define SOCKS5_VER 5 #define SOCKS5_CMD_CONNECT 1 #define SOCKS5_CMD_BIND 2 #define SOCKS5_CMD_ASSOCIATE 3 #define SOCKS5_ADDR_IPV4 1 #define SOCKS5_ADDR_DOMAIN 3 #define SOCKS5_ADDR_IPV6 4 #define SOCKS5_AUTH_NONE 0 #define SOCKS5_AUTH_GSSAPI 1 #define SOCKS5_AUTH_USER_PASS 2 GSocks5Socket::GSocks5Socket() { Socks5Connected = false; } void GSocks5Socket::SetProxy(const GSocks5Socket *s) { Proxy.Reset(s ? NewStr(s->Proxy) : 0); Port = s ? s->Port : 0; UserName.Reset(s ? NewStr(s->UserName) : 0); Password.Reset(s ? NewStr(s->Password) : 0); } void GSocks5Socket::SetProxy(char *proxy, int port, char *username, char *password) { Proxy.Reset(NewStr(proxy)); Port = port; UserName.Reset(NewStr(username)); Password.Reset(NewStr(password)); } int GSocks5Socket::Open(const char *HostAddr, int port) { bool Status = false; if (HostAddr) { char Msg[256]; sprintf_s(Msg, sizeof(Msg), "[SOCKS5] Connecting to proxy server '%s'", HostAddr); OnInformation(Msg); Status = GSocket::Open(Proxy, Port) != 0; if (Status) { char Buf[1024]; Buf[0] = SOCKS5_VER; Buf[1] = 2; // methods Buf[2] = SOCKS5_AUTH_NONE; Buf[3] = SOCKS5_AUTH_USER_PASS; // No idea how to implement this. // AuthReq[3] = SOCKS5_AUTH_GSSAPI; OnInformation("[SOCKS5] Connected, Requesting authentication type."); GSocket::Write(Buf, 4, 0); if (GSocket::Read(Buf, 2, 0) == 2) { if (Buf[0] == SOCKS5_VER) { bool Authenticated = false; switch (Buf[1]) { case SOCKS5_AUTH_NONE: { Authenticated = true; OnInformation("[SOCKS5] No authentication needed."); break; } case SOCKS5_AUTH_USER_PASS: { OnInformation("[SOCKS5] User/Pass authentication needed."); if (UserName && Password) { char *b = Buf; *b++ = 1; // ver of sub-negotiation ?? size_t NameLen = strlen(UserName); LgiAssert(NameLen < 0x80); *b++ = (char)NameLen; b += sprintf_s(b, NameLen+1, "%s", UserName.Get()); size_t PassLen = strlen(Password); LgiAssert(PassLen < 0x80); *b++ = (char)PassLen; b += sprintf_s(b, PassLen+1, "%s", Password.Get()); GSocket::Write(Buf, (int)(3 + NameLen + PassLen)); if (GSocket::Read(Buf, 2, 0) == 2) { Authenticated = (Buf[0] == 1 && Buf[1] == 0); } if (!Authenticated) { OnInformation("[SOCKS5] User/Pass authentication failed."); } } break; } } if (Authenticated) { OnInformation("[SOCKS5] Authentication successful."); int HostPort = htons(port); // Header char *b = Buf; *b++ = SOCKS5_VER; *b++ = SOCKS5_CMD_CONNECT; *b++ = 0; // reserved long IpAddr = inet_addr(HostAddr); if (IpAddr != -1) { // Ip *b++ = SOCKS5_ADDR_IPV4; memcpy(b, &IpAddr, 4); b += 4; } else { // Domain Name *b++ = SOCKS5_ADDR_DOMAIN; size_t Len = strlen(HostAddr); LgiAssert(Len < 0x80); *b++ = (char)Len; strcpy_s(b, Buf+sizeof(Buf)-b, HostAddr); b += Len; } // Port memcpy(b, &HostPort, 2); b += 2; GSocket::Write(Buf, (int)(b - Buf), 0); if (GSocket::Read(Buf, 10, 0) == 10) { if (Buf[0] == SOCKS5_VER) { switch (Buf[1]) { case 0: Socks5Connected = true; OnInformation("[SOCKS5] Connected!"); break; case 1: OnInformation("[SOCKS5] General SOCKS server failure"); break; case 2: OnInformation("[SOCKS5] Connection not allowed by ruleset"); break; case 3: OnInformation("[SOCKS5] Network unreachable"); break; case 4: OnInformation("[SOCKS5] Host unreachable"); break; case 5: OnInformation("[SOCKS5] Connection refused"); break; case 6: OnInformation("[SOCKS5] TTL expired"); break; case 7: OnInformation("[SOCKS5] Command not supported"); break; case 8: OnInformation("[SOCKS5] Address type not supported"); break; default: OnInformation("[SOCKS5] Unknown SOCKS server failure"); break; } } else { OnInformation("[SOCKS5] Wrong socks version."); } } else { OnInformation("[SOCKS5] Connection request read failed."); } } else { OnInformation("[SOCKS5] Not authenticated."); } } else { OnInformation("[SOCKS5] Wrong socks version."); } } else { OnInformation("[SOCKS5] Authentication type read failed."); } Status = Socks5Connected; if (!Status) { GSocket::Close(); OnInformation("[SOCKS5] Failure: Disconnecting."); } } } return Status; } diff --git a/src/common/INet/INetTools.cpp b/src/common/INet/INetTools.cpp --- a/src/common/INet/INetTools.cpp +++ b/src/common/INet/INetTools.cpp @@ -1,357 +1,357 @@ #include #include #include "GMem.h" #include "LgiOsDefs.h" #include "INetTools.h" #include "GString.h" #define IsAlpha(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) // Gets a field value char *InetGetField(const char *s) { const char *e = s; static const char *WhiteSpace = " \r\t\n"; // Look for the end of the string while (*e) { if (*e == '\n') { if (!strchr(" \t", e[1])) { break; } } e++; } // Trim whitespace off each end of the string while (s < e && strchr(WhiteSpace, *s)) s++; while (e > s && strchr(" \r\t\n", e[-1])) e--; // Calc the length size_t Size = e - s; char *Str = new char[Size+1]; if (Str) { const char *In = s; char *Out = Str; while (In < e) { if (*In == '\r') { } else if (*In == 9) { *Out++ = ' '; } else { *Out++ = *In; } In++; } *Out++ = 0; } return Str; } const char *SeekNextLine(const char *s, const char *End) { if (s) { for (; *s && *s != '\n' && (!End || s < End); s++); if (*s == '\n' && (!End || s < End)) s++; } return s; } // Search through headers for a field char *InetGetHeaderField( // Returns an allocated string or NULL on failure const char *Headers, // Pointer to all the headers const char *Field, // The field your after ssize_t Len) // Maximum len to run, or -1 for NULL terminated { if (Headers && Field) { // for all lines const char *End = Len < 0 ? 0 : Headers + Len; size_t FldLen = strlen(Field); for (const char *s = Headers; *s && (!End || s < End); s = SeekNextLine(s, End)) { if (*s != 9 && _strnicmp(s, Field, FldLen) == 0) { // found a match s += FldLen; if (*s == ':') { s++; while (*s) { if (strchr(" \t\r", *s)) { s++; } else if (*s == '\n') { if (strchr(" \r\n\t", s[1])) s += 2; else break; } else break; } return InetGetField(s); } } } } return 0; } void InetTokeniseHeaders(List &Out, const char *In) { // loop through and extract all the headers const char *e = 0; for (const char *s=In; s && *s; s = e) { // skip whitespace while (strchr(" \t", *s)) s++; // get end-of-line e = strstr(s, "\r\n"); if (!e) e = s + strlen(s); size_t Len = e - s; // process line char *eName = strnchr(s, ':', Len); if (eName) { GInetHeader *h = new GInetHeader(NewStr(s, eName - s)); if (h) { // move on to text after the whitespace s = eName + 1; while (strchr(" \t", *s)) s++; // extract the data itself h->Str = InetGetField(s); Out.Insert(h); } } if (*e == '\r') e++; if (*e == '\n') e++; } } char *InetRemoveField(char *Headers, const char *Field) { char *Status = Headers; if (Headers && Field) { // loop through and look at all the headers char *e = 0; for (char *s=Headers; s && *s; s = *e == '\n' ? e + 1 : e) { // skip whitespace while (strchr(" \t", *s)) s++; // get end-of-line e = strchr(s, '\n'); if (!e) e = s + strlen(s); size_t Len = e - s; // process line char *eName = strnchr(s, ':', Len); if (eName) { if (_strnicmp(s, Field, eName - s) == 0) { GMemQueue Out; // found header... push headers before this one Out.Write((uchar*)Headers, (int) (s - Headers)); // get end char *End = eName; for (char *eol = strchr(End, '\n'); eol; eol = strchr(eol+1, '\n')) { End = eol + 1; if (eol[1] != '\t' && eol[1] != ' ') break; } // push text after this header Out.Write((uchar*)End, (int)strlen(End)); // replace text.. DeleteArray(Headers); int64 Len = Out.GetSize(); Status = new char[Len+1]; if (Status) { Out.Read((uchar*)Status, (int)Len); Status[Len] = 0; } // leave loop break; } } } } return Status; } char *InetGetAllHeaders(const char *s) { char *All = 0; if (s) { const char *Start = s; while (s && *s) { int i=0; for (; *s && *s != '\r' && *s != '\n'; s++, i++); if (*s == '\r') s++; if (*s == '\n') s++; if (!i) break; } All = NewStr(Start, s - Start); } return All; } char *InetExtractBoundary(const char *Field) { if (Field) { char *Start = stristr(Field, "boundary="); if (Start) { // skip over the id Start += 9; if (*Start == '\"') { // attempt to read the start and end of the boundry key Start++; char *End = strchr(Start, '\"'); if (End) { *End = 0; return NewStr(Start); } } else { // not delimited.... hmmm... grrrr! char *End = Start; while ( *End && !strchr(" \t\r\n", *End) && *End != ';') { End++; } *End = 0; return NewStr(Start); } } } return 0; } char *InetGetSubField(const char *s, const char *Field) { char *Status = 0; if (s && Field) { s = strchr(s, ';'); if (s) { s++; size_t FieldLen = strlen(Field); char White[] = " \t\r\n"; while (*s) { // Skip leading whitespace while (*s && (strchr(White, *s) || *s == ';')) s++; // Parse field name - if (IsAlpha((uint8)*s)) + if (IsAlpha((uint8_t)*s)) { const char *f = s; while (*s && (IsAlpha(*s) || *s == '-')) s++; bool HasField = ((s-f) == FieldLen) && (_strnicmp(Field, f, FieldLen) == 0); while (*s && strchr(White, *s)) s++; if (*s == '=') { s++; while (*s && strchr(White, *s)) s++; if (*s && strchr("\'\"", *s)) { // Quote Delimited Field char d = *s++; char *e = strchr((char*)s, d); if (e) { if (HasField) { Status = NewStr(s, e-s); break; } s = e + 1; } else break; } else { // Space Delimited Field const char *e = s; while (*e && !strchr(White, *e) && *e != ';') e++; if (HasField) { Status = NewStr(s, e-s); break; } s = e; } } else break; } else break; } } } return Status; } diff --git a/src/common/INet/OpenSSLSocket.cpp b/src/common/INet/OpenSSLSocket.cpp --- a/src/common/INet/OpenSSLSocket.cpp +++ b/src/common/INet/OpenSSLSocket.cpp @@ -1,1457 +1,1457 @@ /*hdr ** FILE: OpenSSLSocket.cpp ** AUTHOR: Matthew Allen ** DATE: 24/9/2004 ** DESCRIPTION: Open SSL wrapper socket ** ** Copyright (C) 2004-2014, Matthew Allen ** fret@memecode.com ** */ #include #ifdef WINDOWS #pragma comment(lib,"Ws2_32.lib") #else #include #endif #include "Lgi.h" #include "OpenSSLSocket.h" #include #ifdef WIN32 #include #endif #include "GToken.h" #include "GVariant.h" #include "INet.h" #define PATH_OFFSET "../" #ifdef WIN32 #if OPENSSL_VERSION_NUMBER >= 0x10100000L #ifdef _WIN64 #define SSL_LIBRARY "libssl-1_1-x64" #define EAY_LIBRARY "libcrypto-1_1-x64" #else // 32bit #define SSL_LIBRARY "libssl-1_1" #define EAY_LIBRARY "libcrypto-1_1" #endif #else #define SSL_LIBRARY "ssleay32" #define EAY_LIBRARY "libeay32" #endif #else #define SSL_LIBRARY "libssl" #endif #define HasntTimedOut() ((To < 0) || (LgiCurrentTime() - Start < To)) static const char* MinimumVersion = "1.0.1g"; void SSL_locking_function(int mode, int n, const char *file, int line); unsigned long SSL_id_function(); class LibSSL : public GLibrary { public: LibSSL() { char p[MAX_PATH]; #if defined MAC if (LgiGetExeFile(p, sizeof(p))) { LgiMakePath(p, sizeof(p), p, "Contents/MacOS/libssl.1.0.0.dylib"); if (FileExists(p)) { Load(p); } } if (!IsLoaded()) { Load("/opt/local/lib/" SSL_LIBRARY); } #elif defined LINUX if (LgiGetExePath(p, sizeof(p))) { LgiMakePath(p, sizeof(p), p, "libssl.so"); if (FileExists(p)) { LgiTrace("%s:%i - loading SSL library '%s'\n", _FL, p); Load(p); } } #endif if (!IsLoaded()) Load(SSL_LIBRARY); if (!IsLoaded()) { LgiGetExePath(p, sizeof(p)); LgiMakePath(p, sizeof(p), p, PATH_OFFSET "../OpenSSL"); #ifdef WIN32 GString old = FileDev->GetCurrentFolder(); FileDev->SetCurrentFolder(p); #endif Load(SSL_LIBRARY); #ifdef WIN32 FileDev->SetCurrentFolder(old); #endif } } #if OPENSSL_VERSION_NUMBER >= 0x10100000L DynFunc0(int, OPENSSL_library_init); DynFunc0(int, OPENSSL_load_error_strings); DynFunc2(int, OPENSSL_init_ssl, uint64_t, opts, const OPENSSL_INIT_SETTINGS *, settings); DynFunc0(const SSL_METHOD *, TLS_method); DynFunc0(const SSL_METHOD *, TLS_server_method); DynFunc0(const SSL_METHOD *, TLS_client_method); #else DynFunc0(int, SSL_library_init); DynFunc0(int, SSL_load_error_strings); DynFunc0(SSL_METHOD*, SSLv23_client_method); DynFunc0(SSL_METHOD*, SSLv23_server_method); #endif DynFunc1(int, SSL_open, SSL*, s); DynFunc1(int, SSL_connect, SSL*, s); DynFunc4(long, SSL_ctrl, SSL*, ssl, int, cmd, long, larg, void*, parg); DynFunc1(int, SSL_shutdown, SSL*, s); DynFunc1(int, SSL_free, SSL*, ssl); DynFunc1(int, SSL_get_fd, const SSL *, s); DynFunc2(int, SSL_set_fd, SSL*, s, int, fd); DynFunc1(SSL*, SSL_new, SSL_CTX*, ctx); DynFunc1(BIO*, BIO_new_ssl_connect, SSL_CTX*, ctx); DynFunc1(X509*, SSL_get_peer_certificate, SSL*, s); DynFunc1(int, SSL_set_connect_state, SSL*, s); DynFunc1(int, SSL_set_accept_state, SSL*, s); DynFunc2(int, SSL_get_error, SSL*, s, int, ret_code); DynFunc3(int, SSL_set_bio, SSL*, s, BIO*, rbio, BIO*, wbio); DynFunc3(int, SSL_write, SSL*, ssl, const void*, buf, int, num); DynFunc3(int, SSL_read, SSL*, ssl, const void*, buf, int, num); DynFunc1(int, SSL_pending, SSL*, ssl); DynFunc1(BIO *, SSL_get_rbio, const SSL *, s); DynFunc1(int, SSL_accept, SSL *, ssl); DynFunc1(SSL_CTX*, SSL_CTX_new, const SSL_METHOD*, meth); DynFunc3(int, SSL_CTX_load_verify_locations, SSL_CTX*, ctx, const char*, CAfile, const char*, CApath); DynFunc3(int, SSL_CTX_use_certificate_file, SSL_CTX*, ctx, const char*, file, int, type); DynFunc3(int, SSL_CTX_use_PrivateKey_file, SSL_CTX*, ctx, const char*, file, int, type); DynFunc1(int, SSL_CTX_check_private_key, const SSL_CTX*, ctx); DynFunc1(int, SSL_CTX_free, SSL_CTX*, ctx); #ifdef WIN32 // If this is freaking you out then good... openssl-win32 ships // in 2 DLL's and on Linux everything is 1 shared object. Thus // the code reflects that. }; class LibEAY : public GLibrary { public: LibEAY() : GLibrary(EAY_LIBRARY) { if (!IsLoaded()) { char p[300]; LgiGetExePath(p, sizeof(p)); LgiMakePath(p, sizeof(p), p, PATH_OFFSET "../OpenSSL"); #ifdef WIN32 auto old = FileDev->GetCurrentFolder(); FileDev->SetCurrentFolder(p); #endif Load("libeay32"); #ifdef WIN32 FileDev->SetCurrentFolder(old); #endif } } #endif typedef void (*locking_callback)(int mode,int type, const char *file,int line); typedef unsigned long (*id_callback)(); DynFunc1(BIO*, BIO_new, BIO_METHOD*, type); DynFunc0(BIO_METHOD*, BIO_s_socket); DynFunc0(BIO_METHOD*, BIO_s_mem); DynFunc1(BIO*, BIO_new_connect, char *, host_port); DynFunc4(long, BIO_ctrl, BIO*, bp, int, cmd, long, larg, void*, parg); DynFunc4(long, BIO_int_ctrl, BIO *, bp, int, cmd, long, larg, int, iarg); DynFunc3(int, BIO_read, BIO*, b, void*, data, int, len); DynFunc3(int, BIO_write, BIO*, b, const void*, data, int, len); DynFunc1(int, BIO_free, BIO*, a); DynFunc1(int, BIO_free_all, BIO*, a); DynFunc2(int, BIO_test_flags, const BIO *, b, int, flags); DynFunc0(int, ERR_load_BIO_strings); #if OPENSSL_VERSION_NUMBER < 0x10100000L DynFunc0(int, ERR_free_strings); DynFunc0(int, EVP_cleanup); DynFunc0(int, OPENSSL_add_all_algorithms_noconf); DynFunc1(int, CRYPTO_set_locking_callback, locking_callback, func); DynFunc1(int, CRYPTO_set_id_callback, id_callback, func); DynFunc0(int, CRYPTO_num_locks); DynFunc1(const char *, SSLeay_version, int, type); #else DynFunc2(int, OPENSSL_init_crypto, uint64_t, opts, const OPENSSL_INIT_SETTINGS *, settings); DynFunc1(const char *, OpenSSL_version, int, type); #endif DynFunc1(const char *, ERR_lib_error_string, unsigned long, e); DynFunc1(const char *, ERR_func_error_string, unsigned long, e); DynFunc1(const char *, ERR_reason_error_string, unsigned long, e); DynFunc1(int, ERR_print_errors, BIO *, bp); DynFunc3(char*, X509_NAME_oneline, X509_NAME*, a, char*, buf, int, size); DynFunc1(X509_NAME*, X509_get_subject_name, X509*, a); DynFunc2(char*, ERR_error_string, unsigned long, e, char*, buf); DynFunc0(unsigned long, ERR_get_error); DynFunc2(int, RAND_bytes, unsigned char *, buf, int, num); }; typedef GArray SslVer; SslVer ParseSslVersion(const char *v) { GToken t(v, "."); SslVer out; for (unsigned i=0; i(SslVer &a, SslVer &b) { return CompareSslVersion(a, b) > 0; } static const char *FileLeaf(const char *f) { const char *l = strrchr(f, DIR_CHAR); return l ? l + 1 : f; } #undef _FL #define _FL FileLeaf(__FILE__), __LINE__ class OpenSSL : #ifdef WINDOWS public LibEAY, #endif public LibSSL { SSL_CTX *Server; public: SSL_CTX *Client; GArray Locks; GAutoString ErrorMsg; bool IsLoaded() { return LibSSL::IsLoaded() #ifdef WINDOWS && LibEAY::IsLoaded() #endif ; } bool InitLibrary(SslSocket *sock) { GStringPipe Err; GArray Ver; GArray MinimumVer = ParseSslVersion(MinimumVersion); GToken t; int Len = 0; const char *v = NULL; if (!IsLoaded()) { #ifdef EAY_LIBRARY Err.Print("%s:%i - SSL libraries missing (%s, %s)\n", _FL, SSL_LIBRARY, EAY_LIBRARY); #else Err.Print("%s:%i - SSL library missing (%s)\n", _FL, SSL_LIBRARY); #endif goto OnError; } SSL_library_init(); SSL_load_error_strings(); ERR_load_BIO_strings(); OpenSSL_add_all_algorithms(); Len = CRYPTO_num_locks(); Locks.Length(Len); CRYPTO_set_locking_callback(SSL_locking_function); CRYPTO_set_id_callback(SSL_id_function); v = SSLeay_version(SSLEAY_VERSION); if (!v) { Err.Print("%s:%i - SSLeay_version failed.\n", _FL); goto OnError; } t.Parse(v, " "); if (t.Length() < 2) { Err.Print("%s:%i - SSLeay_version: no version\n", _FL); goto OnError; } Ver = ParseSslVersion(t[1]); if (Ver.Length() < 3) { Err.Print("%s:%i - SSLeay_version: not enough tokens\n", _FL); goto OnError; } if (Ver < MinimumVer) { #if WINDOWS char FileName[MAX_PATH] = ""; DWORD r = GetModuleFileNameA(LibEAY::Handle(), FileName, sizeof(FileName)); #endif Err.Print("%s:%i - SSL version '%s' is too old (minimum '%s')\n" #if WINDOWS "%s\n" #endif , _FL, t[1], MinimumVersion #if WINDOWS ,FileName #endif ); goto OnError; } Client = SSL_CTX_new(SSLv23_client_method()); if (!Client) { long e = ERR_get_error(); char *Msg = ERR_error_string(e, 0); Err.Print("%s:%i - SSL_CTX_new(client) failed with '%s' (%i)\n", _FL, Msg, e); goto OnError; } return true; OnError: ErrorMsg.Reset(Err.NewStr()); if (sock) sock->DebugTrace("%s", ErrorMsg.Get()); return false; } OpenSSL() { Client = NULL; Server = NULL; } ~OpenSSL() { if (Client) { SSL_CTX_free(Client); Client = NULL; } if (Server) { SSL_CTX_free(Server); Server = NULL; } Locks.DeleteObjects(); } SSL_CTX *GetServer(SslSocket *sock, const char *CertFile, const char *KeyFile) { if (!Server) { Server = SSL_CTX_new(SSLv23_server_method()); if (Server) { if (CertFile) SSL_CTX_use_certificate_file(Server, CertFile, SSL_FILETYPE_PEM); if (KeyFile) SSL_CTX_use_PrivateKey_file(Server, KeyFile, SSL_FILETYPE_PEM); if (!SSL_CTX_check_private_key(Server)) { LgiAssert(0); } } else { long e = ERR_get_error(); char *Msg = ERR_error_string(e, 0); GStringPipe p; p.Print("%s:%i - SSL_CTX_new(server) failed with '%s' (%i)\n", _FL, Msg, e); ErrorMsg.Reset(p.NewStr()); sock->DebugTrace("%s", ErrorMsg.Get()); } } return Server; } bool IsOk(SslSocket *sock) { bool Loaded = #ifdef WIN32 LibSSL::IsLoaded() && LibEAY::IsLoaded(); #else IsLoaded(); #endif if (Loaded) return true; // Try and load again... cause the library can be provided by install on demand. #ifdef WIN32 Loaded = LibSSL::Load(SSL_LIBRARY) && LibEAY::Load(EAY_LIBRARY); #else Loaded = Load(SSL_LIBRARY); #endif if (Loaded) InitLibrary(sock); return Loaded; } }; static OpenSSL *Library = 0; #if 0 #define SSL_DEBUG_LOCKING #endif void SSL_locking_function(int mode, int n, const char *file, int line) { LgiAssert(Library != NULL); if (Library) { if (!Library->Locks[n]) { #ifdef SSL_DEBUG_LOCKING LgiTrace("SSL[%i] create\n", n); #endif Library->Locks[n] = new LMutex; } #ifdef SSL_DEBUG_LOCKING LgiTrace("SSL[%i] lock=%i, unlock=%i, re=%i, wr=%i (mode=0x%x, cnt=%i, thr=0x%x, %s:%i)\n", n, TestFlag(mode, CRYPTO_LOCK), TestFlag(mode, CRYPTO_UNLOCK), TestFlag(mode, CRYPTO_READ), TestFlag(mode, CRYPTO_WRITE), mode, Library->Locks[n]->GetCount(), LgiGetCurrentThread(), file, line); #endif if (mode & CRYPTO_LOCK) Library->Locks[n]->Lock((char*)file, line); else if (mode & CRYPTO_UNLOCK) Library->Locks[n]->Unlock(); } } unsigned long SSL_id_function() { return (unsigned long) LgiGetCurrentThread(); } bool StartSSL(GAutoString &ErrorMsg, SslSocket *sock) { static LMutex Lock; if (Lock.Lock(_FL)) { if (!Library) { Library = new OpenSSL; if (Library && !Library->InitLibrary(sock)) { ErrorMsg = Library->ErrorMsg; DeleteObj(Library); } } Lock.Unlock(); } return Library != NULL; } void EndSSL() { DeleteObj(Library); } struct SslSocketPriv : public LCancel { GCapabilityClient *Caps; bool SslOnConnect; bool IsSSL; bool UseSSLrw; int Timeout; bool RawLFCheck; #ifdef _DEBUG bool LastWasCR; #endif bool IsBlocking; LCancel *Cancel; // This is just for the UI. GStreamI *Logger; // This is for the connection logging. GAutoString LogFile; GAutoPtr LogStream; int LogFormat; SslSocketPriv() { #ifdef _DEBUG LastWasCR = false; #endif Cancel = this; Timeout = 20 * 1000; IsSSL = false; UseSSLrw = false; LogFormat = 0; } }; bool SslSocket::DebugLogging = false; SslSocket::SslSocket(GStreamI *logger, GCapabilityClient *caps, bool sslonconnect, bool RawLFCheck) { d = new SslSocketPriv; Bio = 0; Ssl = 0; d->RawLFCheck = RawLFCheck; d->SslOnConnect = sslonconnect; d->Caps = caps; d->Logger = logger; d->IsBlocking = true; GAutoString ErrMsg; if (StartSSL(ErrMsg, this)) { #ifdef WIN32 if (Library->IsOk(this)) { char n[MAX_PATH]; char s[MAX_PATH]; if (GetModuleFileNameA(Library->LibSSL::Handle(), n, sizeof(n))) { sprintf_s(s, sizeof(s), "Using '%s'", n); OnInformation(s); } if (GetModuleFileNameA(Library->LibEAY::Handle(), n, sizeof(n))) { sprintf_s(s, sizeof(s), "Using '%s'", n); OnInformation(s); } } #endif } else if (caps) { caps->NeedsCapability("openssl", ErrMsg); } else { OnError(0, "Can't load or find OpenSSL library."); } } SslSocket::~SslSocket() { Close(); DeleteObj(d); } GStreamI *SslSocket::Clone() { return new SslSocket(d->Logger, d->Caps, true); } LCancel *SslSocket::GetCancel() { return d->Cancel; } void SslSocket::SetCancel(LCancel *c) { d->Cancel = c; } int SslSocket::GetTimeout() { return d->Timeout; } void SslSocket::SetTimeout(int ms) { d->Timeout = ms; } void SslSocket::SetLogger(GStreamI *logger) { d->Logger = logger; } void SslSocket::SetSslOnConnect(bool b) { d->SslOnConnect = b; } GStream *SslSocket::GetLogStream() { if (!d->LogStream && d->LogFile) { if (!d->LogStream.Reset(new GFile)) return NULL; if (!d->LogStream->Open(d->LogFile, O_WRITE)) return NULL; // Seek to the end d->LogStream->SetPos(d->LogStream->GetSize()); } return d->LogStream; } bool SslSocket::GetVariant(const char *Name, GVariant &Val, char *Arr) { if (!Name) return false; if (!_stricmp(Name, "isSsl")) // Type: Bool { Val = true; return true; } return false; } void SslSocket::Log(const char *Str, ssize_t Bytes, SocketMsgType Type) { if (!ValidStr(Str)) return; if (d->Logger) d->Logger->Write(Str, Bytes<0?(int)strlen(Str):Bytes, Type); else if (Type == SocketMsgError) LgiTrace("%.*s", Bytes, Str); } const char *SslSocket::GetErrorString() { return ErrMsg; } void SslSocket::SslError(const char *file, int line, const char *Msg) { char *Part = strrchr((char*)file, DIR_CHAR); #ifndef WIN32 printf("%s:%i - %s\n", file, line, Msg); #endif ErrMsg.Printf("Error: %s:%i - %s\n", Part ? Part + 1 : file, line, Msg); Log(ErrMsg, ErrMsg.Length(), SocketMsgError); } OsSocket SslSocket::Handle(OsSocket Set) { OsSocket h = INVALID_SOCKET; if (Set != INVALID_SOCKET) { long r; bool IsError = false; if (!Ssl) { Ssl = Library->SSL_new(Library->GetServer(this, NULL, NULL)); } if (Ssl) { r = Library->SSL_set_fd(Ssl, (int) Set); Bio = Library->SSL_get_rbio(Ssl); r = Library->SSL_accept(Ssl); if (r <= 0) IsError = true; else if (r == 1) h = Set; } else IsError = true; if (IsError) { long e = Library->ERR_get_error(); char *Msg = Library->ERR_error_string(e, 0); Log(Msg, -1, SocketMsgError); return INVALID_SOCKET; } } else if (Bio) { int hnd = (int)INVALID_SOCKET; Library->BIO_get_fd(Bio, &hnd); h = hnd; } return h; } bool SslSocket::IsOpen() { return Bio != 0 && !d->Cancel->IsCancelled(); } GString SslGetErrorAsString(OpenSSL *Library) { BIO *bio = Library->BIO_new (Library->BIO_s_mem()); Library->ERR_print_errors (bio); char *buf = NULL; size_t len = Library->BIO_get_mem_data (bio, &buf); GString s(buf, len); Library->BIO_free (bio); return s; } int SslSocket::Open(const char *HostAddr, int Port) { bool Status = false; LMutex::Auto Lck(&Lock, _FL); DebugTrace("%s:%i - SslSocket::Open(%s,%i)\n", _FL, HostAddr, Port); if (Library && Library->IsOk(this) && HostAddr) { char h[256]; sprintf_s(h, sizeof(h), "%s:%i", HostAddr, Port); // Do SSL handshake? if (d->SslOnConnect) { // SSL connection.. d->IsSSL = true; if (Library->Client) { const char *CertDir = "/u/matthew/cert"; int r = Library->SSL_CTX_load_verify_locations(Library->Client, 0, CertDir); DebugTrace("%s:%i - SSL_CTX_load_verify_locations=%i\n", _FL, r); if (r > 0) { Bio = Library->BIO_new_ssl_connect(Library->Client); DebugTrace("%s:%i - BIO_new_ssl_connect=%p\n", _FL, Bio); if (Bio) { Library->BIO_get_ssl(Bio, &Ssl); DebugTrace("%s:%i - BIO_get_ssl=%p\n", _FL, Ssl); if (Ssl) { // SNI setup Library->SSL_set_tlsext_host_name(Ssl, HostAddr); // Library->SSL_CTX_set_timeout() Library->BIO_set_conn_hostname(Bio, HostAddr); #if OPENSSL_VERSION_NUMBER < 0x10100000L Library->BIO_set_conn_int_port(Bio, &Port); #else GString sPort; sPort.Printf("%i", Port); Library->BIO_set_conn_port(Bio, sPort.Get()); #endif // Do non-block connect uint64 Start = LgiCurrentTime(); int To = GetTimeout(); IsBlocking(false); r = Library->SSL_connect(Ssl); DebugTrace("%s:%i - initial SSL_connect=%i\n", _FL, r); while (r != 1 && !d->Cancel->IsCancelled()) { int err = Library->SSL_get_error(Ssl, r); if (err != SSL_ERROR_WANT_CONNECT) { DebugTrace("%s:%i - SSL_get_error=%i\n", _FL, err); } LgiSleep(50); r = Library->SSL_connect(Ssl); DebugTrace("%s:%i - SSL_connect=%i (%i of %i ms)\n", _FL, r, (int)(LgiCurrentTime() - Start), (int)To); bool TimeOut = !HasntTimedOut(); if (TimeOut) { DebugTrace("%s:%i - SSL connect timeout, to=%i\n", _FL, To); SslError(_FL, "Connection timeout."); break; } } DebugTrace("%s:%i - open loop finished, r=%i, Cancelled=%i\n", _FL, r, d->Cancel->IsCancelled()); if (r == 1) { IsBlocking(true); Library->SSL_set_mode(Ssl, SSL_MODE_AUTO_RETRY); Status = true; // d->UseSSLrw = true; char m[256]; sprintf_s(m, sizeof(m), "Connected to '%s' using SSL", h); OnInformation(m); } else { GString Err = SslGetErrorAsString(Library).Strip(); if (!Err) Err.Printf("BIO_do_connect(%s:%i) failed.", HostAddr, Port); SslError(_FL, Err); } } else SslError(_FL, "BIO_get_ssl failed."); } else SslError(_FL, "BIO_new_ssl_connect failed."); } else SslError(_FL, "SSL_CTX_load_verify_locations failed."); } else SslError(_FL, "No Ctx."); } else { Bio = Library->BIO_new_connect(h); DebugTrace("%s:%i - BIO_new_connect=%p\n", _FL, Bio); if (Bio) { // Non SSL... go into non-blocking mode so that if ::Close() is called we // can quit out of the connect loop. IsBlocking(false); uint64 Start = LgiCurrentTime(); int To = GetTimeout(); long r = Library->BIO_do_connect(Bio); DebugTrace("%s:%i - BIO_do_connect=%i\n", _FL, r); while (r != 1 && !d->Cancel->IsCancelled()) { if (!Library->BIO_should_retry(Bio)) { break; } LgiSleep(50); r = Library->BIO_do_connect(Bio); DebugTrace("%s:%i - BIO_do_connect=%i\n", _FL, r); if (!HasntTimedOut()) { DebugTrace("%s:%i - open timeout, to=%i\n", _FL, To); OnError(0, "Connection timeout."); break; } } DebugTrace("%s:%i - open loop finished=%i\n", _FL, r); if (r == 1) { IsBlocking(true); Status = true; char m[256]; sprintf_s(m, sizeof(m), "Connected to '%s'", h); OnInformation(m); } else SslError(_FL, "BIO_do_connect failed"); } else SslError(_FL, "BIO_new_connect failed"); } } if (!Status) { Close(); } DebugTrace("%s:%i - SslSocket::Open status=%i\n", _FL, Status); return Status; } bool SslSocket::SetVariant(const char *Name, GVariant &Value, char *Arr) { bool Status = false; if (!Library || !Name) return false; if (!_stricmp(Name, SslSocket_LogFile)) { d->LogFile.Reset(Value.ReleaseStr()); } else if (!_stricmp(Name, SslSocket_LogFormat)) { d->LogFormat = Value.CastInt32(); } else if (!_stricmp(Name, GSocket_Protocol)) { char *v = Value.CastString(); if (v && stristr(v, "SSL")) { if (!Bio) { d->SslOnConnect = true; } else { if (!Library->Client) { SslError(_FL, "Library->Client is null."); } else { Ssl = Library->SSL_new(Library->Client); DebugTrace("%s:%i - SSL_new=%p\n", _FL, Ssl); if (!Ssl) { SslError(_FL, "SSL_new failed."); } else { int r = Library->SSL_set_bio(Ssl, Bio, Bio); DebugTrace("%s:%i - SSL_set_bio=%i\n", _FL, r); uint64 Start = LgiCurrentTime(); int To = GetTimeout(); while (HasntTimedOut()) { r = Library->SSL_connect(Ssl); DebugTrace("%s:%i - SSL_connect=%i\n", _FL, r); if (r < 0) LgiSleep(100); else break; } if (r > 0) { Status = d->UseSSLrw = d->IsSSL = true; OnInformation("Session is now using SSL"); X509 *ServerCert = Library->SSL_get_peer_certificate(Ssl); DebugTrace("%s:%i - SSL_get_peer_certificate=%p\n", _FL, ServerCert); if (ServerCert) { char Txt[256] = ""; Library->X509_NAME_oneline(Library->X509_get_subject_name(ServerCert), Txt, sizeof(Txt)); DebugTrace("%s:%i - X509_NAME_oneline=%s\n", _FL, Txt); OnInformation(Txt); } // SSL_get_verify_result } else { SslError(_FL, "SSL_connect failed."); r = Library->SSL_get_error(Ssl, r); char *Msg = Library->ERR_error_string(r, 0); if (Msg) { OnError(r, Msg); } } } } } } } return Status; } int SslSocket::Close() { bool Prev = d->Cancel->IsCancelled(); d->Cancel->Cancel(); LMutex::Auto Lck(&Lock, _FL); bool Status = Library != NULL; if (Library) { if (Ssl) { DebugTrace("%s:%i - SSL_shutdown\n", _FL); int r = 0; if ((r = Library->SSL_shutdown(Ssl)) >= 0) { #ifdef WIN32 closesocket #else close #endif (Library->SSL_get_fd(Ssl)); } Library->SSL_free(Ssl); OnInformation("SSL connection closed."); // I think the Ssl object "owns" the Bio object... // So assume it gets fread by SSL_shutdown } else if (Bio) { DebugTrace("%s:%i - BIO_free\n", _FL); Library->BIO_free(Bio); OnInformation("Connection closed."); } Ssl = NULL; Bio = NULL; } d->Cancel->Cancel(Prev); return Status; } bool SslSocket::Listen(int Port) { return false; } bool SslSocket::IsBlocking() { return d->IsBlocking; } void SslSocket::IsBlocking(bool block) { d->IsBlocking = block; if (Bio) { Library->BIO_set_nbio(Bio, !d->IsBlocking); } } bool SslSocket::IsReadable(int TimeoutMs) { // Assign to local var to avoid a thread changing it // on us between the validity check and the select. // Which is important because a socket value of -1 // (ie invalid) will crash the FD_SET macro. OsSocket s = Handle(); if (!d->Cancel->IsCancelled() && ValidSocket(s)) { struct timeval t = {TimeoutMs / 1000, (TimeoutMs % 1000) * 1000}; fd_set r; FD_ZERO(&r); FD_SET(s, &r); int v = select((int)s+1, &r, 0, 0, &t); if (v > 0 && FD_ISSET(s, &r)) { return true; } else if (v < 0) { // Error(); } } else LgiTrace("%s:%i - Not a valid socket.\n", _FL); return false; } bool SslSocket::IsWritable(int TimeoutMs) { // Assign to local var to avoid a thread changing it // on us between the validity check and the select. // Which is important because a socket value of -1 // (ie invalid) will crash the FD_SET macro. OsSocket s = Handle(); if (!d->Cancel->IsCancelled() && ValidSocket(s)) { struct timeval t = {TimeoutMs / 1000, (TimeoutMs % 1000) * 1000}; fd_set w; FD_ZERO(&w); FD_SET(s, &w); int v = select((int)s+1, &w, 0, 0, &t); if (v > 0 && FD_ISSET(s, &w)) { return true; } else if (v < 0) { // Error(); } } else LgiTrace("%s:%i - Not a valid socket.\n", _FL); return false; } void SslSocket::OnWrite(const char *Data, ssize_t Len) { #ifdef _DEBUG if (d->RawLFCheck) { const char *End = Data + Len; while (Data < End) { LgiAssert(*Data != '\n' || d->LastWasCR); d->LastWasCR = *Data == '\r'; Data++; } } #endif // Log(Data, Len, SocketMsgSend); } void SslSocket::OnRead(char *Data, ssize_t Len) { #ifdef _DEBUG if (d->RawLFCheck) { const char *End = Data + Len; while (Data < End) { LgiAssert(*Data != '\n' || d->LastWasCR); d->LastWasCR = *Data == '\r'; Data++; } } #endif // Log(Data, Len, SocketMsgReceive); } ssize_t SslSocket::Write(const void *Data, ssize_t Len, int Flags) { LMutex::Auto Lck(&Lock, _FL); if (!Library || d->Cancel->IsCancelled()) return -1; if (!Bio) { DebugTrace("%s:%i - BIO is NULL\n", _FL); return -1; } int r = 0; if (d->UseSSLrw) { if (Ssl) { uint64 Start = LgiCurrentTime(); int To = GetTimeout(); while (HasntTimedOut()) { r = Library->SSL_write(Ssl, Data, (int)Len); if (r <= 0) { if (!Library->BIO_should_retry(Bio)) break; if (d->IsBlocking) LgiSleep(1); else break; } else { DebugTrace("%s:%i - SSL_write(%p,%i)=%i\n", _FL, Data, Len, r); OnWrite((const char*)Data, r); break; } } } else { r = -1; DebugTrace("%s:%i - No SSL\n", _FL); } } else { uint64 Start = LgiCurrentTime(); int To = GetTimeout(); while (HasntTimedOut()) { if (!Library) break; r = Library->BIO_write(Bio, Data, (int)Len); DebugTrace("%s:%i - BIO_write(%p,%i)=%i\n", _FL, Data, Len, r); if (r <= 0) { if (!Library->BIO_should_retry(Bio)) break; if (d->IsBlocking) LgiSleep(1); else break; } else { OnWrite((const char*)Data, r); break; } } } if (r > 0) { GStream *l = GetLogStream(); if (l) l->Write(Data, r); } else if (Ssl) { auto Err = Library->SSL_get_error(Ssl, r); if (Err == SSL_ERROR_ZERO_RETURN) { DebugTrace("%s:%i - ::Write closing %i\n", _FL, r); Close(); } else if (Err != SSL_ERROR_WANT_WRITE && Err != SSL_ERROR_SYSCALL) { char Buf[256] = ""; char *e = Library->ERR_error_string(Err, Buf); DebugTrace("%s:%i - ::Write error %i, %s\n", _FL, Err, e); OnError(Err, e ? e : "ERR_error_string failed"); } } return r; } ssize_t SslSocket::Read(void *Data, ssize_t Len, int Flags) { LMutex::Auto Lck(&Lock, _FL); if (!Library || d->Cancel->IsCancelled()) return -1; if (Bio) { int r = 0; if (d->UseSSLrw) { if (Ssl) { uint64 Start = LgiCurrentTime(); int To = GetTimeout(); while (HasntTimedOut()) { r = Library->SSL_read(Ssl, Data, (int)Len); DebugTrace("%s:%i - SSL_read(%p,%i)=%i\n", _FL, Data, Len, r); if (r < 0) { if (!Library->BIO_should_retry(Bio)) { DebugTrace("%s:%i - BIO_should_retry is false\n", _FL); break; } if (d->IsBlocking) LgiSleep(1); else break; } else { OnRead((char*)Data, r); break; } } } else { DebugTrace("%s:%i - Ssl is NULL\n", _FL); r = -1; } } else { uint64 Start = LgiCurrentTime(); int To = GetTimeout(); while (HasntTimedOut()) { r = Library->BIO_read(Bio, Data, (int)Len); DebugTrace("%s:%i - BIO_read(%p,%i)=%i\n", _FL, Data, Len, r); if (r < 0) { auto Retry = Library->BIO_should_retry(Bio); DebugTrace("%s:%i - BIO_should_retry = %i\n", _FL, Retry); if (!Retry) break; if (d->IsBlocking) LgiSleep(1); else break; } else { OnRead((char*)Data, r); break; } } } if (r > 0) { GStream *l = GetLogStream(); if (l) l->Write(Data, r); } else if (Ssl) { int Err = Library->SSL_get_error(Ssl, r); DebugTrace("%s:%i - SSL_get_error = %i\n", _FL, Err); if (Err == SSL_ERROR_ZERO_RETURN) { DebugTrace("%s:%i - ::Read closing %i\n", _FL, r); Close(); } else if (Err != SSL_ERROR_WANT_READ && Err != SSL_ERROR_SYSCALL) { char Buf[256]; char *e = Library->ERR_error_string(Err, Buf); OnError(Err, e ? e : "ERR_error_string failed"); } } return r; } return -1; } void SslSocket::OnError(int ErrorCode, const char *ErrorDescription) { DebugTrace("%s:%i - OnError=%i,%s\n", _FL, ErrorCode, ErrorDescription); GString s; s.Printf("Error %i: %s\n", ErrorCode, ErrorDescription); Log(s, s.Length(), SocketMsgError); } void SslSocket::DebugTrace(const char *fmt, ...) { if (DebugLogging) { char Buffer[512]; va_list Arg; va_start(Arg, fmt); int Ch = vsprintf_s(Buffer, sizeof(Buffer), fmt, Arg); va_end(Arg); if (Ch > 0) { #if 1 LgiTrace("SSL:%p: %s", this, Buffer); #else OnInformation(Buffer); #endif } } } void SslSocket::OnInformation(const char *Str) { while (Str && *Str) { GAutoString a; const char *nl = Str; while (*nl && *nl != '\n') nl++; int Len = (int) (nl - Str + 2); a.Reset(new char[Len]); char *o; for (o = a; Str < nl; Str++) { if (*Str != '\r') *o++ = *Str; } *o++ = '\n'; *o++ = 0; LgiAssert((o-a) <= Len); Log(a, -1, SocketMsgInfo); Str = *nl ? nl + 1 : nl; } } GString SslSocket::Random(int Len) { GString s; s.Length(Len); - auto r = Library ? Library->RAND_bytes((uint8*) s.Get(), Len) : 0; + auto r = Library ? Library->RAND_bytes((uint8_t*) s.Get(), Len) : 0; return r ? s : NULL; } diff --git a/src/common/Lgi/GCss.cpp b/src/common/Lgi/GCss.cpp --- a/src/common/Lgi/GCss.cpp +++ b/src/common/Lgi/GCss.cpp @@ -1,2991 +1,2991 @@ #include #include "Lgi.h" #include "LgiCommon.h" #define DEBUG_CSS_LOGGING 0 #define IsWhite(s) ((s) && strchr(WhiteSpace, (s)) != NULL) #define SkipWhite(s) while (IsWhite(*s)) s++; #undef IsNumeric #define IsNumeric(s) \ ( \ (*(s)) && \ ( \ strchr("-.", *(s)) || \ (*(s) >= '0' && *(s) <= '9') \ ) \ ) #undef IsAlpha #define IsAlpha(s) \ ( \ ((s) >= 'a' && (s) <= 'z') || \ ((s) >= 'A' && (s) <= 'Z') || \ - (((uint8)s) > 0xa0) \ + (((uint8_t)s) > 0xa0) \ ) #define CopyPropOnSave(Type, Id) \ { \ Type *e = (Type*)Props.Find(Id); \ if (e) *e = *(t); \ else Props.Add(Id, new Type(*(t))); \ } #define ReleasePropOnSave(Type, Id) \ { \ Type *e = (Type*)Props.Find(Id); \ if (e) *e = *(t); \ else Props.Add(Id, (t).Release()); \ } LHashTbl, GCss::PropType> GCss::Lut; LHashTbl, GCss::PropType> GCss::ParentProp; const char *GCss::PropName(PropType p) { // const char *s; // for (PropType t = Lut.First(&s); t; t = Lut.Next(&s)) for (auto i : Lut) { if (p == i.value) return i.key; } LgiAssert(!"Add this field to the LUT"); return 0; } double GCss::FontSizeTable[7] = { 0.6, // SizeXXSmall 0.75, // SizeXSmall 0.85, // SizeSmall 1.0, // SizeMedium 1.2, // SizeLarge 1.5, // SizeXLarge 2.0, // SizeXXLarge }; ///////////////////////////////////////////////////////////////////////////// static bool ParseWord(const char *&s, const char *word) { const char *doc = s; while (*doc && *word) { if (tolower(*doc) == tolower(*word)) { doc++; word++; } else return false; } if (*word) return false; if (*doc && (IsAlpha(*doc) || IsDigit(*doc))) return false; s = doc; return true; } bool ParseProp(char *&s, char *w) { char *doc = s; char *word = w; while (*doc && *word) { if (tolower(*doc) == tolower(*word)) { doc++; word++; } else return false; } if (*word) return false; SkipWhite(doc); if (*doc != ':') return false; s = doc + 1; return true; } static int ParseComponent(const char *&s) { int ret = 0; SkipWhite(s); if (!strnicmp(s, "0x", 2)) { ret = htoi(s); while (*s && (IsDigit(*s) || *s == 'x' || *s == 'X')) s++; } else { ret = atoi(s); while (*s && (IsDigit(*s) || *s == '.' || *s == '-')) s++; SkipWhite(s); if (*s == '%') { s++; ret = ret * 255 / 100; } } SkipWhite(s); return ret; } static char *ParseString(const char *&s) { char *ret = 0; if (*s == '\'' || *s == '\"') { char delim = *s++; char *e = strchr((char*)s, delim); if (!e) return 0; ret = NewStr(s, e - s); s = e + 1; } else { const char *e = s; while (*e && (IsAlpha(*e) || IsDigit(*e) || strchr("_-", *e))) e++; ret = NewStr(s, e - s); s = e; } return ret; } ///////////////////////////////////////////////////////////////////////////// GCss::GCss() : Props(32) { if (Lut.Length() == 0) { Lut.Add("letter-spacing", PropLetterSpacing); Lut.Add("word-wrap", PropWordWrap); Lut.Add("list-style", PropListStyle); Lut.Add("list-style-type", PropListStyleType); Lut.Add("text-align", PropTextAlign); Lut.Add("text-decoration", PropTextDecoration); Lut.Add("display", PropDisplay); Lut.Add("float", PropFloat); Lut.Add("position", PropPosition); Lut.Add("overflow", PropOverflow); Lut.Add("visibility", PropVisibility); Lut.Add("font", PropFont); Lut.Add("font-size", PropFontSize); Lut.Add("fontsize", PropFontSize); // Really, do we need this in the real world?? Lut.Add("font-style", PropFontStyle); Lut.Add("font-variant", PropFontVariant); Lut.Add("font-weight", PropFontWeight); Lut.Add("z-index", PropZIndex); Lut.Add("width", PropWidth); Lut.Add("min-width", PropMinWidth); Lut.Add("max-width", PropMaxWidth); Lut.Add("height", PropHeight); Lut.Add("min-height", PropMinHeight); Lut.Add("max-height", PropMaxHeight); Lut.Add("top", PropTop); Lut.Add("right", PropRight); Lut.Add("bottom", PropBottom); Lut.Add("left", PropLeft); Lut.Add("margin", PropMargin); Lut.Add("margin-top", PropMarginTop); Lut.Add("margin-right", PropMarginRight); Lut.Add("margin-bottom", PropMarginBottom); Lut.Add("margin-left", PropMarginLeft); Lut.Add("padding", PropPadding); Lut.Add("padding-top", PropPaddingTop); Lut.Add("padding-right", PropPaddingRight); Lut.Add("padding-bottom", PropPaddingBottom); Lut.Add("padding-left", PropPaddingLeft); Lut.Add("background", PropBackground); Lut.Add("background-color", PropBackgroundColor); Lut.Add("background-image", PropBackgroundImage); Lut.Add("background-repeat", PropBackgroundRepeat); Lut.Add("background-attachment", PropBackgroundAttachment); Lut.Add("background-x", PropBackgroundX); Lut.Add("background-y", PropBackgroundY); Lut.Add("background-position", PropBackgroundPos); Lut.Add("border", PropBorder); Lut.Add("border-style", PropBorderStyle); Lut.Add("border-color", PropBorderColor); Lut.Add("border-radius", PropBorderRadius); Lut.Add("border-collapse", PropBorderCollapse); Lut.Add("border-spacing", PropBorderSpacing); Lut.Add("border-top", PropBorderTop); Lut.Add("border-top-color", PropBorderTopColor); Lut.Add("border-top-style", PropBorderTopStyle); Lut.Add("border-top-width", PropBorderTopWidth); Lut.Add("border-right", PropBorderRight); Lut.Add("border-right-color", PropBorderRightColor); Lut.Add("border-right-style", PropBorderRightStyle); Lut.Add("border-right-width", PropBorderRightWidth); Lut.Add("border-bottom", PropBorderBottom); Lut.Add("border-bottom-color", PropBorderBottomColor); Lut.Add("border-bottom-style", PropBorderBottomStyle); Lut.Add("border-bottom-width", PropBorderBottomWidth); Lut.Add("border-left", PropBorderLeft); Lut.Add("border-left-color", PropBorderLeftColor); Lut.Add("border-left-style", PropBorderLeftStyle); Lut.Add("border-left-width", PropBorderLeftWidth); Lut.Add("line-height", PropLineHeight); Lut.Add("vertical-align", PropVerticalAlign); Lut.Add("clip", PropClip); Lut.Add("x-rect", PropXSubRect); Lut.Add("color", PropColor); Lut.Add("no-paint-color", PropNoPaintColor); Lut.Add("font-family", PropFontFamily); Lut.Add("cellpadding", Prop_CellPadding); } if (ParentProp.Length() == 0) { ParentProp.Add(PropBorderTopColor, PropBorderTop); ParentProp.Add(PropBorderTopStyle, PropBorderTop); ParentProp.Add(PropBorderTopWidth, PropBorderTop); ParentProp.Add(PropBorderLeftColor, PropBorderLeft); ParentProp.Add(PropBorderLeftStyle, PropBorderLeft); ParentProp.Add(PropBorderLeftWidth, PropBorderLeft); ParentProp.Add(PropBorderRightColor, PropBorderRight); ParentProp.Add(PropBorderRightStyle, PropBorderRight); ParentProp.Add(PropBorderRightWidth, PropBorderRight); ParentProp.Add(PropBorderBottomColor, PropBorderBottom); ParentProp.Add(PropBorderBottomStyle, PropBorderBottom); ParentProp.Add(PropBorderBottomWidth, PropBorderBottom); /* ParentProp.Add(PropBackgroundColor, PropBackground); ParentProp.Add(PropBackgroundImage, PropBackground); ParentProp.Add(PropBackgroundRepeat, PropBackground); ParentProp.Add(PropBackgroundAttachment, PropBackground); ParentProp.Add(PropBackgroundX, PropBackground); ParentProp.Add(PropBackgroundY, PropBackground); */ } } GCss::GCss(const GCss &c) { *this = c; } GCss::~GCss() { Empty(); } int GCss::Len::ToPx(int Box, GFont *Font, int Dpi) { switch (Type) { default: case LenInherit: { return 0; } case LenAuto: case LenNormal: case LenPx: { return (int) Value; } case LenPt: { int EffectiveDpi = Dpi > 0 ? Dpi : LgiScreenDpi(); return (int) (Value * EffectiveDpi / 72.0); } case LenCm: { int EffectiveDpi = Dpi > 0 ? Dpi : LgiScreenDpi(); return (int) (Value * EffectiveDpi / 2.54); } case LenEm: { int FntHt = Font ? Font->GetHeight() : 18; return (int) (Value * FntHt); } case LenEx: { double FntAsc = Font ? Font->Ascent() : 18.0; return (int) (Value * FntAsc); // haha I don't care. } case LenPercent: { if (Box > 0) return (int) (Box * Value / 100.0); else return 0; // No idea... } } } GCss::Len GCss::Len::operator *(const Len &b) const { if (b.IsDynamic()) { GCss::Len a; switch (b.Type) { case LenPercent: { a.Type = Type; a.Value = Value * b.Value / 100.0f; break; } default: { LgiAssert(!"Impl me."); return b; } } return a; } return b; } bool GCss::Len::ToString(GStream &p) const { const char *Unit = 0; switch (Type) { case LenPx: Unit = "px"; break; case LenPt: Unit = "pt"; break; case LenEm: Unit = "em"; break; case LenEx: Unit = "ex"; break; case LenPercent: Unit = "%"; break; case LenCm: Unit = "cm"; break; default: break; } if (Unit) { return p.Print("%g%s", Value, Unit) > 0; } switch (Type) { case LenInherit: Unit = "Inherit"; break; case LenAuto: Unit = "Auto"; break; case LenNormal: Unit = "Normal"; break; case AlignLeft: Unit = "left"; break; case AlignRight: Unit = "right"; break; case AlignCenter: Unit = "center"; break; case AlignJustify: Unit = "justify"; break; case VerticalBaseline: Unit = "baseline"; break; case VerticalSub: Unit = "sub"; break; case VerticalSuper: Unit = "super"; break; case VerticalTop: Unit = "top"; break; case VerticalTextTop: Unit = "text-top"; break; case VerticalMiddle: Unit = "middle"; break; case VerticalBottom: Unit = "bottom"; break; case VerticalTextBottom: Unit = "text-bottom"; break; default: break; } if (!Unit) { LgiAssert(!"Impl missing length enum."); return false; } return p.Print("%s", Unit) > 0; } bool GCss::ColorDef::ToString(GStream &p) { switch (Type) { case ColorInherit: { p.Print("inherit"); break; } case ColorRgb: { p.Print("#%02.2x%02.2x%02.2x", R32(Rgb32), G32(Rgb32), B32(Rgb32)); break; } default: { LgiAssert(!"Impl me."); return false; } } return true; } const char *GCss::ToString(DisplayType dt) { switch (dt) { case DispInherit: return "inherit"; case DispBlock: return "block"; case DispInline: return "inline"; case DispInlineBlock: return "inline-block"; case DispListItem: return "list-item"; case DispNone: return "none"; default: return NULL; } } GAutoString GCss::ToString() { GStringPipe p; // PropType Prop; // for (void *v = Props.First((int*)&Prop); v; v = Props.Next((int*)&Prop)) for (auto v : Props) { PropType Prop = (PropType) v.key; switch (v.key >> 8) { case TypeEnum: { const char *s = 0; const char *Name = PropName(Prop); switch (v.key) { case PropFontWeight: { FontWeightType *b = (FontWeightType*)v.value; switch (*b) { case FontWeightInherit: s = "inherit"; break; case FontWeightNormal: s = "normal"; break; case FontWeightBold: s = "bold"; break; case FontWeightBolder: s = "bolder"; break; case FontWeightLighter: s = "lighter"; break; case FontWeight100: s = "100"; break; case FontWeight200: s = "200"; break; case FontWeight300: s = "300"; break; case FontWeight400: s = "400"; break; case FontWeight500: s = "500"; break; case FontWeight600: s = "600"; break; case FontWeight700: s = "700"; break; case FontWeight800: s = "800"; break; case FontWeight900: s = "900"; break; } break; } case PropFontStyle: { FontStyleType *t = (FontStyleType*)v.value; switch (*t) { case FontStyleInherit: s = "inherit"; break; case FontStyleNormal: s = "normal"; break; case FontStyleItalic: s = "italic"; break; case FontStyleOblique: s = "oblique"; break; } break; } case PropTextDecoration: { TextDecorType *d = (TextDecorType*)v.value; switch (*d) { case TextDecorInherit: s= "inherit"; break; case TextDecorNone: s= "none"; break; case TextDecorUnderline: s= "underline"; break; case TextDecorOverline: s= "overline"; break; case TextDecorLineThrough: s= "line-Through"; break; case TextDecorSquiggle: s= "squiggle"; break; } break; } case PropDisplay: { DisplayType *d = (DisplayType*)v.value; s = ToString(*d); break; } case PropFloat: { FloatType *d = (FloatType*)v.value; switch (*d) { case FloatInherit: s = "inherit"; break; case FloatLeft: s = "left"; break; case FloatRight: s = "right"; break; case FloatNone: s = "none"; break; } break; } case PropPosition: { PositionType *d = (PositionType*)v.value; switch (*d) { case PosInherit: s = "inherit"; break; case PosStatic: s = "static"; break; case PosRelative: s = "relative"; break; case PosAbsolute: s = "absolute"; break; case PosFixed: s = "fixed"; break; } break; } case PropOverflow: { OverflowType *d = (OverflowType*)v.value; switch (*d) { case OverflowInherit: s = "inherit"; break; case OverflowVisible: s = "visible"; break; case OverflowHidden: s = "hidden"; break; case OverflowScroll: s = "scroll"; break; case OverflowAuto: s = "auto"; break; } break; } case PropVisibility: { VisibilityType *d = (VisibilityType*)v.value; switch (*d) { case VisibilityInherit: s = "inherit"; break; case VisibilityVisible: s = "visible"; break; case VisibilityHidden: s = "hidden"; break; case VisibilityCollapse: s = "collapse"; break; } break; } case PropFontVariant: { FontVariantType *d = (FontVariantType*)v.value; switch (*d) { case FontVariantInherit: s = "inherit"; break; case FontVariantNormal: s = "normal"; break; case FontVariantSmallCaps: s = "small-caps"; break; } break; } case PropBackgroundRepeat: { RepeatType *d = (RepeatType*)v.value; switch (*d) { default: s = "inherit"; break; case RepeatBoth: s = "repeat"; break; case RepeatX: s = "repeat-x"; break; case RepeatY: s = "repeat-y"; break; case RepeatNone: s = "none"; break; } break; } case PropBackgroundAttachment: { AttachmentType *d = (AttachmentType*)v.value; switch (*d) { default: s = "inherit"; break; case AttachmentScroll: s = "scroll"; break; case AttachmentFixed: s = "fixed"; break; } break; } case PropListStyleType: { ListStyleTypes *w = (ListStyleTypes*)v.value; switch (*w) { default: s = "inherit"; break; case ListNone: s = "none"; break; case ListDisc: s = "disc"; break; case ListCircle: s = "circle"; break; case ListSquare: s = "square"; break; case ListDecimal: s = "decimal"; break; case ListDecimalLeadingZero: s = "decimalleadingzero"; break; case ListLowerRoman: s = "lowerroman"; break; case ListUpperRoman: s = "upperroman"; break; case ListLowerGreek: s = "lowergreek"; break; case ListUpperGreek: s = "uppergreek"; break; case ListLowerAlpha: s = "loweralpha"; break; case ListUpperAlpha: s = "upperalpha"; break; case ListArmenian: s = "armenian"; break; case ListGeorgian: s = "georgian"; break; } break; } case PropBorderCollapse: { BorderCollapseType *w = (BorderCollapseType*)v.value; switch (*w) { default: s = "inherit"; break; case CollapseCollapse: s = "Collapse"; break; case CollapseSeparate: s = "Separate"; break; } break; } default: { LgiAssert(!"Impl me."); break; } } if (s) p.Print("%s: %s;\n", Name, s); break; } case TypeLen: { Len *l = (Len*)v.value; const char *Name = PropName(Prop); p.Print("%s: ", Name); l->ToString(p); p.Print(";\n"); break; } case TypeGRect: { GRect *r = (GRect*)v.value; const char *Name = PropName(Prop); p.Print("%s: rect(%s);\n", Name, r->GetStr()); break; } case TypeColor: { ColorDef *c = (ColorDef*)v.value; const char *Name = PropName(Prop); p.Print("%s: ", Name); c->ToString(p); p.Print(";\n"); break; } case TypeImage: { ImageDef *i = (ImageDef*)v.value; const char *Name = PropName(Prop); switch (i->Type) { case ImageInherit: { p.Print("%s: inherit;\n", Name); break; } case ImageOwn: case ImageRef: { if (i->Uri) p.Print("%s: url(%s);\n", Name, i->Uri.Get()); break; } default: break; } break; } case TypeBorder: { BorderDef *b = (BorderDef*)v.value; const char *Name = PropName(Prop); p.Print("%s:", Name); b->ToString(p); const char *s = 0; switch (b->Style) { case BorderNone: s = "none"; break; case BorderHidden: s = "hidden"; break; case BorderDotted: s = "dotted"; break; case BorderDashed: s = "dashed"; break; case BorderDouble: s = "double"; break; case BorderGroove: s = "groove"; break; case BorderRidge: s = "ridge"; break; case BorderInset: s = "inset"; break; case BorderOutset: s = "outset"; break; default: case BorderSolid: s = "solid"; break; } p.Print(" %s", s); if (b->Color.Type != ColorInherit) { p.Print(" "); b->Color.ToString(p); } p.Print(";\n"); break; } case TypeStrings: { StringsDef *s = (StringsDef*)v.value; const char *Name = PropName(Prop); p.Print("%s: ", Name); for (int i=0; iLength(); i++) { p.Print("%s%s", i?",":"", (*s)[i]); } p.Print(";\n"); break; } default: { LgiAssert(!"Invalid type."); break; } } } return GAutoString(p.NewStr()); } bool GCss::InheritCollect(GCss &c, PropMap &Contrib) { int StillInherit = 0; // int p; // for (PropArray *a = Contrib.First(&p); a; a = Contrib.Next(&p)) for (auto a : Contrib) { switch (a.key >> 8) { #define InheritEnum(prop, type, inherit) \ case prop: \ { \ type *Mine = (type*)Props.Find(a.key); \ if (!Mine || *Mine == inherit) \ { \ type *Theirs = (type*)c.Props.Find(a.key); \ if (Theirs) \ { \ if (!Mine) Props.Add(a.key, Mine = new type); \ *Mine = *Theirs; \ } \ else StillInherit++; \ } \ break; \ } #define InheritClass(prop, type, inherit) \ case prop: \ { \ type *Mine = (type*)Props.Find(a.key); \ if (!Mine || Mine->Type == inherit) \ { \ type *Theirs = (type*)c.Props.Find(a.key); \ if (Theirs) \ { \ if (!Mine) Props.Add(a.key, Mine = new type); \ *Mine = *Theirs; \ } \ else StillInherit++; \ } \ break; \ } case TypeEnum: { switch (a.key) { InheritEnum(PropFontStyle, FontStyleType, FontStyleInherit); InheritEnum(PropFontVariant, FontVariantType, FontVariantInherit); InheritEnum(PropFontWeight, FontWeightType, FontWeightInherit); InheritEnum(PropTextDecoration, TextDecorType, TextDecorInherit); default: { LgiAssert(!"Not impl."); break; } } break; } case TypeLen: { Len *Mine = (Len*)Props.Find(a.key); if (!Mine || Mine->IsDynamic()) { Len *Cur = (Len*)c.Props.Find(a.key); if (Cur && Cur->Type != LenInherit) { if (!Mine) Props.Add(a.key, Mine = new Len); *Mine = *Cur; a.value->Add(Cur); } else StillInherit++; } break; } case TypeGRect: { GRect *Mine = (GRect*)Props.Find(a.key); if (!Mine || !Mine->Valid()) { GRect *Theirs = (GRect*)c.Props.Find(a.key); if (Theirs) { if (!Mine) Props.Add(a.key, Mine = new GRect); *Mine = *Theirs; } else StillInherit++; } break; } case TypeStrings: { StringsDef *Mine = (StringsDef*)Props.Find(a.key); if (!Mine || Mine->Length() == 0) { StringsDef *Theirs = (StringsDef*)c.Props.Find(a.key); if (Theirs) { if (!Mine) Props.Add(a.key, Mine = new StringsDef); *Mine = *Theirs; } else StillInherit++; } break; } InheritClass(TypeColor, ColorDef, ColorInherit); InheritClass(TypeImage, ImageDef, ImageInherit); InheritClass(TypeBorder, BorderDef, LenInherit); default: { LgiAssert(!"Not impl."); break; } } } return StillInherit > 0; } bool GCss::InheritResolve(PropMap &Contrib) { // int p; // for (PropArray *a = Contrib.First(&p); a; a = Contrib.Next(&p)) for (auto a : Contrib) { switch (a.key >> 8) { case TypeLen: { Len *Mine = 0; for (ssize_t i=a.value->Length()-1; i>=0; i--) { Len *Cur = (Len*)(*a.value)[i]; if (!Mine) { Props.Add(a.key, Mine = new Len(*Cur)); continue; } if (Cur->IsDynamic()) { switch (Cur->Type) { case LenAuto: { i = 0; break; } case SizeSmaller: { switch (Mine->Type) { case LenPt: case LenPx: { Mine->Value = Mine->Value - 1; break; } case SizeXXSmall: { // No smaller sizes.. break; } case SizeXSmall: { Mine->Value = SizeXXSmall; break; } case SizeSmall: { Mine->Value = SizeXSmall; break; } case SizeMedium: { Mine->Value = SizeSmall; break; } case SizeLarge: { Mine->Value = SizeMedium; break; } case SizeXLarge: { Mine->Value = SizeLarge; break; } case SizeXXLarge: { Mine->Value = SizeXLarge; break; } case SizeSmaller: { // Just stick with smaller... break; } default: { LgiAssert(!"Not impl"); break; } } break; } case SizeLarger: { switch (Mine->Type) { case LenPt: case LenPx: { Mine->Value = Mine->Value + 1; break; } case SizeXXSmall: { Mine->Value = SizeXSmall; break; } case SizeXSmall: { Mine->Value = SizeSmall; break; } case SizeSmall: { Mine->Value = SizeMedium; break; } case SizeMedium: { Mine->Value = SizeLarge; break; } case SizeLarge: { Mine->Value = SizeXLarge; break; } case SizeXLarge: { Mine->Value = SizeXXLarge; break; } case SizeXXLarge: { // No higher sizess... break; } case SizeLarger: { // Just stick with larger.... break; } default: { LgiAssert(!"Not impl"); break; } } break; } case LenPercent: { if (Cur->Value == 100) break; switch (Mine->Type) { case LenPt: case LenPercent: case LenPx: case LenEm: case LenEx: case LenCm: { Mine->Value *= Cur->Value / 100; break; } case SizeXXSmall: case SizeXSmall: case SizeSmall: case SizeMedium: case SizeLarge: case SizeXLarge: case SizeXXLarge: { int Idx = (int)Mine->Type - SizeXXSmall; if (Idx >= 0 && Idx < CountOf(FontSizeTable)) { double Sz = FontSizeTable[Idx]; double NewSz = Sz * Cur->Value / 100; Mine->Value = (float) NewSz; Mine->Type = LenEm; } else LgiAssert(0); break; } default: { LgiAssert(!"Not impl"); break; } } break; } default: { LgiAssert(!"Not impl"); break; } } } else { *Mine = *Cur; } } break; } } } return false; } GCss &GCss::operator -=(const GCss &c) { // Removes all props in 'cc' from this Css store... GCss &cc = (GCss&)c; // int Prop; // for (void *p=cc.Props.First(&Prop); p; p=cc.Props.Next(&Prop)) for (auto p : cc.Props) { DeleteProp((PropType)p.key); } return *this; } bool GCss::CopyStyle(const GCss &c) { GCss &cc = (GCss&)c; // int Prop; // for (void *p=cc.Props.First(&Prop); p; p=cc.Props.Next(&Prop)) for (auto p : cc.Props) { switch (p.key >> 8) { #define CopyProp(TypeId, Type) \ case TypeId: \ { \ Type *n = (Type*)Props.Find(p.key); \ if (!n) n = new Type; \ *n = *(Type*)p.value; \ Props.Add(p.key, n); \ break; \ } case TypeEnum: { void *n = new DisplayType; - *(uint32*)n = *(uint32*)p.value; + *(uint32_t*)n = *(uint32_t*)p.value; Props.Add(p.key, n); break; } CopyProp(TypeLen, Len); CopyProp(TypeGRect, GRect); CopyProp(TypeColor, ColorDef); CopyProp(TypeImage, ImageDef); CopyProp(TypeBorder, BorderDef); CopyProp(TypeStrings, StringsDef); default: { LgiAssert(!"Invalidate property type."); return false; } } } return true; } bool GCss::operator ==(GCss &c) { if (Props.Length() != c.Props.Length()) return false; // Check individual types bool Eq = true; // PropType Prop; // for (void *Local=Props.First((int*)&Prop); Local && Eq; Local=Props.Next((int*)&Prop)) for (auto p : Props) { void *Other = c.Props.Find(p.key); if (!Other) return false; switch (p.key >> 8) { #define CmpType(id, type) \ case id: \ { \ if ( *((type*)p.value) != *((type*)Other)) \ Eq = false; \ break; \ } - CmpType(TypeEnum, uint32); + CmpType(TypeEnum, uint32_t); CmpType(TypeLen, Len); CmpType(TypeGRect, GRect); CmpType(TypeColor, ColorDef); CmpType(TypeImage, ImageDef); CmpType(TypeBorder, BorderDef); CmpType(TypeStrings, StringsDef); default: LgiAssert(!"Unknown type."); break; } } return Eq; } void GCss::DeleteProp(PropType p) { void *Data = Props.Find(p); if (Data) { DeleteProp(p, Data); Props.Delete(p); } } void GCss::DeleteProp(PropType Prop, void *Data) { if (!Data) return; int Type = Prop >> 8; switch (Type) { case TypeEnum: delete ((PropType*)Data); break; case TypeLen: delete ((Len*)Data); break; case TypeGRect: delete ((GRect*)Data); break; case TypeColor: delete ((ColorDef*)Data); break; case TypeImage: delete ((ImageDef*)Data); break; case TypeBorder: delete ((BorderDef*)Data); break; case TypeStrings: delete ((StringsDef*)Data); break; default: LgiAssert(!"Unknown property type."); break; } } void GCss::Empty() { // int Prop; // for (void *Data=Props.First(&Prop); Data; Data=Props.Next(&Prop)) for (auto p : Props) { DeleteProp((PropType)p.key, p.value); } Props.Empty(); } /// This returns true if there is a non default font style. Useful for checking if you /// need to create a font... bool GCss::HasFontStyle() { auto Fam = FontFamily(); if (Fam.Length() > 0) return true; auto Sz = FontSize(); if (Sz.IsValid()) return true; auto Style = FontStyle(); if (Style != FontStyleInherit) return true; auto Var = FontVariant(); if (Var != FontVariantInherit) return true; auto Wt = FontWeight(); if (Wt != FontWeightInherit && Wt != FontWeightNormal) return true; auto Dec = TextDecoration(); if (Dec != TextDecorInherit) return true; return false; } void GCss::OnChange(PropType Prop) { } bool GCss::ParseFontStyle(PropType PropId, const char *&s) { FontStyleType *w = (FontStyleType*)Props.Find(PropId); if (!w) Props.Add(PropId, w = new FontStyleType); if (ParseWord(s, "inherit")) *w = FontStyleInherit; else if (ParseWord(s, "normal")) *w = FontStyleNormal; else if (ParseWord(s, "italic")) *w = FontStyleItalic; else if (ParseWord(s, "oblique")) *w = FontStyleOblique; else { Props.Delete(PropId); DeleteObj(w); return false; } return true; } bool GCss::ParseFontVariant(PropType PropId, const char *&s) { FontVariantType *w = (FontVariantType*)Props.Find(PropId); if (!w) Props.Add(PropId, w = new FontVariantType); if (ParseWord(s, "inherit")) *w = FontVariantInherit; else if (ParseWord(s, "normal")) *w = FontVariantNormal; else if (ParseWord(s, "small-caps")) *w = FontVariantSmallCaps; else { Props.Delete(PropId); DeleteObj(w); return false; } return true; } bool GCss::ParseFontWeight(PropType PropId, const char *&s) { FontWeightType *w = (FontWeightType*)Props.Find(PropId); if (!w) Props.Add(PropId, w = new FontWeightType); if (ParseWord(s, "Inherit")) *w = FontWeightInherit; else if (ParseWord(s, "Normal")) *w = FontWeightNormal; else if (ParseWord(s, "Bold")) *w = FontWeightBold; else if (ParseWord(s, "Bolder")) *w = FontWeightBolder; else if (ParseWord(s, "Lighter")) *w = FontWeightLighter; else if (ParseWord(s, "100")) *w = FontWeight100; else if (ParseWord(s, "200")) *w = FontWeight200; else if (ParseWord(s, "300")) *w = FontWeight300; else if (ParseWord(s, "400")) *w = FontWeight400; else if (ParseWord(s, "500")) *w = FontWeight500; else if (ParseWord(s, "600")) *w = FontWeight600; else if (ParseWord(s, "700")) *w = FontWeight700; else if (ParseWord(s, "800")) *w = FontWeight800; else if (ParseWord(s, "900")) *w = FontWeight900; else { Props.Delete(PropId); DeleteObj(w); return false; } return true; } bool GCss::ParseBackgroundRepeat(const char *&s) { RepeatType *w = (RepeatType*)Props.Find(PropBackgroundRepeat); if (!w) Props.Add(PropBackgroundRepeat, w = new RepeatType); if (ParseWord(s, "inherit")) *w = RepeatInherit; else if (ParseWord(s, "repeat-x")) *w = RepeatX; else if (ParseWord(s, "repeat-y")) *w = RepeatY; else if (ParseWord(s, "no-repeat")) *w = RepeatNone; else if (ParseWord(s, "repeat")) *w = RepeatBoth; else return false; return true; } bool GCss::ParseDisplayType(const char *&s) { DisplayType *t = (DisplayType*)Props.Find(PropDisplay); if (!t) Props.Add(PropDisplay, t = new DisplayType); if (ParseWord(s, "block")) *t = DispBlock; else if (ParseWord(s, "inline-block")) *t = DispInlineBlock; else if (ParseWord(s, "inline")) *t = DispInline; else if (ParseWord(s, "list-item")) *t = DispListItem; else if (ParseWord(s, "none")) *t = DispNone; else { *t = DispInherit; return false; } return true; } void GCss::ParsePositionType(const char *&s) { PositionType *t = (PositionType*)Props.Find(PropPosition); if (!t) Props.Add(PropPosition, t = new PositionType); if (ParseWord(s, "static")) *t = PosStatic; else if (ParseWord(s, "relative")) *t = PosRelative; else if (ParseWord(s, "absolute")) *t = PosAbsolute; else if (ParseWord(s, "fixed")) *t = PosFixed; else *t = PosInherit; } bool GCss::Parse(const char *&s, ParsingStyle Type) { if (!s) return false; while (*s && *s != '}') { // Parse the prop name out while (*s && !IsAlpha(*s) && !strchr("-_", *s)) s++; char Prop[64], *p = Prop, *end = Prop + sizeof(Prop) - 1; if (!*s) break; while (*s && (IsAlpha(*s) || strchr("-_", *s))) { if (p < end) *p++ = *s++; else s++; } *p++ = 0; SkipWhite(s); if (*s != ':') return false; s++; PropType PropId = Lut.Find(Prop); PropTypes PropType = (PropTypes)((int)PropId >> 8); SkipWhite(s); // const char *ValueStart = s; // Do the data parsing based on type switch (PropType) { case TypeEnum: { switch (PropId) { case PropDisplay: ParseDisplayType(s); break; case PropPosition: ParsePositionType(s); break; case PropFloat: { FloatType *t = (FloatType*)Props.Find(PropId); if (!t) Props.Add(PropId, t = new FloatType); if (ParseWord(s, "left")) *t = FloatLeft; else if (ParseWord(s, "right")) *t = FloatRight; else if (ParseWord(s, "none")) *t = FloatNone; else *t = FloatInherit; break; } case PropFontStyle: { ParseFontStyle(PropId, s); break; } case PropFontVariant: { ParseFontVariant(PropId, s); break; } case PropFontWeight: { ParseFontWeight(PropId, s); break; } case PropTextDecoration: { TextDecorType *w = (TextDecorType*)Props.Find(PropId); if (!w) Props.Add(PropId, w = new TextDecorType); if (ParseWord(s, "inherit")) *w = TextDecorInherit; else if (ParseWord(s, "none")) *w = TextDecorNone; else if (ParseWord(s, "underline")) *w = TextDecorUnderline; else if (ParseWord(s, "overline")) *w = TextDecorOverline; else if (ParseWord(s, "line-through")) *w = TextDecorLineThrough; else if (ParseWord(s, "squiggle")) *w = TextDecorSquiggle; break; } case PropWordWrap: { WordWrapType *w = (WordWrapType*)Props.Find(PropId); if (!w) Props.Add(PropId, w = new WordWrapType); if (ParseWord(s, "break-word")) *w = WrapBreakWord; else *w = WrapNormal; break; } case PropBorderCollapse: { BorderCollapseType *w = (BorderCollapseType*)Props.Find(PropId); if (!w) Props.Add(PropId, w = new BorderCollapseType); if (ParseWord(s, "inherit")) *w = CollapseInherit; else if (ParseWord(s, "Collapse")) *w = CollapseCollapse; else if (ParseWord(s, "Separate")) *w = CollapseSeparate; break; } case PropBackgroundRepeat: { ParseBackgroundRepeat(s); break; } case PropListStyle: { // Fall thru } case PropListStyleType: { ListStyleTypes *w = (ListStyleTypes*)Props.Find(PropListStyleType); if (!w) Props.Add(PropListStyleType, w = new ListStyleTypes); if (ParseWord(s, "none")) *w = ListNone; else if (ParseWord(s, "disc")) *w = ListDisc; else if (ParseWord(s, "circle")) *w = ListCircle; else if (ParseWord(s, "square")) *w = ListSquare; else if (ParseWord(s, "decimal")) *w = ListDecimal; else if (ParseWord(s, "decimalleadingzero")) *w = ListDecimalLeadingZero; else if (ParseWord(s, "lowerroman")) *w = ListLowerRoman; else if (ParseWord(s, "upperroman")) *w = ListUpperRoman; else if (ParseWord(s, "lowergreek")) *w = ListLowerGreek; else if (ParseWord(s, "uppergreek")) *w = ListUpperGreek; else if (ParseWord(s, "loweralpha")) *w = ListLowerAlpha; else if (ParseWord(s, "upperalpha")) *w = ListUpperAlpha; else if (ParseWord(s, "armenian")) *w = ListArmenian; else if (ParseWord(s, "georgian")) *w = ListGeorgian; else *w = ListInherit; break; } case PropLetterSpacing: { // Fixme: do not care right now... break; } case PropBackgroundAttachment: { AttachmentType *w = (AttachmentType*)Props.Find(PropId); if (!w) Props.Add(PropId, w = new AttachmentType); if (ParseWord(s, "inherit")) *w = AttachmentInherit; else if (ParseWord(s, "scroll")) *w = AttachmentScroll; else if (ParseWord(s, "fixed")) *w = AttachmentFixed; break; } case PropOverflow: { OverflowType *w = (OverflowType*)Props.Find(PropId); if (!w) Props.Add(PropId, w = new OverflowType); if (ParseWord(s, "inherit")) *w = OverflowInherit; else if (ParseWord(s, "visible")) *w = OverflowVisible; else if (ParseWord(s, "hidden")) *w = OverflowHidden; else if (ParseWord(s, "scroll")) *w = OverflowScroll; else if (ParseWord(s, "auto")) *w = OverflowAuto; break; } case PropVisibility: { VisibilityType *w = (VisibilityType*)Props.Find(PropId); if (!w) Props.Add(PropId, w = new VisibilityType); if (ParseWord(s, "inherit")) *w = VisibilityInherit; else if (ParseWord(s, "visible")) *w = VisibilityVisible; else if (ParseWord(s, "hidden")) *w = VisibilityHidden; else if (ParseWord(s, "collapse")) *w = VisibilityCollapse; break; } case PropBorderTopStyle: case PropBorderRightStyle: case PropBorderBottomStyle: case PropBorderLeftStyle: { GCss::PropType Parent = ParentProp.Find(PropId); if (Parent) { BorderDef *b = (BorderDef*) Props.Find(Parent); if (!b && (b = new BorderDef)) Props.Add(Parent, b); if (b) { if (!b->ParseStyle(s)) { if (Type == ParseStrict) LgiAssert(!"Colour parsing failed."); } } } break; } case PropFont: { // Clear any existing style info. DeleteProp(PropFontStyle); DeleteProp(PropFontVariant); DeleteProp(PropFontWeight); DeleteProp(PropFontSize); DeleteProp(PropLineHeight); DeleteProp(PropFontFamily); bool ApplySize = true; while (*s) { // Try and guess the parts in any order... SkipWhite(s); if (*s == ';') break; if (*s == '/') { ApplySize = false; s++; } else if (*s == ',') { s++; } else { // Point size...? GAutoPtr Pt(new Len); if (Pt->Parse(s, ApplySize ? PropFontSize : PropLineHeight, Type)) { if (ApplySize) FontSize(*Pt); else LineHeight(*Pt); } else if (!ParseFontStyle(PropFontStyle, s) && !ParseFontVariant(PropFontVariant, s) && !ParseFontWeight(PropFontWeight, s)) { // Face name... GAutoPtr Fam(new StringsDef); if (Fam->Parse(s)) FontFamily(*Fam); else break; } } } break; } default: { LgiAssert(!"Prop parsing support not implemented."); } } break; } case TypeLen: { GArray Lengths; SkipWhite(s); while (*s && *s != ';') { GAutoPtr t(new Len); if (t->Parse(s, PropId, PropId == PropZIndex ? ParseRelaxed : Type)) { Lengths.Add(t.Release()); SkipWhite(s); } else { if (Type == ParseStrict) LgiAssert(!"Parsing failed."); break; } } SkipWhite(s); bool Mismatch = false; switch (PropId) { case PropBorderTopWidth: case PropBorderRightWidth: case PropBorderBottomWidth: case PropBorderLeftWidth: { GCss::PropType Parent = ParentProp.Find(PropId); if (Parent) { BorderDef *b = (BorderDef*) Props.Find(Parent); if (!b && (b = new BorderDef)) Props.Add(Parent, b); if (b && Lengths.Length() == 1) { *((GCss::Len*&)b) = *Lengths[0]; } else if (Type == ParseStrict) { LgiAssert(0); } } break; } case PropPadding: { if (Lengths.Length() == 4) { StoreProp(PropPaddingTop, Lengths[0], true); StoreProp(PropPaddingRight, Lengths[1], true); StoreProp(PropPaddingBottom, Lengths[2], true); StoreProp(PropPaddingLeft, Lengths[3], true); } else if (Lengths.Length() == 3) { StoreProp(PropPaddingTop, Lengths[0], true); StoreProp(PropPaddingLeft, Lengths[1], false); StoreProp(PropPaddingRight, Lengths[1], true); StoreProp(PropPaddingBottom, Lengths[2], true); } else if (Lengths.Length() == 2) { StoreProp(PropPaddingTop, Lengths[0], false); StoreProp(PropPaddingBottom, Lengths[0], true); StoreProp(PropPaddingRight, Lengths[1], false); StoreProp(PropPaddingLeft, Lengths[1], true); } else if (Lengths.Length() == 1) { StoreProp(PropPadding, Lengths[0], true); DeleteProp(PropPaddingLeft); DeleteProp(PropPaddingTop); DeleteProp(PropPaddingRight); DeleteProp(PropPaddingBottom); } else Mismatch = true; if (!Mismatch) { Lengths.Length(0); OnChange(PropPadding); } break; } case PropMargin: { if (Lengths.Length() == 4) { StoreProp(PropMarginTop, Lengths[0], true); StoreProp(PropMarginRight, Lengths[1], true); StoreProp(PropMarginBottom, Lengths[2], true); StoreProp(PropMarginLeft, Lengths[3], true); } else if (Lengths.Length() == 3) { StoreProp(PropMarginTop, Lengths[0], true); StoreProp(PropMarginLeft, Lengths[1], false); StoreProp(PropMarginRight, Lengths[1], true); StoreProp(PropMarginBottom, Lengths[2], true); } else if (Lengths.Length() == 2) { StoreProp(PropMarginTop, Lengths[0], false); StoreProp(PropMarginBottom, Lengths[0], true); StoreProp(PropMarginRight, Lengths[1], false); StoreProp(PropMarginLeft, Lengths[1], true); } else if (Lengths.Length() == 1) { StoreProp(PropMargin, Lengths[0], true); DeleteProp(PropMarginTop); DeleteProp(PropMarginBottom); DeleteProp(PropMarginRight); DeleteProp(PropMarginLeft); } else Mismatch = true; if (!Mismatch) { Lengths.Length(0); OnChange(PropMargin); } } default: { LgiAssert(ParentProp.Find(PropId) == PropNull); if (Lengths.Length() > 0) { StoreProp(PropId, Lengths[0], true); Lengths.DeleteAt(0); OnChange(PropId); } break; } } Lengths.DeleteObjects(); break; } case TypeBackground: { while (*s && !strchr(";}", *s)) { const char *Start = s; ImageDef Img; if (Img.Parse(s)) { BackgroundImage(Img); continue;; } Len x, y; if (x.Parse(s)) { y.Parse(s); BackgroundX(x); BackgroundY(y); continue; } if (ParseBackgroundRepeat(s)) { continue; } ColorDef Color; if (Color.Parse(s) || OnUnhandledColor(&Color, s)) { BackgroundColor(Color); continue; } SkipWhite(s); while (*s && *s != ';' && !IsWhite(*s)) s++; if (Start == s) { LgiAssert(!"Parsing hang."); break; } } break; } case TypeColor: { GAutoPtr t(new ColorDef); if (t->Parse(s) || OnUnhandledColor(t, s)) { switch (PropId) { case PropBorderTopColor: case PropBorderRightColor: case PropBorderBottomColor: case PropBorderLeftColor: { GCss::PropType Parent = ParentProp.Find(PropId); if (Parent) { BorderDef *b = (BorderDef*) Props.Find(Parent); if (!b && (b = new BorderDef)) Props.Add(Parent, b); if (b) b->Color = *t; } else LgiAssert(0); break; } default: { LgiAssert(ParentProp.Find(PropId) == PropNull); ColorDef *e = (ColorDef*)Props.Find(PropId); if (e) *e = *t; else Props.Add(PropId, t.Release()); break; } } } else if (Type == ParseStrict) LgiAssert(!"Parsing failed."); break; } case TypeStrings: { GAutoPtr t(new StringsDef); if (t->Parse(s)) { StringsDef *e = (StringsDef*)Props.Find(PropId); if (e) *e = *t; else Props.Add(PropId, t.Release()); } else LgiAssert(!"Parsing failed."); break; } case TypeBorder: { if (PropId == PropBorderStyle) { GCss::BorderDef b; if (b.ParseStyle(s)) { GCss::BorderDef *db; GetOrCreate(db, PropBorderLeft)->Style = b.Style; GetOrCreate(db, PropBorderRight)->Style = b.Style; GetOrCreate(db, PropBorderTop)->Style = b.Style; GetOrCreate(db, PropBorderBottom)->Style = b.Style; } } else if (PropId == PropBorderColor) { ColorDef c; if (c.Parse(s)) { GCss::BorderDef *db; GetOrCreate(db, PropBorderLeft)->Color = c; GetOrCreate(db, PropBorderRight)->Color = c; GetOrCreate(db, PropBorderTop)->Color = c; GetOrCreate(db, PropBorderBottom)->Color = c; } } else { GAutoPtr t(new BorderDef); if (t->Parse(this, s)) { ReleasePropOnSave(BorderDef, PropId); } } break; } case TypeGRect: { GRect r; if (ParseWord(s, "rect")) { SkipWhite(s); if (*s == '(') { const char *Start = ++s; while (*s && *s != ')' && *s != ';') s++; if (*s == ')') { GString tmp(Start, s - Start); r.SetStr(tmp); s++; GRect *e = (GRect*)Props.Find(PropId); if (e) *e = r; else Props.Add(PropId, new GRect(r)); } return false; } } break; } case TypeImage: { GAutoPtr Img(new ImageDef); if (Img->Parse(s)) { ImageDef *i = (ImageDef*)Props.Find(PropId); if (i) *i = *Img; else Props.Add(PropId, Img.Release()); } else if (Type == ParseStrict) { LgiAssert(!"Failed to parse Image definition"); return false; } break; } default: { if (Type == ParseStrict) { LgiAssert(!"Unsupported property type."); return false; } else { #if DEBUG_CSS_LOGGING LgiTrace("%s:%i - Unsupported CSS property: %s\n", _FL, Prop); #endif } break; } } // End of property delimiter while (*s && *s != ';') s++; if (*s != ';') break; s++; } return true; } ///////////////////////////////////////////////////////////////////////////// bool GCss::Len::Parse(const char *&s, PropType Prop, ParsingStyle ParseType) { if (!s) return false; SkipWhite(s); if (ParseWord(s, "inherit")) Type = LenInherit; else if (ParseWord(s, "auto")) Type = LenAuto; else if (ParseWord(s, "normal")) Type = LenNormal; else if (ParseWord(s, "center")) Type = AlignCenter; else if (ParseWord(s, "left")) Type = AlignLeft; else if (ParseWord(s, "right")) Type = AlignRight; else if (ParseWord(s, "justify")) Type = AlignJustify; else if (ParseWord(s, "xx-small")) Type = SizeXXSmall; else if (ParseWord(s, "x-small")) Type = SizeXSmall; else if (ParseWord(s, "small")) Type = SizeSmall; else if (ParseWord(s, "medium")) Type = SizeMedium; else if (ParseWord(s, "large")) Type = SizeLarge; else if (ParseWord(s, "x-large")) Type = SizeXLarge; else if (ParseWord(s, "xx-large")) Type = SizeXXLarge; else if (ParseWord(s, "smaller")) Type = SizeSmaller; else if (ParseWord(s, "larger")) Type = SizeLarger; else if (ParseWord(s, "baseline")) Type = VerticalBaseline; else if (ParseWord(s, "sub")) Type = VerticalSub; else if (ParseWord(s, "super")) Type = VerticalSuper; else if (ParseWord(s, "top")) Type = VerticalTop; else if (ParseWord(s, "text-top")) Type = VerticalTextTop; else if (ParseWord(s, "middle")) Type = VerticalMiddle; else if (ParseWord(s, "bottom")) Type = VerticalBottom; else if (ParseWord(s, "text-bottom")) Type = VerticalTextBottom; else if (IsNumeric(s)) { Value = (float) atof(s); while (IsNumeric(s)) s++; SkipWhite(s); if (*s == '%') { Type = LenPercent; s++; } else if (ParseWord(s, "px"))Type = LenPx; else if (ParseWord(s, "pt")) Type = LenPt; else if (ParseWord(s, "em")) Type = LenEm; else if (ParseWord(s, "ex")) Type = LenEx; else if (ParseWord(s, "cm")) Type = LenCm; else if (IsAlpha(*s)) { // Unknown unit, in the case of a missing ';' we should // reset to "inherit" as it's less damaging to the layout Type = LenInherit; return false; } else if (ParseType == ParseRelaxed) { if (Prop == PropLineHeight) { Type = LenPercent; Value *= 100; } else { Type = LenPx; } } else return false; } else return false; return true; } bool GCss::ColorDef::Parse(const char *&s) { if (!s) return false; #define NamedColour(Name, Value) \ else if (ParseWord(s, #Name)) { Type = ColorRgb; Rgb32 = Value; return true; } #define ParseExpect(s, ch) \ if (*s != ch) return false; \ else s++; SkipWhite(s); if (*s == '#') { s++; int v = 0; const char *e = s; while (*e && e < s + 6) { if (*e >= 'a' && *e <= 'f') { v <<= 4; v |= *e - 'a' + 10; e++; } else if (*e >= 'A' && *e <= 'F') { v <<= 4; v |= *e - 'A' + 10; e++; } else if (*e >= '0' && *e <= '9') { v <<= 4; v |= *e - '0'; e++; } else break; } if (e == s + 3) { Type = ColorRgb; int r = (v >> 8) & 0xf; int g = (v >> 4) & 0xf; int b = v & 0xf; Rgb32 = Rgb32( (r << 4 | r), (g << 4 | g), (b << 4 | b) ); s = e; } else if (e == s + 6) { Type = ColorRgb; int r = (v >> 16) & 0xff; int g = (v >> 8) & 0xff; int b = v & 0xff; Rgb32 = Rgb32(r, g, b); s = e; } else return false; } else if (ParseWord(s, "transparent")) { Type = ColorTransparent; } else if (ParseWord(s, "rgb") && *s == '(') { s++; int r = ParseComponent(s); ParseExpect(s, ','); int g = ParseComponent(s); ParseExpect(s, ','); int b = ParseComponent(s); ParseExpect(s, ')'); Type = ColorRgb; Rgb32 = Rgb32(r, g, b); } else if (ParseWord(s, "rgba") && *s == '(') { s++; int r = ParseComponent(s); ParseExpect(s, ','); int g = ParseComponent(s); ParseExpect(s, ','); int b = ParseComponent(s); ParseExpect(s, ','); int a = ParseComponent(s); ParseExpect(s, ')'); Type = ColorRgb; Rgb32 = Rgba32(r, g, b, a); } else if (ParseWord(s, "-webkit-gradient(")) { GAutoString GradientType(ParseString(s)); ParseExpect(s, ','); if (!GradientType) return false; if (!stricmp(GradientType, "radial")) { } else if (!stricmp(GradientType, "linear")) { Len StartX, StartY, EndX, EndY; if (!StartX.Parse(s, PropNull) || !StartY.Parse(s, PropNull)) return false; ParseExpect(s, ','); if (!EndX.Parse(s, PropNull) || !EndY.Parse(s, PropNull)) return false; ParseExpect(s, ','); SkipWhite(s); while (*s) { if (*s == ')') { Type = ColorLinearGradient; break; } else { GAutoString Stop(ParseString(s)); if (!Stop) return false; if (!stricmp(Stop, "from")) { } else if (!stricmp(Stop, "to")) { } else if (!stricmp(Stop, "stop")) { } else return false; } } } else return false; } NamedColour(black, Rgb32(0x00, 0x00, 0x00)) NamedColour(white, Rgb32(0xff, 0xff, 0xff)) NamedColour(gray, Rgb32(0x80, 0x80, 0x80)) NamedColour(red, Rgb32(0xff, 0x00, 0x00)) NamedColour(yellow, Rgb32(0xff, 0xff, 0x00)) NamedColour(green, Rgb32(0x00, 0x80, 0x00)) NamedColour(orange, Rgb32(0xff, 0xA5, 0x00)) NamedColour(blue, Rgb32(0x00, 0x00, 0xff)) NamedColour(maroon, Rgb32(0x80, 0x00, 0x00)) NamedColour(olive, Rgb32(0x80, 0x80, 0x00)) NamedColour(purple, Rgb32(0x80, 0x00, 0x80)) NamedColour(fuchsia, Rgb32(0xff, 0x00, 0xff)) NamedColour(lime, Rgb32(0x00, 0xff, 0x00)) NamedColour(navy, Rgb32(0x00, 0x00, 0x80)) NamedColour(aqua, Rgb32(0x00, 0xff, 0xff)) NamedColour(teal, Rgb32(0x00, 0x80, 0x80)) NamedColour(silver, Rgb32(0xc0, 0xc0, 0xc0)) else return false; return true; } GCss::ImageDef::~ImageDef() { if (Type == ImageOwn) DeleteObj(Img); } bool GCss::ImageDef::Parse(const char *&s) { SkipWhite(s); if (*s == '-') s++; if (ParseWord(s, "initial")) { Type = ImageInherit; } else if (ParseWord(s, "none")) { Type = ImageNone; } else if (!strnicmp(s, "url(", 4)) { s += 4; char *e = strchr((char*)s, ')'); if (!e) return false; Uri.Set(s, e - s); s = e + 1; Type = ImageRef; } else { return false; } return true; } bool GCss::ImageDef::operator !=(const ImageDef &i) { if (Type != i.Type) return false; if (Uri.Get() && i.Uri.Get()) return Uri.Equals(i.Uri); return true; } GCss::ImageDef &GCss::ImageDef::operator =(const ImageDef &o) { if (Type == ImageOwn) DeleteObj(Img); if (o.Img) { Img = o.Img; Type = ImageRef; } else if ((Uri = o.Uri)) { Type = ImageUri; } return *this; } bool GCss::BorderDef::Parse(GCss *Css, const char *&s) { if (!s) return false; const char *Start = NULL; while (*s && *s != ';') { SkipWhite(s); if (Start == s) { LgiAssert(0); return false; } Start = s; if (Len::Parse(s, PropBorder, ParseRelaxed)) continue; if (ParseStyle(s)) continue; if (Color.Parse(s)) continue; // Ok running out of ideas here.... // Is it a weird colour? if (Css && Css->OnUnhandledColor(&Color, s)) continue; // Unknown token... try and parse over it? while (*s && *s != ';' && !strchr(WhiteSpace, *s)) s++; } return true; } bool GCss::BorderDef::ParseStyle(const char *&s) { if (ParseWord(s, "Hidden")) Style = BorderHidden; else if (ParseWord(s, "Solid")) Style = BorderSolid; else if (ParseWord(s, "Dotted")) Style = BorderDotted; else if (ParseWord(s, "Dashed")) Style = BorderDashed; else if (ParseWord(s, "Double")) Style = BorderDouble; else if (ParseWord(s, "Groove")) Style = BorderGroove; else if (ParseWord(s, "Ridge")) Style = BorderRidge; else if (ParseWord(s, "Inset")) Style = BorderInset; else if (ParseWord(s, "Outset")) Style = BorderOutset; else if (ParseWord(s, "None")) Style = BorderNone; else if (ParseWord(s, "!important")) Important = false; else return false; return true; } ///////////////////////////////////////////////////////////////////////////////////////// GCss::Selector &GCss::Selector::operator =(const GCss::Selector &s) { Parts.Length(0); for (int i=0; i"; case CombAdjacent: return "+"; default: break; } return ""; }; GAutoString GCss::Selector::Print() { GStringPipe p; for (int i=0; i"); break; case CombAdjacent: p.Print("+"); break; default: LgiAssert(0); break; } } // And now the rules... p.Print(" {\n%s\n}\n", Style ? Style : ""); return true; } bool GCss::Selector::Parse(const char *&s) { if (!s) return false; const char *Start = s, *Prev = s; GArray Offsets; GStringPipe p; while (*s) { SkipWhite(s); if (*s == '{' || *s == ',') { break; } else if (*s == '/') { if (s[1] != '*') return false; s += 2; char *End = strstr((char*)s, "*/"); if (!End) return false; s = End + 2; continue; } else if (*s == '<') { if (s[1] != '!' && s[2] != '-' && s[3] != '-') return false; s += 4; char *End = strstr((char*)s, "-->"); if (!End) return false; s = End + 3; continue; } else if (*s == ':') { s++; if (*s == ':') { s++; } else if (!IsAlpha(*s)) { s++; break; } Part &n = Parts.New(); n.Type = SelPseudo; if (!TokString(n.Value, s)) return false; if (*s == '(') { char *e = strchr(s + 1, ')'); if (e && e - s < 100) { s++; n.Param.Reset(NewStr(s, e - s)); s = e + 1; } } } else if (*s == '#') { s++; Part &n = Parts.New(); n.Type = SelID; if (!TokString(n.Value, s)) return false; } else if (*s == '.') { s++; while (*s && !IsAlpha(*s)) s++; Part &n = Parts.New(); n.Type = SelClass; if (!TokString(n.Value, s)) return false; } else if (*s == '@') { s++; Part &n = Parts.New(); n.Media = MediaNull; GAutoString Str; if (!TokString(Str, s)) return false; if (!Str) return false; if (!_stricmp(Str, "media")) n.Type = SelMedia; else if (!_stricmp(Str, "font-face")) n.Type = SelFontFace; else if (!_stricmp(Str, "page")) n.Type = SelPage; else if (!_stricmp(Str, "list")) n.Type = SelList; else if (!_stricmp(Str, "import")) n.Type = SelImport; else if (!_stricmp(Str, "keyframes")) n.Type = SelKeyFrames; else n.Type = SelIgnored; SkipWhite(s); while (*s && !strchr(";{", *s)) { if (*s == '(') { const char *e = strchr(s, ')'); if (e) { s = e + 1; SkipWhite(s); continue; } } if (*s == ',') { s++; SkipWhite(s); continue; } if (!TokString(Str, s)) { // Skip bad char... s++; break; } SkipWhite(s); if (!Str) break; if (!stricmp(Str, "screen")) n.Media |= MediaScreen; else if (!stricmp(Str, "print")) n.Media |= MediaPrint; } } else if (s[0] == '*') { s++; Part &n = Parts.New(); n.Type = SelUniversal; } else if (IsAlpha(*s)) { Part &n = Parts.New(); n.Type = SelType; if (!TokString(n.Value, s)) return false; } else if (*s == '[') { s++; Part &n = Parts.New(); n.Type = SelAttrib; char *End = strchr((char*)s, ']'); if (!End) return false; n.Value.Reset(NewStr(s, End - s)); s = End + 1; } else { // Unexpected character s++; continue; } const char *Last = s; SkipWhite(s); if (*s == '+') { s++; LgiAssert(Parts.Length() > 0); Combs.Add(Parts.Length()); Part &n = Parts.New(); n.Type = CombAdjacent; } else if (*s == '>') { s++; LgiAssert(Parts.Length() > 0); Combs.Add(Parts.Length()); Part &n = Parts.New(); n.Type = CombChild; } else if (s > Last && (IsAlpha(*s) || strchr(".:#", *s))) { LgiAssert(Parts.Length() > 0); Combs.Add(Parts.Length()); Part &n = Parts.New(); n.Type = CombDesc; } if (*s && s == Prev) { LgiAssert(!"Parsing is stuck."); return false; } Prev = s; } Raw.Reset(NewStr(Start, s - Start)); return Parts.Length() > 0; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////// #define SkipWhiteSpace(s) while (*s && strchr(" \t\r\n", *s)) s++; static const char *SkipComment(const char *c) { // Skip comment SkipWhiteSpace(c); if (c[0] == '/' && c[1] == '*') { char *e = strstr((char*)c + 2, "*/"); if (e) c = e + 2; else c += 2; SkipWhiteSpace(c); } return c; } bool GCss::Store::Dump(GStream &out) { const char *MapNames[] = {"TypeMap", "ClassMap", "IdMap", NULL}; SelectorMap *Maps[] = {&TypeMap, &ClassMap, &IdMap, NULL}; for (int i=0; Maps[i]; i++) { SelectorMap *m = Maps[i]; out.Print("%s = {\n", MapNames[i]); // const char *Key; // for (SelArray *a = m->First(&Key); a; a = m->Next(&Key)) for (auto a : *m) { out.Print("\t'%s' -> ", a.key); for (int n=0; nLength(); n++) { GCss::Selector *sel = (*a.value)[n]; if (n) out.Print("\t\t"); out.Print("%i of %i: %s\n", n, a.value->Length(), sel->Raw.Get()); // out.Print("\t\t{ %s }\n", sel->Style); } } } return true; } int GCssSelectorCmp(class GCss::Selector **a, GCss::Selector **b) { - uint32 as = (*a)->GetSpecificity(); - uint32 bs = (*b)->GetSpecificity(); + uint32_t as = (*a)->GetSpecificity(); + uint32_t bs = (*b)->GetSpecificity(); if (as == bs) return (*a)->SourceIndex - (*b)->SourceIndex; return as - bs; } void GCss::Store::SortStyles(GCss::SelArray &Styles) { if (Styles.Length() > 1) Styles.Sort(GCssSelectorCmp); } bool GCss::Store::ToString(GStream &p) { SelectorMap *Maps[] = {&TypeMap, &ClassMap, &IdMap, NULL}; for (int i=0; Maps[i]; i++) { SelectorMap *m = Maps[i]; // for (SelArray *a = m->First(); a; a = m->Next()) for (auto a : *m) { for (unsigned n=0; nLength(); n++) { GCss::Selector *sel = (*a.value)[n]; if (!sel->ToString(p)) return false; } } } // Output all the other styles not in the maps... for (unsigned n=0; nToString(p)) return false; } return true; } bool GCss::Store::Parse(const char *&c, int Depth) { int SelectorIndex = 1; if (!c) return false; // const char *Start = c; c = SkipComment(c); if (!strncmp(c, "", 3)) break; SkipWhiteSpace(c); if (*c == '}' && Depth > 0) { c++; return true; } // read selector GArray Selectors; GCss::Selector *Cur = new GCss::Selector; if (Cur->Parse(c)) { Cur->SourceIndex = SelectorIndex++; Selectors.Add(Cur); } else { DeleteObj(Cur); if (*c) return false; } while (*c) { SkipWhiteSpace(c); if (*c == ',') { c++; Cur = new GCss::Selector; if (Cur && Cur->Parse(c)) { Cur->SourceIndex = SelectorIndex++; Selectors.Add(Cur); } else { DeleteObj(Cur); } } else if (*c == '/') { const char *n = SkipComment(c); if (n == c) c++; else c = n; } else break; } SkipWhiteSpace(c); // read styles if (*c == '{') { c++; SkipWhiteSpace(c); if (Cur && Cur->IsAtMedia()) { // At media rules, so create a child store and put all the rules in there... if (Cur->Children.Reset(new GCss::Store)) { if (!Cur->Children->Parse(c, Depth + 1)) return false; } } else { // Normal rule... const char *Start = c; while (*c && *c != '}') c++; char *Style = NewStr(Start, c - Start); Styles.Add(Style); c++; for (int i=0; iStyle = Style; ssize_t n = s->GetSimpleIndex(); if (n >= (ssize_t) s->Parts.Length()) { Error.Printf("ErrSimpleIndex %i>=%zi @ '%.80s'", n, s->Parts.Length(), c); LgiAssert(!"Part index too high."); return false; } GCss::Selector::Part &p = s->Parts[n]; switch (p.Type) { case GCss::Selector::SelType: { if (p.Value) { SelArray *a = TypeMap.Get(p.Value); a->Add(s); } break; } case GCss::Selector::SelClass: { if (p.Value) { SelArray *a = ClassMap.Get(p.Value); a->Add(s); } break; } case GCss::Selector::SelID: { if (p.Value) { SelArray *a = IdMap.Get(p.Value); a->Add(s); } break; } default: { Other.Add(s); break; } } } } } else { Selectors.DeleteObjects(); break; } } return true; } diff --git a/src/common/Lgi/GGuiUtils.cpp b/src/common/Lgi/GGuiUtils.cpp --- a/src/common/Lgi/GGuiUtils.cpp +++ b/src/common/Lgi/GGuiUtils.cpp @@ -1,524 +1,524 @@ #include #include "Lgi.h" #include "GSkinEngine.h" #include "GToken.h" #if defined(LINUX) && !defined(LGI_SDL) #include "LgiWinManGlue.h" #endif ////////////////////////////////////////////////////////////////////////////////// #if !defined(VK_CONTEXTKEY) #if defined(WINDOWS) #define VK_CONTEXTKEY 0x5d #elif defined(MAC) #define VK_CONTEXTKEY VK_APPS #else #define VK_CONTEXTKEY 0x5d #warning "Check local platform def for app menu key." #endif #endif bool GKey::IsContextMenu() { #if WINNATIVE || defined(LINUX) return !IsChar && vkey == VK_CONTEXTKEY; #else return false; #endif } ////////////////////////////////////////////////////////////////////////////////// bool GMouse::IsContextMenu() { if (Right()) return true; #if defined(MAC) || defined(LINUX) if (Left() && Ctrl()) return true; #endif return false; } bool GMouse::ToScreen() { if (ViewCoords && Target) { GdcPt2 p(x, y); Target->PointToScreen(p); x = p.x; y = p.y; ViewCoords = false; return true; } else { printf("%s:%i - Error: Target=%p ViewCoords=%i\n", _FL, Target, ViewCoords); } return false; } bool GMouse::ToView() { if (!ViewCoords && Target) { GdcPt2 p(x, y); Target->PointToView(p); x = p.x; y = p.y; ViewCoords = true; return true; } else { printf("%s:%i - Error: Target=%p ViewCoords=%i\n", __FILE__, __LINE__, Target, ViewCoords); } return false; } ////////////////////////////////////////////////////////////////////////////// -uint32 _LgiColours[LC_MAXIMUM]; +uint32_t _LgiColours[LC_MAXIMUM]; #define ReadColourConfig(def) _lgi_read_colour_config("Colour."#def, _LgiColours+i++) -bool _lgi_read_colour_config(const char *Tag, uint32 *c) +bool _lgi_read_colour_config(const char *Tag, uint32_t *c) { if (!c || !Tag) return false; GXmlTag *Col = LgiApp->GetConfig(Tag); if (!Col) return false; char *h; if (!(h = Col->GetAttr("Hex"))) return false; if (*h == '#') h++; int n = htoi(h); *c = Rgb24( n>>16, n>>8, n ); return true; } //////////////////////////////////////////////////////////////////////////// #ifdef __GTK_H__ COLOUR ColTo24(Gtk::GdkColor &c) { return Rgb24(c.red >> 8, c.green >> 8, c.blue >> 8); } #endif #if defined(WINDOWS) -static uint32 ConvertWinColour(uint32 c) +static uint32_t ConvertWinColour(uint32_t c) { return Rgb24(GetRValue(c), GetGValue(c), GetBValue(c)); } #elif defined(BEOS) static uint32 ConvertHaikuColour(color_which c) { rgb_color rgb = ui_color(c); return Rgb24(rgb.red, rgb.green, rgb.blue); } #endif void LgiInitColours() { int i = 0; // Basic colours _LgiColours[i++] = Rgb24(0, 0, 0); // LC_BLACK _LgiColours[i++] = Rgb24(0x40, 0x40, 0x40); // LC_DKGREY _LgiColours[i++] = Rgb24(0x80, 0x80, 0x80); // LC_MIDGREY _LgiColours[i++] = Rgb24(0xc0, 0xc0, 0xc0); // LC_LTGREY _LgiColours[i++] = Rgb24(0xff, 0xff, 0xff); // LC_WHITE // Variable colours #if defined _XP_CTRLS _LgiColours[i++] = Rgb24(0x42, 0x27, 0x63); // LC_SHADOW _LgiColours[i++] = Rgb24(0x7a, 0x54, 0xa9); // LC_LOW _LgiColours[i++] = Rgb24(0xbc, 0xa9, 0xd4); // LC_MED _LgiColours[i++] = Rgb24(0xdd, 0xd4, 0xe9); // LC_HIGH _LgiColours[i++] = Rgb24(0xff, 0xff, 0xff); // LC_LIGHT _LgiColours[i++] = Rgb24(0xbc, 0xa9, 0xd4); // LC_DIALOG _LgiColours[i++] = Rgb24(0xeb, 0xe6, 0xf2); // LC_WORKSPACE _LgiColours[i++] = Rgb24(0x35, 0x1f, 0x4f); // LC_TEXT _LgiColours[i++] = Rgb24(0xbf, 0x67, 0x93); // LC_FOCUS_SEL_BACK _LgiColours[i++] = Rgb24(0xff, 0xff, 0xff); // LC_FOCUS_SEL_FORE _LgiColours[i++] = Rgb24(0x70, 0x3a, 0xec); // LC_ACTIVE_TITLE _LgiColours[i++] = Rgb24(0xff, 0xff, 0xff); // LC_ACTIVE_TITLE_TEXT _LgiColours[i++] = Rgb24(0x80, 0x80, 0x80); // LC_INACTIVE_TITLE _LgiColours[i++] = Rgb24(0x40, 0x40, 0x40); // LC_INACTIVE_TITLE_TEXT _LgiColours[i++] = Rgb24(0xbc, 0xa9, 0xd4); // LC_MENU_BACKGROUND _LgiColours[i++] = Rgb24(0x35, 0x1f, 0x4f); // LC_MENU_TEXT _LgiColours[i++] = Rgb24(0xbc, 0xa9, 0xd4); // LC_NON_FOCUS_SEL_BACK _LgiColours[i++] = Rgb24(0x35, 0x1f, 0x4f); // LC_NON_FOCUS_SEL_FORE LgiAssert(i == LC_MAXIMUM); #elif defined BEOS GColour Black(0, 0, 0); GColour White(255, 255, 255); GColour Med(ConvertHaikuColour(B_PANEL_BACKGROUND_COLOR), 24); GColour SelFore(ConvertHaikuColour(B_MENU_SELECTED_ITEM_TEXT_COLOR), 24); GColour SelBack(ConvertHaikuColour(B_CONTROL_HIGHLIGHT_COLOR), 24); _LgiColours[i++] = Med.Mix(Black, 0.4f).c24(); // LC_SHADOW _LgiColours[i++] = Med.Mix(Black, 0.25f).c24(); // LC_LOW _LgiColours[i++] = Med.c24(); // LC_MED _LgiColours[i++] = Med.Mix(White, 0.25f).c24(); // LC_HIGH, _LgiColours[i++] = Med.Mix(White, 0.4f).c24(); // LC_LIGHT _LgiColours[i++] = ConvertHaikuColour(B_PANEL_BACKGROUND_COLOR); // LC_DIALOG _LgiColours[i++] = ConvertHaikuColour(B_DOCUMENT_BACKGROUND_COLOR); // LC_WORKSPACE _LgiColours[i++] = ConvertHaikuColour(B_CONTROL_TEXT_COLOR); // LC_TEXT _LgiColours[i++] = SelBack.c24(); // LC_FOCUS_SEL_BACK _LgiColours[i++] = /*SelFore.c24()*/ White.c24(); // LC_FOCUS_SEL_FORE _LgiColours[i++] = ConvertHaikuColour(B_WINDOW_TAB_COLOR); // LC_ACTIVE_TITLE _LgiColours[i++] = ConvertHaikuColour(B_WINDOW_TEXT_COLOR); // LC_ACTIVE_TITLE_TEXT _LgiColours[i++] = ConvertHaikuColour(B_WINDOW_INACTIVE_TAB_COLOR); // LC_INACTIVE_TITLE _LgiColours[i++] = ConvertHaikuColour(B_WINDOW_INACTIVE_TEXT_COLOR); // LC_INACTIVE_TITLE_TEXT _LgiColours[i++] = ConvertHaikuColour(B_MENU_BACKGROUND_COLOR); // LC_MENU_BACKGROUND _LgiColours[i++] = ConvertHaikuColour(B_MENU_ITEM_TEXT_COLOR); // LC_MENU_TEXT _LgiColours[i++] = SelBack.Mix(White, 0.5f).c24(); // LC_NON_FOCUS_SEL_BACK _LgiColours[i++] = /*SelFore.Mix(White, 0.4f).c24()*/ White.c24(); // LC_NON_FOCUS_SEL_FORE _LgiColours[i++] = Rgb24(255, 222, 0); // LC_DEBUG_CURRENT_LINE LgiAssert(i <= LC_MAXIMUM); #elif defined __GTK_H__ Gtk::GtkSettings *set = Gtk::gtk_settings_get_default(); if (!set) { printf("%s:%i - gtk_settings_get_for_screen failed.\n", _FL); return; } char PropName[] = "gtk-color-scheme"; Gtk::gchararray Value = 0; Gtk::g_object_get(set, PropName, &Value, 0); GToken Lines(Value, "\n"); Gtk::g_free(Value); LHashTbl, int> Colours(0, -1); for (int i=0; i %llX\n", val, c); COLOUR c24 = ((c >> 8) & 0xff) | ((c >> 16) & 0xff00) | ((c >> 24) & 0xff0000); // printf("ParseSysColour %s = %x\n", var, c24); Colours.Add(var, c24); } } #define LookupColour(name, default) ((Colours.Find(name) >= 0) ? Colours.Find(name) : default) COLOUR Med = LookupColour("bg_color", Rgb24(0xe8, 0xe8, 0xe8)); COLOUR White = Rgb24(255, 255, 255); COLOUR Black = Rgb24(0, 0, 0); COLOUR Sel = Rgb24(0x33, 0x99, 0xff); _LgiColours[i++] = GdcMixColour(Med, Black, 0.25); // LC_SHADOW _LgiColours[i++] = GdcMixColour(Med, Black, 0.5); // LC_LOW _LgiColours[i++] = Med; // LC_MED _LgiColours[i++] = GdcMixColour(Med, White, 0.5); // LC_HIGH _LgiColours[i++] = GdcMixColour(Med, White, 0.25); // LC_LIGHT _LgiColours[i++] = Med; // LC_DIALOG _LgiColours[i++] = LookupColour("base_color", White); // LC_WORKSPACE _LgiColours[i++] = LookupColour("text_color", Black); // LC_TEXT _LgiColours[i++] = LookupColour("selected_bg_color", Sel); // LC_FOCUS_SEL_BACK _LgiColours[i++] = LookupColour("selected_fg_color", White); // LC_FOCUS_SEL_FORE _LgiColours[i++] = LookupColour("selected_bg_color", Sel); // LC_ACTIVE_TITLE _LgiColours[i++] = LookupColour("selected_fg_color", White); // LC_ACTIVE_TITLE_TEXT _LgiColours[i++] = Rgb24(0xc0, 0xc0, 0xc0); // LC_INACTIVE_TITLE _LgiColours[i++] = Rgb24(0x80, 0x80, 0x80); // LC_INACTIVE_TITLE_TEXT _LgiColours[i++] = LookupColour("bg_color", White); // LC_MENU_BACKGROUND _LgiColours[i++] = LookupColour("text_color", Black); // LC_MENU_TEXT _LgiColours[i++] = GdcMixColour(LookupColour("selected_bg_color", Sel), _LgiColours[11]); // LC_NON_FOCUS_SEL_BACK _LgiColours[i++] = LookupColour("selected_fg_color", White); // LC_NON_FOCUS_SEL_FORE LgiAssert(i <= LC_MAXIMUM); #elif defined(WINDOWS) _LgiColours[i++] = ConvertWinColour(GetSysColor(COLOR_3DDKSHADOW)); // LC_SHADOW _LgiColours[i++] = ConvertWinColour(GetSysColor(COLOR_3DSHADOW)); // LC_LOW _LgiColours[i++] = ConvertWinColour(GetSysColor(COLOR_3DFACE)); // LC_MED _LgiColours[i++] = ConvertWinColour(GetSysColor(COLOR_3DLIGHT)); // LC_HIGH, _LgiColours[i++] = ConvertWinColour(GetSysColor(COLOR_3DHIGHLIGHT)); // LC_LIGHT _LgiColours[i++] = ConvertWinColour(GetSysColor(COLOR_3DFACE)); // LC_DIALOG _LgiColours[i++] = ConvertWinColour(GetSysColor(COLOR_WINDOW)); // LC_WORKSPACE _LgiColours[i++] = ConvertWinColour(GetSysColor(COLOR_WINDOWTEXT)); // LC_TEXT _LgiColours[i++] = ConvertWinColour(GetSysColor(COLOR_HIGHLIGHT)); // LC_FOCUS_SEL_BACK _LgiColours[i++] = ConvertWinColour(GetSysColor(COLOR_HIGHLIGHTTEXT)); // LC_FOCUS_SEL_FORE _LgiColours[i++] = ConvertWinColour(GetSysColor(COLOR_ACTIVECAPTION)); // LC_ACTIVE_TITLE _LgiColours[i++] = ConvertWinColour(GetSysColor(COLOR_CAPTIONTEXT)); // LC_ACTIVE_TITLE_TEXT _LgiColours[i++] = ConvertWinColour(GetSysColor(COLOR_INACTIVECAPTION)); // LC_INACTIVE_TITLE _LgiColours[i++] = ConvertWinColour(GetSysColor(COLOR_INACTIVECAPTIONTEXT)); // LC_INACTIVE_TITLE_TEXT _LgiColours[i++] = ConvertWinColour(GetSysColor(COLOR_MENU)); // LC_MENU_BACKGROUND _LgiColours[i++] = ConvertWinColour(GetSysColor(COLOR_MENUTEXT)); // LC_MENU_TEXT _LgiColours[i++] = ConvertWinColour(GetSysColor(COLOR_BTNFACE)); // LC_NON_FOCUS_SEL_BACK _LgiColours[i++] = ConvertWinColour(GetSysColor(COLOR_BTNTEXT)); // LC_NON_FOCUS_SEL_FORE LgiAssert(i <= LC_MAXIMUM); #else // defaults for non-windows, plain greys #if defined(LINUX) && !defined(LGI_SDL) WmColour c; Proc_LgiWmGetColour WmGetColour = 0; GLibrary *WmLib = LgiApp->GetWindowManagerLib(); if (WmLib) { WmGetColour = (Proc_LgiWmGetColour) WmLib->GetAddress("LgiWmGetColour"); } #define SetCol(def) \ if (WmGetColour && WmGetColour(i, &c)) \ _LgiColours[i++] = Rgb24(c.r, c.g, c.b); \ else \ _LgiColours[i++] = def; #else // MAC #define SetCol(def) \ _LgiColours[i++] = def; #endif SetCol(Rgb24(64, 64, 64)); // LC_SHADOW SetCol(Rgb24(128, 128, 128)); // LC_LOW //#ifdef BEOS SetCol(Rgb24(216, 216, 216)); // LC_MED //#else //SetCol(Rgb24(230, 230, 230)); // LC_MED //#endif SetCol(Rgb24(230, 230, 230)); // LC_HIGH SetCol(Rgb24(255, 255, 255)); // LC_LIGHT SetCol(Rgb24(216, 216, 216)); // LC_DIALOG SetCol(Rgb24(0xff, 0xff, 0xff)); // LC_WORKSPACE SetCol(Rgb24(0, 0, 0)); // LC_TEXT SetCol(Rgb24(0x4a, 0x59, 0xa5)); // LC_FOCUS_SEL_BACK SetCol(Rgb24(0xff, 0xff, 0xff)); // LC_FOCUS_SEL_FORE SetCol(Rgb24(0, 0, 0x80)); // LC_ACTIVE_TITLE SetCol(Rgb24(0xff, 0xff, 0xff)); // LC_ACTIVE_TITLE_TEXT SetCol(Rgb24(0x80, 0x80, 0x80)); // LC_INACTIVE_TITLE SetCol(Rgb24(0x40, 0x40, 0x40)); // LC_INACTIVE_TITLE_TEXT SetCol(Rgb24(222, 222, 222)); // LC_MENU_BACKGROUND SetCol(Rgb24(0, 0, 0)); // LC_MENU_TEXT SetCol(Rgb24(222, 222, 222)); // LC_NON_FOCUS_SEL_BACK SetCol(Rgb24(0, 0, 0)); // LC_NON_FOCUS_SEL_FORE #endif // Tweak if (LgiGetOs() == LGI_OS_WIN32 || LgiGetOs() == LGI_OS_WIN64) { // Win32 doesn't seem to get this right, so we just tweak it here #define MixComp(a) ( (a(_LgiColours[7])+a(_LgiColours[9])) / 2 ) _LgiColours[8] = Rgb24(MixComp(R24), MixComp(G24), MixComp(B24)); } _LgiColours[23] = Rgb24(0xff, 0xe0, 0x00); // Read any settings out of config i = 5; ReadColourConfig(LC_SHADOW); ReadColourConfig(LC_LOW); ReadColourConfig(LC_MED); ReadColourConfig(LC_HIGH); ReadColourConfig(LC_LIGHT); ReadColourConfig(LC_DIALOG); ReadColourConfig(LC_WORKSPACE); ReadColourConfig(LC_TEXT); ReadColourConfig(LC_FOCUS_SEL_BACK); ReadColourConfig(LC_FOCUS_SEL_FORE); ReadColourConfig(LC_ACTIVE_TITLE); ReadColourConfig(LC_ACTIVE_TITLE_TEXT); ReadColourConfig(LC_INACTIVE_TITLE); ReadColourConfig(LC_INACTIVE_TITLE_TEXT); } /// \brief Returns a 24bit representation of a system colour. /// /// You can use the macros R24(), G24() and B24() to extract the colour /// components COLOUR LgiColour ( /// The system colour to return. /// \sa The define LC_BLACK and those that follow in LgiDefs.h int i ) { if (GApp::SkinEngine && TestFlag(GApp::SkinEngine->GetFeatures(), GSKIN_COLOUR)) { return GApp::SkinEngine->GetColour(i); } return i &Displays, GRect *AllDisplays) { #if WINNATIVE if (AllDisplays) AllDisplays->ZOff(-1, -1); GLibrary 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 = WideToUtf8(disp.DeviceString); Dsp->Device = WideToUtf8(disp.DeviceName); DISPLAY_DEVICEW temp = disp; if (EnumDisplayDevicesW(temp.DeviceName, 0, &disp, 0)) { Dsp->Monitor = WideToUtf8(disp.DeviceString); } Displays.Add(Dsp); } } disp.cb = sizeof(disp); } #endif return Displays.Length() > 0; } void GetChildrenList(GViewI *w, List &l) { if (w) { GViewIterator *it = w->IterateViews(); if (it) { for (GViewI *v = it->First(); v; v = it->Next()) { #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 } DeleteObj(it); } } } GViewI *GetNextTabStop(GViewI *v, bool Back) { if (v) { GWindow *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.First(); } } } return 0; } diff --git a/src/common/Lgi/GProcess.cpp b/src/common/Lgi/GProcess.cpp --- a/src/common/Lgi/GProcess.cpp +++ b/src/common/Lgi/GProcess.cpp @@ -1,872 +1,872 @@ /* Process related functions and classes. FYI: There is an alternative class with better streaming: GSubProcess (in include\common\GSubProcess.h) */ #include #include #include #if defined(LINUX) || defined(MAC) #include #include #ifdef COCOA #include #endif #endif #include "Lgi.h" #include "GProcess.h" #include "GToken.h" #ifndef STILL_ACTIVE #define STILL_ACTIVE 254 #endif #define IO_WAIT 10 #if defined(LINUX) || defined(MAC) #include #include #include #include #include #include int hndstate(int hnd) { pollfd p; p.fd = hnd; p.events = 0x3f; p.revents = 0; if (poll(&p, 1, 1) > 0) { return p.revents; } return 0; } #elif defined BEOS #include #define POLLOUT 1 #define POLLIN 2 int hndstate(int h) { return 0; } #endif bool LgiIsProcess(OsProcessId Pid) { bool Status = false; #if defined WIN32 HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION, false, Pid); if (hProc) { DWORD ExitCode = 0; if (GetExitCodeProcess(hProc, &ExitCode) && ExitCode == STILL_ACTIVE) { Status = true; } CloseHandle(hProc); } else LgiTrace("%s:%i - OpenProcess failed with 0x%x\n", _FL, GetLastError()); #elif defined(MAC) #if COCOA NSRunningApplication* app = [NSRunningApplication runningApplicationWithProcessIdentifier: Pid]; return app != nil; #else ProcessSerialNumber psn; OSStatus e = GetProcessForPID(Pid, &psn); return e == 0; #endif #elif defined(LINUX) char ProcPath[256]; sprintf_s(ProcPath, sizeof(ProcPath), "/proc/%i", Pid); Status = DirExists(ProcPath); #elif defined BEOS BRoster r; app_info a; if (r.GetRunningAppInfo(Pid, &a) == B_OK) { Status = true; } #else #error Impl me. #endif return Status; } class GProcessPrivate { public: OsProcess Pid; OsProcessId ProcessId; ulong ExitValue; - uint32 ErrorCode; + uint32_t ErrorCode; char *Exe; GProcessPrivate() { Pid = 0; ProcessId = 0; Exe = NULL; ErrorCode = 0; ExitValue = STILL_ACTIVE; } ~GProcessPrivate() { DeleteArray(Exe); } }; GProcess::GProcess() { d = new GProcessPrivate; } GProcess::~GProcess() { DeleteObj(d); } OsProcess GProcess::Handle() { return d->Pid; } OsProcessId GProcess::GetId() { return d->ProcessId; } -uint32 GProcess::GetErrorCode() +uint32_t GProcess::GetErrorCode() { return d->ErrorCode; } ulong GProcess::ExitValue() { return d->ExitValue; } bool GProcess::IsRunning() { if (d->Pid) { #if defined(LINUX)||defined(MAC) int Status = 0; int i = wait4(d->Pid, &Status, WNOHANG, 0); if (i) { // printf("IsRunning wait4(%i)=%i\n", d->Pid, i); d->ExitValue = Status; d->Pid = 0; } #elif defined BEOS int Status = 0; int i = waitpid(d->Pid, &Status, WNOHANG); if (i) { d->ExitValue = Status; d->Pid = 0; } #elif defined(WIN32) if (GetExitCodeProcess(d->Pid, &d->ExitValue)) { if (d->ExitValue != STILL_ACTIVE) { d->Pid = 0; } } #else #error Impl me. #endif } return d->Pid != 0; } class GNativeString { static bool WinNT; char *n; char16 *w; public: GNativeString() { n = 0; w = 0; } GNativeString(const char *utf) { n = 0; w = 0; *this = utf; } ~GNativeString() { DeleteArray(n); DeleteArray(w); } GNativeString &operator =(const char *utf) { DeleteArray(w); DeleteArray(n); #ifdef WIN32 if (WinNT) { if (utf) w = Utf8ToWide(utf); } else { if (utf) n = LgiToNativeCp(utf); } #else n = NewStr(utf); #endif return *this; } GNativeString &operator =(GNativeString &s) { DeleteArray(w); DeleteArray(n); #ifdef WIN32 if (WinNT) { w = NewStrW(s.w); } else { n = NewStr(s.n); } #else n = NewStr(s.n); #endif return *this; } GNativeString &operator += (GNativeString &add) { size_t AddSize = add.GetSize(); if (AddSize) { size_t Len = GetSize() + AddSize; if (WinNT) { char16 *s = new char16[Len+1]; if (s) { if (w) StrcpyW(s, w); else s[0] = 0; StrcatW(s, (char16*)add); DeleteArray(w); w = s; } } else { char *s = new char[Len+1]; if (s) { if (n) strcpy_s(s, Len+1, n); else s[0] = 0; strcat(s, (char*)add); DeleteArray(n); n = s; } } } return *this; } operator char *() { LgiAssert(!WinNT); return n; } operator char16 *() { LgiAssert(WinNT); return w; } char *NewUtf8() { if (w) { return WideToUtf8(w); } else if (n) { return LgiFromNativeCp(n); } return 0; } size_t GetSize() { if (n) { return strlen(n); } else if (w) { return StrlenW(w); } return 0; } }; bool GProcess::Terminate() { #if defined(WIN32) return TerminateProcess(d->Pid, -1) != 0; #else LgiAssert(0); return false; #endif } bool GNativeString::WinNT = LgiGetOs() == LGI_OS_WIN32 || LgiGetOs() == LGI_OS_WIN64; bool GProcess::Run(const char *Exe, const char *Arguments, const char *Dir, bool Wait, GStream *In, GStream *Out, int Priority) { bool Status = false; #ifdef WIN32 char _exe[256]; if (!LgiGetExtension((char*)Exe)) { sprintf_s(_exe, sizeof(_exe), "%s.exe", Exe); Exe = _exe; } #endif GNativeString NExe; if (Exe) { if (FileExists((char*)Exe)) { NExe = Exe; } else { GString::Array p = LGetEnv("PATH").Split(LGI_PATH_SEPARATOR); for (auto Path : p) { char s[MAX_PATH]; LgiMakePath(s, sizeof(s), Path, Exe); if (FileExists(s)) { NExe = s; break; } } } } if (NExe.GetSize()) { GNativeString Buf; GNativeString Delim; #ifdef WIN32 if (FileExists(Exe)) Delim = "\""; #endif GNativeString Space = " "; GNativeString NPath = Dir; GNativeString NArgs = Arguments; Buf += Delim; Buf += NExe; Buf += Delim; Buf += Space; Buf += NArgs; // sprintf_s(Buf, sizeof(Buf), "%s%s%s %s", Delim, NExe, Delim, NArgs ? NArgs : (char*)""); #ifdef WIN32 HANDLE hChildStdinRd = 0, hChildStdinWr = 0, hChildStdinWrDup = 0, hChildStdoutRd = 0, hChildStdoutWr = 0, hChildStdoutRdDup = 0, hInputFile = 0, hSaveStdin = GetStdHandle(STD_INPUT_HANDLE), hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE); SECURITY_ATTRIBUTES saAttr; saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = true; saAttr.lpSecurityDescriptor = NULL; if (!Out || (CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0) && SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr) && DuplicateHandle(GetCurrentProcess(), hChildStdoutRd, GetCurrentProcess(), &hChildStdoutRdDup, 0, FALSE, DUPLICATE_SAME_ACCESS))) { if (Out) { CloseHandle(hChildStdoutRd); } if (!In || (CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0) && SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd) && DuplicateHandle(GetCurrentProcess(), hChildStdinWr, GetCurrentProcess(), &hChildStdinWrDup, 0, false, DUPLICATE_SAME_ACCESS))) { PROCESS_INFORMATION piProcInfo; ZeroObj(piProcInfo); STARTUPINFOA SiA; STARTUPINFOW SiW; ZeroObj(SiA); ZeroObj(SiW); if (LgiGetOs() == LGI_OS_WIN9X) { SiA.cb = sizeof(SiA); SiA.dwFlags = In || Out ? STARTF_USESTDHANDLES : 0; if (Out) { SiA.hStdOutput = hChildStdoutWr; SiA.hStdError = hChildStdoutWr; } if (In) { CloseHandle(hChildStdinWr); SiA.hStdInput = hChildStdinRd; } } else { SiW.cb = sizeof(SiW); SiW.dwFlags = In || Out ? STARTF_USESTDHANDLES : 0; if (Out) { SiW.hStdOutput = hChildStdoutWr; SiW.hStdError = hChildStdoutWr; } if (In) { CloseHandle(hChildStdinWr); SiW.hStdInput = hChildStdinRd; } } GNativeString StartingPath; if (NPath.GetSize()) { StartingPath = NPath; } else { char *u = NExe.NewUtf8(); if (u) { LgiTrimDir(u); StartingPath = u; DeleteArray(u); } } DWORD Flags = Wait ? CREATE_NO_WINDOW : 0; switch (Priority) { case -2: { Flags |= IDLE_PRIORITY_CLASS; break; } case -1: { Flags |= 0x00004000; break; } case 1: { Flags |= HIGH_PRIORITY_CLASS; break; } case 2: { Flags |= REALTIME_PRIORITY_CLASS; break; } } bool Ok = false; if (LgiGetOs() == LGI_OS_WIN9X) { Ok = CreateProcessA(0, Buf, // command line NULL, // process security attributes NULL, // primary thread security attributes TRUE, // handles are inherited Flags, // Flags NULL, // use parent's environment StartingPath, // use parent's current directory &SiA, // STARTUPINFO pointer &piProcInfo) != 0; // receives PROCESS_INFORMATION } else { Ok = CreateProcessW(0, Buf, // command line NULL, // process security attributes NULL, // primary thread security attributes TRUE, // handles are inherited Flags, // Flags NULL, // use parent's environment StartingPath, // use parent's current directory &SiW, // STARTUPINFO pointer &piProcInfo) != 0; // receives PROCESS_INFORMATION } d->ProcessId = piProcInfo.dwProcessId; if (!Ok) { d->ErrorCode = GetLastError(); } SetStdHandle(STD_INPUT_HANDLE, hSaveStdin); SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout); CloseHandle(hChildStdoutWr); if (Ok) { Status = true; d->Pid = piProcInfo.hProcess; // Write stdin data into app... if (In) { if (In) { ssize_t r; char Buf[256]; while ((r = In->Read(Buf, sizeof(Buf))) > 0) { DWORD w = 0; WriteFile(hChildStdinWrDup, Buf, (DWORD)r, &w, 0); } } CloseHandle(hChildStdinWrDup); } if (Wait) { do { bool Running = IsRunning(); if (Out) { // Look in the read queue char Buf[256] = ""; DWORD r = 0; while (ReadFile(hChildStdoutRdDup, Buf, sizeof(Buf), &r, 0) && r > 0) { Out->Write(Buf, r); } } // Check it's not exited.. if (!Running) { break; } // Don't use the cpu LgiSleep(100); } while (true); } } } } #else // Linux/Mac etc struct Pipe { int Read; int Write; Pipe() { Read = -1; Write = -1; } }; Pipe Read; Pipe Write; Pipe Error; GArray a; a.Add((char*)Exe); for (const char *s=Arguments; s && *s; ) { // skip white while (*s && strchr(" \t\r\n", *s)) s++; // Find token GStringPipe p(256); const char *n; char d = 0; if (strchr("\"\'", *s)) { d = *s++; n = strchr(s, d); if (!n) n = s + strlen(s); p.Push(s, n-s); } else { n = s; while (*n) { if (*n == '\\' && n[1] == ' ') { n++; } else if (strchr(" \t\r\n", *n)) { break; } p.Push(n, 1); n++; } } char *Arg = p.NewStr(); if (Arg) a.Add(Arg); if (d) n++; s = n; } char **Args = &a[0]; if (Args) { char ExeName[256] = ""; if (!FileExists(Exe)) { // Find exe in path GToken Path(getenv("PATH"), ":"); for (int i=0; iPid = fork())) { // Child process if (Dir) { chdir(Dir); } // stdin -> Write close(0); // close stdin dup(Write.Read); close(Write.Write); // stdout -> Read close(1); // close stdout dup(Read.Write); close(Read.Read); // stderr -> Error close(2); // close stderr dup(Error.Write); close(Error.Read); // setup read & write handles printf("execv(%s, ...)\n", Exe); execv(Exe, Args); // We should never get here printf("execv(%s) failed.\n", Exe); exit(-1); } close(Write.Read); } else { // Just output if (!(d->Pid = fork())) { // Child process if (Dir) { chdir(Dir); } // stdout -> Read dup2(Read.Write, fileno(stdout)); close(Read.Write); close(Read.Read); // stderr -> Error dup2(Error.Write, fileno(stderr)); close(Error.Write); close(Error.Read); execv(Exe, Args); // We should never get here printf("execv(%s) failed.\n", Exe); exit(-1); } } close(Read.Write); close(Error.Write); Status = true; ssize_t r; if (In && In->GetSize() > 0) { /* while (!TestFlag(hndstate(Read.Read), POLLOUT)) { LgiSleep(IO_WAIT); } */ char Buf[256]; while ((r = In->Read(Buf, sizeof(Buf))) > 0) { write(Write.Write, Buf, r); } } while (Wait) { // printf("parent: IsRunning()\n"); bool Running = IsRunning(); if (Out) { char Buf[256]; while ( TestFlag(hndstate(Read.Read), POLLIN) && (r = read(Read.Read, Buf, sizeof(Buf)-1)) > 0) { Buf[r] = 0; // printf("stdout: %s\n", Buf); Out->Write(Buf, (int)r); } while ( TestFlag(hndstate(Error.Read), POLLIN) && (r = read(Error.Read, Buf, sizeof(Buf)-1)) > 0) { Buf[r] = 0; // printf("stderr: %s\n", Buf); Out->Write(Buf, (int)r); } } if (!Running) { break; } LgiSleep(IO_WAIT); } if (In) { close(Write.Write); } close(Read.Read); close(Error.Read); for (char **a = Args + 1; *a; a++) { DeleteArray(*a); } } #endif } else { LgiTrace("%s:%i - '%s' is not a valid exe.\n", _FL, (char*)NExe); } return Status; } diff --git a/src/common/Lgi/GSubProcess.cpp b/src/common/Lgi/GSubProcess.cpp --- a/src/common/Lgi/GSubProcess.cpp +++ b/src/common/Lgi/GSubProcess.cpp @@ -1,1023 +1,1023 @@ /** \file \brief Sub-process wrapper. This class runs one or more sub-processes chained together by pipes. Example: GSubProcess p1("ls", "-l"); GSubProcess p2("grep", "string"); p1.Connect(&p2); p1.Start(true, false); int r; char Buf[256]; while ((r = p1.Read(Buf, sizeof(Buf))) > 0) { // So something with 'Buf' } */ #if defined(MAC) || defined(POSIX) #define _GNU_SOURCE #include #include #include #include #endif #ifdef BEOS #include #endif #include "Lgi.h" #include "GSubProcess.h" #include "GToken.h" #define DEBUG_SUBPROCESS 0 #if defined(WIN32) #define NULL_PIPE NULL #define ClosePipe CloseHandle #else #define NULL_PIPE -1 #define ClosePipe close #define INVALID_PID -1 #endif GSubProcess::Pipe::Pipe() { Read = Write = NULL_PIPE; } bool GSubProcess::Pipe::Create ( #ifdef WIN32 LPSECURITY_ATTRIBUTES pAttr #else void *UnusedParam #endif ) { #if defined(WIN32) return CreatePipe(&Read, &Write, pAttr, 0) != 0; #else return pipe(Handles) != NULL_PIPE; #endif } void GSubProcess::Pipe::Close() { if (Read != NULL_PIPE) { ClosePipe(Read); Read = NULL_PIPE; } if (Write != NULL_PIPE) { ClosePipe(Write); Write = NULL_PIPE; } } GSubProcess::GSubProcess(const char *exe, const char *args) { #if defined(POSIX) ChildPid = INVALID_PID; ExitValue = -1; #elif defined(WIN32) ChildPid = NULL; ChildHnd = NULL; ExitValue = 0; #endif NewGroup = true; ErrorCode = 0; Parent = Child = NULL; Exe = exe; Args.Add(Exe); EnvironmentChanged = false; ExternIn = NULL_PIPE; ExternOut = NULL_PIPE; #if DEBUG_SUBPROCESS LgiTrace("%s:%i - %p::GSubProcess('%s','%s')\n", _FL, this, exe, args); #endif char *s; while ((s = LgiTokStr(args))) { Args.Add(s); } } GSubProcess::~GSubProcess() { #if defined(POSIX) Io.Close(); #endif if (Child) { LgiAssert(Child->Parent == this); Child->Parent = NULL; } if (Parent) { LgiAssert(Parent->Child == this); Parent->Child = NULL; } } #ifndef WINDOWS extern char **environ; #endif GSubProcess::Variable *GSubProcess::GetEnvVar(const char *Var, bool Create) { if (Environment.Length() == 0) { // Read all variables in #ifdef WINDOWS LPWCH e = GetEnvironmentStringsW(); if (e) { char16 *s = e; while (*s) { char16 *eq = StrchrW(s, '='); if (!eq) break; ptrdiff_t NameChars = eq - s; if (NameChars > 0) { Variable &v = Environment.New(); v.Var.SetW(s, eq - s); eq++; v.Val.SetW(eq); } eq += StrlenW(eq); s = eq + 1; } FreeEnvironmentStringsW(e); } #else for (int i=0; environ[i]; i++) { auto p = GString(environ[i]).Split("=", 1); if (p.Length() == 2) { Variable &v = Environment.New(); v.Var = p[0]; v.Val = p[1]; } } #endif } for (unsigned i=0; iVal.Get() : NULL; } bool GSubProcess::SetEnvironment(const char *Var, const char *Value) { Variable *v = GetEnvVar(Var, true); if (!v) return false; bool IsPath = !_stricmp(Var, "PATH"); GStringPipe a; const char *s = Value; while (*s) { char *n = strchr(s, '%'); char *e = n ? strchr(n + 1, '%') : NULL; if (n && e) { a.Write(s, (int) (n-s)); n++; ptrdiff_t bytes = e - n; char Name[128]; if (bytes > sizeof(Name) - 1) bytes = sizeof(Name)-1; memcpy(Name, n, bytes); Name[bytes] = 0; const char *existing = GetEnvironment(Name); if (existing) { a.Write(existing, (int)strlen(existing)); } s = e + 1; } else { a.Write(s, (int)strlen(s)); break; } } v->Val = a.NewGStr(); if (IsPath) { // Remove missing paths from the list GToken t(v->Val, LGI_PATH_SEPARATOR); GStringPipe p; for (unsigned i=0; iVal = p.NewGStr(); } EnvironmentChanged = true; return true; } bool GSubProcess::GetValue(const char *Var, ::GVariant &Value) { switch (LgiStringToDomProp(Var)) { case StreamReadable: { #ifdef WINNATIVE char Buf[32] = ""; DWORD lpBytesRead = 0; BOOL b = PeekNamedPipe( ChildOutput.Read, Buf, sizeof(Buf), &lpBytesRead, NULL, NULL); Value = b && lpBytesRead > 0; break; #endif } /* case StreamWritable: { break; } */ default: return false; } return true; } void GSubProcess::SetStdin(OsFile Hnd) { ExternIn = Hnd; } void GSubProcess::SetStdout(OsFile Hnd) { ExternOut = Hnd; } void GSubProcess::Connect(GSubProcess *child) { Child = child; if (Child) { Child->Parent = this; } } bool GSubProcess::Start(bool ReadAccess, bool WriteAccess, bool MapStderrToStdout) { bool Status = false; #if USE_SIMPLE_FORK int in[2]; if (pipe(in) == -1) { printf("parent: Failed to create stdin pipe"); return false; } int out[2]; if (pipe(out) == -1) { printf("parent: Failed to create stdout pipe"); return false; } ChildPid = fork(); if (ChildPid == 0) { // We are in the child process. if (InitialFolder) { chdir(InitialFolder); } // Child shouldn't write to its stdin. if (close(in[1])) printf("%s:%i - close failed.\n", _FL); // Child shouldn't read from its stdout. if (close(out[0])) printf("%s:%i - close failed.\n", _FL); // Redirect stdin and stdout for the child process. if (dup2(in[0], fileno(stdin)) == -1) { printf("%s:%i - child[pre-exec]: Failed to redirect stdin for child\n", _FL); return false; } if (close(in[0])) printf("%s:%i - close failed.\n", _FL); if (dup2(out[1], fileno(stdout)) == -1) { printf("%s:%i - child[pre-exec]: Failed to redirect stdout for child\n", _FL); return false; } if (dup2(out[1], fileno(stderr)) == -1) { printf("%s:%i - child[pre-exec]: Failed to redirect stderr for child\n", _FL); return false; } close(out[1]); // Execute the child Args.Add(NULL); if (Environment.Length()) { GString::Array Vars; GArray Env; Vars.SetFixedLength(false); for (auto v : Environment) { GString &s = Vars.New(); s.Printf("%s=%s", v.Var.Get(), v.Val.Get()); Env.Add(s.Get()); } Env.Add(NULL); execve(Exe, &Args[0], Env.AddressOf()); } else { execvp(Exe, &Args[0]); } // Execution will pass to here if the 'Exe' can't run or doesn't exist // So by exiting with an error the parent process can handle it. exit(GSUBPROCESS_ERROR); } else { // We are in the parent process. if (ChildPid == -1) { printf("%s:%i - parent: Failed to create child", _FL); return false; } // Parent shouldn't read from child's stdin. if (close(in[0])) printf("%s:%i - close failed.\n", _FL); // Parent shouldn't write to child's stdout. if (close(out[1])) printf("%s:%i - close failed.\n", _FL); Io.Read = out[0]; Io.Write = in[1]; // printf("USE_SIMPLE_FORK success.\n"); return true; } #else #if DEBUG_SUBPROCESS LgiTrace("%s:%i - %p::Start(%i,%i,%i)\n", _FL, this, ReadAccess, WriteAccess, MapStderrToStdout); #endif // Find the end of the process list ::GArray p; for (GSubProcess *s=this; s; s=s->Child) { LgiAssert(!s->Child || s->Child->Parent == s); p.Add(s); } size_t Kids = p.Length() + 1; #ifdef WIN32 SECURITY_ATTRIBUTES Attr; Attr.nLength = sizeof(SECURITY_ATTRIBUTES); Attr.bInheritHandle = true; Attr.lpSecurityDescriptor = NULL; #else int Attr = 0; #endif #if defined(POSIX) ::GArray Pipes; Pipes.Length(Kids); Pipes[0].Create(&Attr); #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *PARENT* pipe[%i].create %i,%i\n", _FL, 0, Pipes[0].Read, Pipes[0].Write); #endif Status = true; for (int i=1; iChildPid = fork(); if (sp->ChildPid == INVALID_PID) { LgiTrace("%s:%i - fork failed with %i", _FL, errno); exit(1); } else if (sp->ChildPid == 0) { if (InitialFolder) { chdir(InitialFolder); } // Close irrelevant pipes for (int j = 0; j < i-1; j++) { #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *CHILD* pipe[%i].close %i,%i\n", _FL, j, Pipes[j].Read, Pipes[j].Write); #endif Pipes[j].Close(); } // Set up STDIN and STDOUT Pipe &in = Pipes[i-1]; Pipe &out = Pipes[i]; #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *CHILD* %i) Child init %i->'%s'->%i\n", _FL, i, in.Read, sp->Exe.Get(), out.Write); #endif #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *CHILD* Dupe %i->%i\n", _FL, in.Read, STDIN_FILENO); #endif Dupe(in.Read, STDIN_FILENO); #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *CHILD* Close %i\n", _FL, in.Write); #endif close(in.Write); #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *CHILD* Dupe %i->%i\n", _FL, out.Write, STDOUT_FILENO); #endif Dupe(out.Write, STDOUT_FILENO); #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *CHILD* Dupe %i->%i\n", out.Write, STDERR_FILENO); #endif Dupe(out.Write, STDERR_FILENO); #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *CHILD* Close %i\n", _FL, out.Read); #endif close(out.Read); fsync(STDOUT_FILENO); LgiSleep(100); // Execute the child sp->Args.Add(NULL); execvp(sp->Exe, &sp->Args[0]); LgiTrace("%s:%i - execvp('%s').\n", _FL, sp->Exe.Get()); for (int i=0; iArgs.Length(); i++) LgiTrace("%s:%i - Args[%i]='%s'\n", _FL, i, sp->Args[i]); Status = false; break; } } // Close irrelevant pipes for (int j = 1; j < Kids - 1; j++) { #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *PARENT* pipe[%i].close %i,%i\n", _FL, j, Pipes[j].Read, Pipes[j].Write); #endif Pipes[j].Close(); } #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *PARENT* pipe[0].close %i, pipe[%i].close %i\n", _FL, Pipes[0].Read, Pipes.Length()-1, Pipes.Last().Write); #endif close(Pipes[0].Read); close(Pipes.Last().Write); // Set the input and output pipes for this sub-process. if (WriteAccess) Io.Write = Pipes[0].Write; else { #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *PARENT* pipe[0].close %i\n", _FL, Pipes[0].Write); #endif close(Pipes[0].Write); } if (ReadAccess) Io.Read = Pipes.Last().Read; else { #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *PARENT* pipe[%i].close %i\n", _FL, Pipes.Length()-1, Pipes.Last().Read); #endif close(Pipes.Last().Read); } // LgiTrace("Final Handles %i, %i\n", Io.Read, Io.Write); #elif defined(WIN32) GAutoWString WExe; if (FileExists(Exe)) { WExe.Reset(Utf8ToWide(Exe)); } else { char *Ext = LgiGetExtension(Exe); bool HasExt = Ext && _stricmp(Ext, "exe") == 0; #if defined(WIN32) && !defined(PLATFORM_MINGW) GToken p; char *sPath = NULL; size_t sSize; errno_t err = _dupenv_s(&sPath, &sSize, "PATH"); if (err == 0) p.Parse(sPath, LGI_PATH_SEPARATOR); free(sPath); #else GToken p(getenv("PATH"), LGI_PATH_SEPARATOR); #endif for (unsigned i=0; i 0) { WArg[Ch++] = ' '; } if (strchr(a, ' ')) Ch += swprintf_s(WArg+Ch, CountOf(WArg)-Ch, L"\"%s\"", aw.Get()); else Ch += swprintf_s(WArg+Ch, CountOf(WArg)-Ch, L"%s", aw.Get()); } #if DEBUG_SUBPROCESS LgiTrace("%s:%i - Args='%S'\n", _FL, WArg); #endif bool HasExternIn = ExternIn != NULL_PIPE; #if DEBUG_SUBPROCESS LgiTrace("%s:%i - Oringinal handles, out=%p, in=%p, HasExternIn=%i\n", _FL, OldStdout, OldStdin, HasExternIn); #endif if (ChildOutput.Create(&Attr) && (HasExternIn || ChildInput.Create(&Attr))) { if (!SetHandleInformation(ChildOutput.Read, HANDLE_FLAG_INHERIT, 0)) LgiTrace("%s:%i - SetHandleInformation failed.\n", _FL); if (!HasExternIn && !SetHandleInformation(ChildInput.Write, HANDLE_FLAG_INHERIT, 0)) LgiTrace("%s:%i - SetHandleInformation failed.\n", _FL); #if DEBUG_SUBPROCESS LgiTrace("%s:%i - Output Pipe: rd=%p, wr=%p\n", _FL, ChildOutput.Read, ChildOutput.Write); if (!HasExternIn) LgiTrace("%s:%i - Input Pipe: rd=%p, wr=%p\n", _FL, ChildInput.Read, ChildInput.Write); #endif STARTUPINFOW Info; ZeroObj(Info); Info.cb = sizeof(Info); PROCESS_INFORMATION ProcInfo; ZeroObj(ProcInfo); Info.dwFlags = STARTF_USESTDHANDLES; Info.hStdOutput = ChildOutput.Write; Info.hStdInput = HasExternIn ? ExternIn : ChildInput.Read; if (MapStderrToStdout) Info.hStdError = ChildOutput.Write; GAutoWString WInitialFolder(Utf8ToWide(InitialFolder)); #if DEBUG_SUBPROCESS LgiTrace("%s:%i - WInitialFolder=%S, EnvironmentChanged=%i\n", _FL, WInitialFolder.Get(), EnvironmentChanged); #endif GAutoWString WEnv; if (EnvironmentChanged) { GMemQueue q(256); for (unsigned i=0; i 0) p.Write(Buf, Rd); else break; } return p.NewGStr(); } ssize_t GSubProcess::Read(void *Buf, ssize_t Size, int TimeoutMs) { #if defined(POSIX) bool DoRead = true; if (TimeoutMs) { OsSocket s = Io.Read; if (ValidSocket(s)) { struct timeval t = {TimeoutMs / 1000, (TimeoutMs % 1000) * 1000}; fd_set r; FD_ZERO(&r); FD_SET(s, &r); int v = select((int)s+1, &r, 0, 0, &t); if (v > 0 && FD_ISSET(s, &r)) { DoRead = true; } else { // printf("SubProc not readable..\n"); return 0; } } else LgiTrace("%s:%i - Invalid socket.\n", _FL); } return (int)read(Io.Read, Buf, Size); #else DWORD Rd = -1, Sz; if (!ReadFile(ChildOutput.Read, Buf, AssertCast(Sz, Size), &Rd, NULL)) return -1; return Rd; #endif } int GSubProcess::Peek() { #if defined(POSIX) int bytesAvailable = 0; int r = ioctl(Io.Read, FIONREAD, &bytesAvailable); return r ? -1 : bytesAvailable; #else DWORD Rd = 0, Avail = 0; char Buf[32]; if (PeekNamedPipe(ChildOutput.Read, Buf, sizeof(Buf), &Rd, &Avail, NULL)) return Rd; return 0; #endif } bool GSubProcess::Write(GString s) { auto Wr = Write(s.Get(), s.Length()); return Wr == s.Length(); } ssize_t GSubProcess::Write(const void *Buf, ssize_t Size, int Flags) { #if defined(POSIX) return (int)write(Io.Write, Buf, Size); #else DWORD Wr = -1, Sz; if (!WriteFile(ChildInput.Write, Buf, AssertCast(Sz, Size), &Wr, NULL)) return -1; return Wr; #endif } diff --git a/src/common/Lgi/GVariant.cpp b/src/common/Lgi/GVariant.cpp --- a/src/common/Lgi/GVariant.cpp +++ b/src/common/Lgi/GVariant.cpp @@ -1,2454 +1,2454 @@ #include #include #include "Lgi.h" #include "GVariant.h" #include "GToken.h" const char *GVariant::TypeToString(GVariantType 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_GSURFACE: return "Surface"; case GV_GVIEW: return "View"; case GV_GMOUSE: return "MouseEvent"; case GV_GKEY: return "KeyboardEvent"; case GV_STREAM: return "Stream"; default: return "Unknown"; } return NULL; } const char *GVariant::OperatorToString(GOperator 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; } GVariant::GVariant() { Type = GV_NULL; ZeroObj(Value); } GVariant::GVariant(GVariant const &v) { Type = GV_NULL; ZeroObj(Value); *this = v; } #if GVARIANT_SIZET GVariant::GVariant(size_t i) { Type = GV_NULL; *this = i; } #endif #if GVARIANT_sSIZET GVariant::GVariant(ssize_t i) { Type = GV_NULL; *this = i; } #endif GVariant::GVariant(int32 i) { Type = GV_INT32; Value.Int = i; } -GVariant::GVariant(uint32 i) +GVariant::GVariant(uint32_t i) { Type = GV_INT32; Value.Int = i; } GVariant::GVariant(int64 i) { Type = GV_INT64; Value.Int64 = i; } GVariant::GVariant(uint64 i) { Type = GV_INT64; Value.Int64 = i; } GVariant::GVariant(double i) { Type = GV_DOUBLE; Value.Dbl = i; } GVariant::GVariant(const char *s) { Value.String = NewStr(s); Type = Value.String ? GV_STRING : GV_NULL; } GVariant::GVariant(const char16 *s) { Value.WString = NewStrW(s); Type = Value.WString ? GV_WSTRING : GV_NULL; } GVariant::GVariant(void *p) { Type = GV_NULL; *this = p; } GVariant::GVariant(GDom *p) { Type = GV_NULL; *this = p; } GVariant::GVariant(GDom *p, char *name) { Type = GV_NULL; SetDomRef(p, name); } GVariant::GVariant(LDateTime *d) { Type = GV_NULL; *this = d; } GVariant::GVariant(GOperator Op) { Type = GV_OPERATOR; Value.Op = Op; } GVariant::~GVariant() { Empty(); } bool GVariant::operator ==(GVariant &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; GVariant *a, *b; for (a = Value.Lst->First(), b = v.Value.Lst->First(); a && b; a = Value.Lst->Next(), b = v.Value.Lst->Next()) { if (!(*a == *b)) break; } return !a && !b; } 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_GSURFACE: 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_GMOUSE: return Value.Mouse == v.Value.Mouse; case GV_GKEY: return Value.Key == v.Value.Key; case GV_VOID_PTR: return Value.Ptr == v.Value.Ptr; case GV_HASHTABLE: { LgiAssert(0); break; } } return false; } GVariant &GVariant::operator =(LDateTime *d) { Empty(); if (d) { Type = GV_DATETIME; Value.Date = new LDateTime; if (Value.Date) { *Value.Date = *d; // if (Dirty) *Dirty = true; } } return *this; } GVariant &GVariant::operator =(bool i) { Empty(); Type = GV_BOOL; Value.Bool = i; // if (Dirty) *Dirty = true; return *this; } #if GVARIANT_SIZET GVariant &GVariant::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 GVARIANT_SSIZET GVariant &GVariant::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 GVariant &GVariant::operator =(int32 i) { Empty(); Type = GV_INT32; Value.Int = i; return *this; } -GVariant &GVariant::operator =(uint32 i) +GVariant &GVariant::operator =(uint32_t i) { Empty(); Type = GV_INT32; Value.Int = i; return *this; } GVariant &GVariant::operator =(int64 i) { Empty(); Type = GV_INT64; Value.Int64 = i; return *this; } GVariant &GVariant::operator =(uint64 i) { Empty(); Type = GV_INT64; Value.Int64 = i; return *this; } #ifdef BEOS GVariant &GVariant::operator =(int32 i) { Empty(); Type = GV_INT32; Value.Int = i; // if (Dirty) *Dirty = true; return *this; } #endif GVariant &GVariant::operator =(double i) { Empty(); Type = GV_DOUBLE; Value.Dbl = i; // if (Dirty) *Dirty = true; return *this; } GVariant &GVariant::operator =(const char *s) { Empty(); if (s) { Type = GV_STRING; Value.String = NewStr(s); } return *this; } GVariant &GVariant::operator =(const char16 *s) { Empty(); if (s) { Type = GV_WSTRING; Value.WString = NewStrW(s); } // if (Dirty) *Dirty = true; return *this; } GVariant &GVariant::operator =(void *p) { Empty(); if (p) { Type = GV_VOID_PTR; Value.Ptr = p; // if (Dirty) *Dirty = true; } return *this; } GVariant &GVariant::operator =(GDom *p) { Empty(); if (p) { Type = GV_DOM; Value.Dom = p; // if (Dirty) *Dirty = true; } return *this; } GVariant &GVariant::operator =(GView *p) { Empty(); if (p) { Type = GV_GVIEW; Value.View = p; // if (Dirty) *Dirty = true; } return *this; } GVariant &GVariant::operator =(GMouse *p) { Empty(); if (p) { Type = GV_GMOUSE; Value.Mouse = p; // if (Dirty) *Dirty = true; } return *this; } GVariant &GVariant::operator =(GKey *p) { Empty(); if (p) { Type = GV_GKEY; Value.Key = p; } return *this; } GVariant &GVariant::operator =(GStream *s) { Empty(); if (s) { Type = GV_STREAM; Value.Stream.Ptr = s; Value.Stream.Own = false; } return *this; } GVariant &GVariant::operator =(GVariant 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(((GVariant&)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_GMOUSE: case GV_GKEY: { 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 (GVariant *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 GVariant(*it.value)); } } } break; } case GV_CUSTOM: { Value.Custom.Data = i.Value.Custom.Data; Value.Custom.Dom = i.Value.Custom.Dom; break; } case GV_GSURFACE: { Value.Surface = i.Value.Surface; if (Value.Surface.Own && Value.Surface.Ptr) Value.Surface.Ptr->AddRef(); 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); LgiAssert(0); break; } } // if (Dirty) *Dirty = true; return *this; } bool GVariant::SetDomRef(GDom *obj, char *name) { Empty(); Type = GV_DOMREF; Value.DomRef.Dom = obj; Value.DomRef.Name = NewStr(name); return Value.DomRef.Name != 0; } bool GVariant::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; } bool GVariant::SetList(List *Lst) { Empty(); Type = GV_LIST; if ((Value.Lst = new List) && Lst) { for (GVariant *s=Lst->First(); s; s=Lst->Next()) { GVariant *New = new GVariant; if (New) { *New = *s; Value.Lst->Insert(New); } } } return Value.Lst != 0; } bool GVariant::SetHashTable(LHash *Table, bool Copy) { Empty(); Type = GV_HASHTABLE; if (Copy && Table) { if ((Value.Hash = new LHash)) { // const char *k; // for (GVariant *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 GVariant::SetSurface(class GSurface *Ptr, bool Own) { Empty(); if (!Ptr) return false; Type = GV_GSURFACE; Value.Surface.Ptr = Ptr; if ((Value.Surface.Own = Own)) Value.Surface.Ptr->AddRef(); return true; } bool GVariant::SetStream(class GStream *Ptr, bool Own) { Empty(); if (!Ptr) return false; Type = GV_STREAM; Value.Stream.Ptr = Ptr; Value.Stream.Own = Own; return true; } bool GVariant::OwnStr(char *s) { Empty(); if (!s) return false; Type = GV_STRING; Value.String = s; return true; } bool GVariant::OwnStr(char16 *w) { Empty(); if (!w) return false; Type = GV_WSTRING; Value.WString = w; return true; } char *GVariant::ReleaseStr() { char *Ret = Str(); if (Ret) { Value.String = 0; Type = GV_NULL; } return Ret; } char *GVariant::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 *GVariant::ReleaseWStr() { char16 *Ret = WStr(); if (Ret) { Value.WString = 0; Type = GV_NULL; } return Ret; } char16 *GVariant::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 GVariant::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 (GVariant *v = (GVariant*) Value.Hash->First(); v; v = (GVariant*) Value.Hash->Next()) for (auto i : *Value.Hash) { DeleteObj(i.value); } DeleteObj(Value.Hash); } break; } case GV_GSURFACE: { 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 GVariant::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: { GVariant 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 (GVariant *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_GSURFACE: { int64 Sz = 0; if (Value.Surface.Ptr) { GRect 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(GView); case GV_GMOUSE: return sizeof(GMouse); case GV_GKEY: return sizeof(GKey); case GV_STREAM: return Value.Stream.Ptr->GetSize(); default: break; } return 0; } bool GVariant::IsInt() { return Type == GV_INT32 || Type == GV_INT64; } bool GVariant::IsBool() { return Type == GV_BOOL; } bool GVariant::IsDouble() { return Type == GV_DOUBLE; } bool GVariant::IsString() { return Type == GV_STRING; } bool GVariant::IsBinary() { return Type == GV_BINARY; } bool GVariant::IsNull() { return Type == GV_NULL; } #define IsList() (Type == GV_LIST && Value.Lst) GVariant &GVariant::Cast(GVariantType NewType) { if (NewType != Type) { switch (NewType) { default: { // No conversion possible break; } case GV_INT32: { *this = (int)CastInt32(); break; } case GV_INT64: { *this = 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)Value.Int64); Empty(); Value.Date = Dt; Type = NewType; } break; } default: { // No conversion available break; } } break; } } } return *this; } void *GVariant::CastVoidPtr() { 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_GSURFACE: return Value.Surface.Ptr; case GV_GVIEW: return Value.View; case GV_GMOUSE: return Value.Mouse; case GV_GKEY: return Value.Key; } return 0; } GDom *GVariant::CastDom() { switch (Type) { default: break; case GV_DOM: return Value.Dom; case GV_DOMREF: return Value.DomRef.Dom; case GV_STREAM: return Value.Stream.Ptr; case GV_GSURFACE: return Value.Surface.Ptr; case GV_CUSTOM: return Value.Custom.Dom; } return NULL; } bool GVariant::CastBool() { switch (Type) { default: LgiAssert(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_GMOUSE: return Value.Mouse != NULL; case GV_GKEY: 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 (!StringVariant) 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 GVariant::CastDouble() { 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 GVariant v; if (Value.DomRef.Dom) { if (Value.DomRef.Dom->GetValue(Value.DomRef.Name, v)) { return v.CastDouble(); } } break; } } return 0; } int32 GVariant::CastInt32() { 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: return Value.String ? atoi(Value.String) : 0; case GV_DOM: return Value.Dom != 0; case GV_DOMREF: { static GVariant 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_GSURFACE: return Value.Surface.Ptr != NULL; case GV_GVIEW: return Value.View != NULL; case GV_GMOUSE: return Value.Mouse != NULL; case GV_GKEY: return Value.Key != NULL; case GV_STREAM: return Value.Stream.Ptr != NULL; } return 0; } int64 GVariant::CastInt64() { 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) { #ifdef _MSC_VER return _atoi64(Value.String); #else return atoll(Value.String); #endif } break; } case GV_DOMREF: { static GVariant v; if (Value.DomRef.Dom) { if (Value.DomRef.Dom->GetValue(Value.DomRef.Name, v)) { return v.CastInt64(); } } break; } } return 0; } char *GVariant::CastString() { char i[40]; switch (Type) { default: break; case GV_LIST: { GStringPipe p(256); List::I it = Value.Lst->begin(); bool First = true; p.Print("{"); for (GVariant *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: { GStringPipe p(256); p.Print("{"); bool First = true; // const char *k; // for (GVariant *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 GVariant 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; } } return 0; } ///////////////////////////////////////////////////////////////////////////////// GDom *GDom::ResolveObject(const char *Var, char *Name, char *Array) { GDom *Object = this; // Tokenise the string GArray t; for (const char *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++; } size_t Len = e - s; if (Len < 1 || Len > 255) { Object = 0; break; } char Part[256]; memcpy(Part, s, Len); Part[Len] = 0; while (strchr(" \t", Part[Len-1])) { Len--; } t[t.Length()] = NewStr(Part, Len); s = *e ? e + 1 : e; } // Process elements for (int i=0; i Name && strchr(" \t\r\n", *e)) { *e-- = 0; } } else { GVariant v; if (Index[0]) { if (Object->GetVariant(Base, v, Index)) { if (v.Type == GV_LIST) { int N = atoi(Index); GVariant *Element = v.Value.Lst->ItemAt(N); if (Element && Element->Type == GV_DOM) { Object = Element->Value.Dom; } else { Object = 0; goto ResolveDone; } } else if (v.Type == GV_DOM) { Object = v.Value.Dom; } else { Object = 0; goto ResolveDone; } } else { Object = 0; goto ResolveDone; } } else { if (Object->GetVariant(Obj, v) && v.Type == GV_DOM) { Object = v.Value.Dom; } else { Object = 0; goto ResolveDone; } } } } ResolveDone: t.DeleteArrays(); return Object; } struct GDomPropMap { LHashTbl, GDomProperty> ToProp; LHashTbl, const char *> ToString; GDomPropMap() { #undef _ #define _(symbol) Define(#symbol, symbol); #include "LDomFields.h" #undef _ Check("Length", ObjLength); Check("Type", ObjType); Check("Name", ObjName); Check("Style", ObjStyle); Check("Class", ObjClass); Check("Field", ObjField); Check("Debug", ObjDebug); Check("textContent", ObjTextContent); Check("innerHTML", ObjInnerHtml); Check("List", TypeList); Check("HashTable", TypeHashTable); Check("File", TypeFile); Check("Surface", TypeSurface); Check("Int", TypeInt); Check("Double", TypeDouble); Check("String", TypeString); Check("DateTime", TypeDateTime); Check("Year", DateYear); Check("Month", DateMonth); Check("Day", DateDay); Check("Hour", DateHour); Check("Minute", DateMinute); Check("Second", DateSecond); Check("Date", DateDate); Check("Time", DateTime); Check("DateAndTime", DateDateAndTime); Check("Timestamp", DateTimestamp); Check("SetNow", DateSetNow); Check("SetStr", DateSetStr); Check("GetStr", DateGetStr); Check("Join", StrJoin); Check("Split", StrSplit); Check("Find", StrFind); Check("Rfind", StrRfind); Check("Lower", StrLower); Check("Upper", StrUpper); Check("Strip", StrStrip); Check("Sub", StrSub); Check("X", SurfaceX); Check("Y", SurfaceY); Check("Bits", SurfaceBits); Check("ColourSpace", SurfaceColourSpace); Check("IncludeCursor", SurfaceIncludeCursor); Check("Add", ContainerAdd); Check("Delete", ContainerDelete); Check("HasKey", ContainerHasKey); Check("Sort", ContainerSort); Check("Children", ContainerChildren); Check("Span", ContainerSpan); Check("Align", ContainerAlign); Check("VAlign", ContainerVAlign); Check("Open", FileOpen); Check("Read", FileRead); Check("Write", FileWrite); Check("Pos", FilePos); Check("Close", FileClose); Check("Modified", FileModified); Check("Folder", FileFolder); Check("Encoding", FileEncoding); Check("Readable", StreamReadable); Check("Writable", StreamWritable); Check("HtmlImagesLinkCid", HtmlImagesLinkCid); Check("SpellCheckLanguage", SpellCheckLanguage); Check("SpellCheckDictionary", SpellCheckDictionary); Check("AppendSeparator", AppendSeparator); Check("AppendItem", AppendItem); Check("AppendSubmenu", AppendSubmenu); } void Define(const char *s, GDomProperty p) { static const char *Prefixes[] = { "Obj", "File", "Date", "Type", "Surface", "Stream", "Str", "Container" }; if (!p) return; for (unsigned i=0; iGetVariant(Name, Value, ValidArray ? Arr : 0); } } _OnAccess(false); } else { LgiTrace("%s:%i - Locking error\n", _FL); LgiAssert(0); } } return Status; } bool GDom::SetValue(const char *Var, GVariant &Value) { bool Status = false; if (Var) { // LMutex *Sem = dynamic_cast(this); if (_OnAccess(true)) { char Name[256], Arr[256] = ""; GDom *Object = ResolveObject(Var, Name, Arr); if (Object) { if (Name[0] == 0) LgiTrace("%s:%i - Warning name parse failed for '%s'\n", _FL, Var); else { bool Valid = ValidStr(Arr); Status = Object->SetVariant(Name, Value, Valid ? Arr : 0); } } _OnAccess(false); } else { LgiTrace("%s:%i - Locking error\n", _FL); LgiAssert(0); } } return Status; } bool GVariant::Add(GVariant *v, int Where) { if (!v) { LgiAssert(!"No value to insert."); return false; } if (Type == GV_NULL) SetList(); if (Type != GV_LIST) { LgiAssert(!"Not a list variant"); return false; } return Value.Lst->Insert(v, Where); } GString GVariant::ToString() { GString 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_GSURFACE: s.Printf("(gsurface)%p", Value.Surface.Ptr); break; case GV_GVIEW: s.Printf("(gview)%p", Value.View); break; case GV_GMOUSE: s.Printf("(gmouse)%p", Value.Mouse); break; case GV_GKEY: 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; } ///////////////////////////////////////////////////////////////////////////////////////////////////// GCustomType::GCustomType(const char *name, int pack) : FldMap(0, -1) { Name = name; Pack = 1; Size = 0; } GCustomType::GCustomType(const char16 *name, int pack) : FldMap(0, -1) { Name = name; Pack = 1; Size = 0; } GCustomType::~GCustomType() { Flds.DeleteObjects(); Methods.DeleteObjects(); } size_t GCustomType::Sizeof() { return (size_t)PadSize(); } ssize_t GCustomType::PadSize() { if (Pack > 1) { // Bump size to the pack boundary... int Remain = Size % Pack; if (Remain) return Size + Pack - Remain; } return Size; } int GCustomType::IndexOf(const char *Field) { return FldMap.Find(Field); } int GCustomType::AddressOf(const char *Field) { if (!Field) return -1; for (unsigned i=0; iName, Field)) return (int)i; } return -1; } bool GCustomType::DefineField(const char *Name, GCustomType *Type, int ArrayLen) { if (ArrayLen < 1) { LgiAssert(!"Can't have zero size field."); return false; } if (Name == NULL || Type == NULL) { LgiAssert(!"Invalid parameter."); return false; } if (FldMap.Find(Name) >= 0) { LgiAssert(!"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 GCustomType::DefineField(const char *Name, GVariantType Type, int Bytes, int ArrayLen) { if (ArrayLen < 1) { LgiAssert(!"Can't have zero size field."); return false; } if (Name == NULL) { LgiAssert(!"No field name."); return false; } if (FldMap.Find(Name) >= 0) { LgiAssert(!"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; } GCustomType::Method *GCustomType::GetMethod(const char *Name) { return MethodMap.Find(Name); } GCustomType::Method *GCustomType::DefineMethod(const char *Name, GArray &Params, size_t Address) { Method *m = MethodMap.Find(Name); if (m) { LgiAssert(!"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 GCustomType::CustomField::GetVariant(const char *Field, GVariant &Value, char *Array) { GDomProperty p = LgiStringToDomProp(Field); switch (p) { case ObjName: // Type: String Value = Name; break; case ObjLength: // Type: Int32 Value = Bytes; break; default: return false; } return true; } ssize_t GCustomType::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(GVariant::LHash); case GV_OPERATOR: return sizeof(GOperator); case GV_GMOUSE: return sizeof(GMouse); case GV_GKEY: return sizeof(GKey); case GV_CUSTOM: return Nested->Sizeof(); default: LgiAssert(!"Unknown type."); break; } return 0; } -bool GCustomType::Get(int Index, GVariant &Out, uint8 *This, int ArrayIndex) +bool GCustomType::Get(int Index, GVariant &Out, uint8_t *This, int ArrayIndex) { if (Index < 0 || Index >= Flds.Length() || !This) { LgiAssert(!"Invalid parameter error."); return false; } CustomField *Def = Flds[Index]; if (ArrayIndex < 0 || ArrayIndex >= Def->ArrayLen) { LgiAssert(!"Array out of bounds."); return false; } - uint8 *Ptr = This + Def->Offset; + 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*)Ptr)[ArrayIndex]; + 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: { LgiAssert(!"Unknown integer size."); return false; } } break; } case GV_MAX: { Out = *((GVariant*)Ptr); break; } default: { LgiAssert(!"Impl this type."); return false; } } return true; } -bool GCustomType::Set(int Index, GVariant &In, uint8 *This, int ArrayIndex) +bool GCustomType::Set(int Index, GVariant &In, uint8_t *This, int ArrayIndex) { if (Index < 0 || Index >= Flds.Length() || !This) { LgiAssert(!"Invalid parameter error."); return false; } CustomField *Def = Flds[Index]; - uint8 *Ptr = This + Def->Offset; + uint8_t *Ptr = This + Def->Offset; if (ArrayIndex < 0 || ArrayIndex >= Def->ArrayLen) { LgiAssert(!"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 = LgiBufConvertCp(Ptr, LGI_WideCharset, Def->ArrayLen-1, In, "utf-8", Len); if (Ch >= 0) { // Null terminate Ptr[Ch] = 0; } else { LgiAssert(!"LgiBufConvertCp 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 = LgiBufConvertCp(Ptr, "utf-8", Def->ArrayLen-sizeof(char16), In, LGI_WideCharset, Len); if (Ch >= 0) { // Null terminate p[Ch/sizeof(char16)] = 0; } else { LgiAssert(!"LgiBufConvertCp 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*)Ptr)[ArrayIndex] = In.CastInt32(); + ((uint32_t*)Ptr)[ArrayIndex] = In.CastInt32(); break; } case 8: { ((uint64*)Ptr)[ArrayIndex] = In.CastInt64(); break; } default: { LgiAssert(!"Unknown integer size."); return false; } } break; } case GV_MAX: { *((GVariant*)Ptr) = In; break; } default: LgiAssert(!"Impl this type."); break; } return true; } bool GCustomType::GetVariant(const char *Field, GVariant &Value, char *Array) { GDomProperty p = LgiStringToDomProp(Field); switch (p) { case ObjName: // Type: String { Value = Name; return true; } case ObjType: // Type: String { Value = "GCustomType"; 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 = (GDom*)&Flds[Index]; return true; } } else { Value = (int)Flds.Length(); break; } break; } default: break; } LgiAssert(0); return false; } bool GCustomType::SetVariant(const char *Name, GVariant &Value, char *Array) { LgiAssert(0); return false; } bool GCustomType::CallMethod(const char *MethodName, GVariant *ReturnValue, GArray &Args) { if (!MethodName || !ReturnValue) return false; if (!_stricmp(MethodName, "New")) { ReturnValue->Empty(); ReturnValue->Type = GV_CUSTOM; ReturnValue->Value.Custom.Dom = this; - ReturnValue->Value.Custom.Data = new uint8[Sizeof()]; + ReturnValue->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; } LgiAssert(0); return false; } diff --git a/src/common/Lgi/GWindowCommon.cpp b/src/common/Lgi/GWindowCommon.cpp --- a/src/common/Lgi/GWindowCommon.cpp +++ b/src/common/Lgi/GWindowCommon.cpp @@ -1,252 +1,252 @@ // // GWindowCommon.cpp // LgiCarbon // // Created by Matthew Allen on 11/09/14. // // #include "Lgi.h" #include "INet.h" void GWindow::BuildShortcuts(ShortcutMap &Map, GViewI *v) { if (!v) v = this; GAutoPtr Ch(v->IterateViews()); for (GViewI *c = Ch->First(); c; c = Ch->Next()) { char *n = c->Name(), *amp; if (n && (amp = strchr(n, '&'))) { amp++; if (*amp && *amp != '&') { - uint8 *i = (uint8*)amp; + uint8_t *i = (uint8_t*)amp; ssize_t sz = Strlen(amp); int32 ch = LgiUtf8To32(i, sz); if (ch) Map.Add(ToUpper(ch), c); } } BuildShortcuts(Map, c); } } void GWindow::MoveOnScreen() { GRect p = GetPos(); GArray Displays; GRect Screen(0, 0, -1, -1); if ( #if WINNATIVE !IsZoomed(Handle()) && !IsIconic(Handle()) && #endif LgiGetDisplays(Displays)) { int Best = -1; int Pixels = 0; int Close = 0x7fffffff; for (int i=0; ir); if (o.Valid()) { int Pix = o.X()*o.Y(); if (Best < 0 || Pix > Pixels) { Best = i; Pixels = Pix; } } else if (Pixels == 0) { int n = Displays[i]->r.Near(p); if (n < Close) { Best = i; Close = n; } } } if (Best >= 0) Screen = Displays[Best]->r; } if (!Screen.Valid()) Screen.Set(0, 0, GdcD->X()-1, GdcD->Y()-1); if (p.x2 >= Screen.x2) p.Offset(Screen.x2 - p.x2, 0); if (p.y2 >= Screen.y2) p.Offset(0, Screen.y2 - p.y2); if (p.x1 < Screen.x1) p.Offset(Screen.x1 - p.x1, 0); if (p.y1 < Screen.y1) p.Offset(0, Screen.y1 - p.y1); SetPos(p, true); Displays.DeleteObjects(); } void GWindow::MoveToCenter() { GRect Screen(0, 0, GdcD->X()-1, GdcD->Y()-1); GRect p = GetPos(); p.Offset(-p.x1, -p.y1); p.Offset((Screen.X() - p.X()) / 2, (Screen.Y() - p.Y()) / 2); SetPos(p, true); } void GWindow::MoveToMouse() { GMouse m; if (GetMouse(m, true)) { GRect p = GetPos(); p.Offset(-p.x1, -p.y1); p.Offset(m.x-(p.X()/2), m.y-(p.Y()/2)); SetPos(p, true); MoveOnScreen(); } } bool GWindow::MoveSameScreen(GViewI *wnd) { if (!wnd) { LgiAssert(0); return false; } GRect p = wnd->GetPos(); int cx = p.x1 + (p.X() >> 4); int cy = p.y1 + (p.Y() >> 4); GRect np = GetPos(); np.Offset(cx - np.x1, cy - np.y1); SetPos(np); MoveOnScreen(); return true; } int GWindow::WillAccept(List &Formats, GdcPt2 Pt, int KeyState) { int Status = DROPEFFECT_NONE; #if DEBUG_DND LgiTrace("%s:%i - WillAccept Formats=%i Pt=%i,%i Key=0x%x\n", _FL, Formats.Length(), Pt.x, Pt.y, KeyState); #endif for (char *f=Formats.First(); f; ) { #if DEBUG_DND LgiTrace("\tFmt=%s\n", f); #endif if (!stricmp(f, LGI_FileDropFormat)) { f = Formats.Next(); Status = DROPEFFECT_COPY; } else { Formats.Delete(f); DeleteArray(f); f = Formats.Current(); } } return Status; } int GWindow::OnDrop(GArray &Data, GdcPt2 Pt, int KeyState) { int Status = DROPEFFECT_NONE; #ifdef DEBUG_DND LgiTrace("%s:%i - OnDrop Data=%i Pt=%i,%i Key=0x%x\n", _FL, Data.Length(), Pt.x, Pt.y, KeyState); #endif for (unsigned i=0; i Files; GString::Array Uri; for (unsigned n=0; nIsBinary()) { GString::Array a = GString((char*)Data->Value.Binary.Data, Data->Value.Binary.Length).SplitDelimit("\r\n"); Uri.Add(a); } else if (Data->Str()) { Uri.New() = Data->Str(); } else if (Data->Type == GV_LIST) { for (GVariant *v=Data->Value.Lst->First(); v; v=Data->Value.Lst->Next()) { char *f = v->Str(); Uri.New() = f; } } } for (int i=0; iDetach(); Unlock(); } } } void LThreadWorker::Attach(LThreadTarget *o) { LMutex::Auto a(this, _FL); if (!Owners.HasItem(o)) { LgiAssert(o->Worker == this); Owners.Add(o); if (Owners.Length() == 1) { Loop = true; Run(); } } } void LThreadWorker::Detach(LThreadTarget *o) { LMutex::Auto a(this, _FL); LgiAssert(Owners.HasItem(o)); Owners.Delete(o); } void LThreadWorker::AddJob(LThreadJob *j) { if (Lock(_FL)) { Jobs.Add(j); if (!Owners.HasItem(j->Owner)) Attach(j->Owner); Unlock(); } } void LThreadWorker::DoJob(LThreadJob *j) { j->Do(); } int LThreadWorker::Main() { while (Loop) { GAutoPtr j; if (Lock(_FL)) { if (Jobs.Length()) { j.Reset(Jobs[0]); Jobs.DeleteAt(0, true); } Unlock(); } if (j) { DoJob(j); if (Lock(_FL)) { if (Owners.IndexOf(j->Owner) < 0) { // Owner is gone already... delete the job. j.Reset(); } else { // Pass job back to owner. // This needs to be in the lock so that the owner can't delete itself // from the owner list... j->Owner->OnDone(j); } Unlock(); } } else LgiSleep(50); } return 0; } ///////////////////////////////////////////////////////////////////////////////////////// LThreadOwner::~LThreadOwner() { if (Lock(_FL)) { if (Worker) Worker->Detach(this); Unlock(); } } //////////////////////////////////////////////////////////////////////////////////////// /* GEventSinkPtr::GEventSinkPtr(GEventTargetThread *p, bool own) { Ptr = p; OwnPtr = own; if (p && p->Lock(_FL)) { p->Ptrs.Add(this); p->Unlock(); } } GEventSinkPtr::~GEventSinkPtr() { if (Lock(_FL)) { GEventTargetThread *tt = dynamic_cast(Ptr); if (tt) { if (tt->Lock(_FL)) { if (!tt->Ptrs.Delete(this)) LgiAssert(0); tt->Unlock(); } } if (OwnPtr) delete Ptr; Ptr = NULL; } } */ diff --git a/src/common/Lgi/LThreadEvent.cpp b/src/common/Lgi/LThreadEvent.cpp --- a/src/common/Lgi/LThreadEvent.cpp +++ b/src/common/Lgi/LThreadEvent.cpp @@ -1,423 +1,423 @@ #include "Lgi.h" #include "LThreadEvent.h" #if USE_POSIX_SEM #define SEM_NULL -1 #endif #if defined(LINUX) || COCOA #include #endif #if USE_MACH_SEM #include #include #include #endif #if POSIX #include void TimeoutToTimespec(struct timespec &to, int TimeoutMs) { timeval tv; gettimeofday(&tv, NULL); to.tv_sec = tv.tv_sec + (TimeoutMs / 1000); to.tv_nsec = (tv.tv_usec + ((TimeoutMs % 1000) * 1000)) * 1000; int sec = 1000000000; while (to.tv_nsec > sec) { to.tv_nsec -= sec; to.tv_sec++; } } #endif #define DEBUG_THREADING 0 #define USE_NAMED_SEM 0 LThreadEvent::LThreadEvent(const char *name) { Name(name); #if USE_MACH_SEM Task = mach_task_self(); Sem = 0; #if DEBUG_THREADING kern_return_t r = #endif semaphore_create(Task, &Sem, SYNC_POLICY_FIFO, 0); #if DEBUG_THREADING printf("%s:%i - semaphore_create(%x)=%i\n", _FL, (int)Sem, r); #endif #elif USE_POSIX_SEM #if USE_NAMED_SEM char Str[256]; ZeroObj(Str); sprintf_s(Str, sizeof(Str), "lgi.sem.%p", this); printf("Str=%p - %p\n", Str, Str+sizeof(Str)); Sem = sem_open(Str, O_CREAT, 0666, 0); if (Sem == SEM_FAILED) { printf("%s:%i - sem_open failed with %i.\n", _FL, errno); } #else Sem = &Local; int r = sem_init(Sem, 0, 0); if (r) { printf("%s:%i - sem_init failed with %i\n", _FL, errno); } #endif else { #if DEBUG_THREADING printf("%p::LThreadEvent init\n", this); #endif } #elif defined(POSIX) // Value = 0; pthread_mutexattr_t mattr; int e = pthread_cond_init(&Cond, NULL); if (e) printf("%s:%i - pthread_cond_init failed %i\n", _FL, e); e = pthread_mutexattr_init(&mattr); if (e) printf("%s:%i - pthread_mutexattr_init failed %i\n", _FL, e); e = pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE); if (e) printf("%s:%i - pthread_mutexattr_settype failed %i\n", _FL, e); e = pthread_mutex_init(&Mutex, &mattr); if (e) printf("%s:%i - pthread_mutex_init failed %i\n", _FL, e); #elif defined(WIN32) GString nm; nm.Printf("%s.%p", name, this); Event = CreateEventA(NULL, false, false, nm); if (Event) LastError = GetLastError(); else LgiAssert(!"Failed to create event."); #else #error "Impl me." #endif } LThreadEvent::~LThreadEvent() { #if USE_MACH_SEM #if DEBUG_THREADING kern_return_t r = #endif semaphore_destroy(Task, Sem); #if DEBUG_THREADING printf("%s:%i - semaphore_destroy(%x)=%i\n", _FL, (int)Sem, r); #endif #elif USE_POSIX_SEM // printf("%s:%i - ~LThreadEvent %i\n", _FL, Sem); if (Sem != SEM_FAILED) { #if USE_NAMED_SEM // printf("%s:%i - sem_close(%i) in %i\n", _FL, Sem, GetCurrentThreadId()); sem_close(Sem); #else sem_destroy(Sem); #endif Sem = SEM_FAILED; #if DEBUG_THREADING printf("%p::~LThreadEvent destroy\n", this); #endif } #elif defined(POSIX) pthread_cond_destroy(&Cond); pthread_mutex_destroy(&Mutex); #elif defined(WIN32) CloseHandle(Event); #else #error "Impl me." #endif } bool LThreadEvent::IsOk() { #if USE_MACH_SEM return true; #elif USE_POSIX_SEM return Sem != SEM_FAILED; #elif defined(POSIX) return true; #elif defined(WIN32) return Event != NULL; #else #error "Impl me." return false; #endif } bool LThreadEvent::Signal() { #if USE_MACH_SEM kern_return_t r = semaphore_signal(Sem); #if DEBUG_THREADING printf("%s:%i - semaphore_signal(%x)=%i\n", _FL, (int)Sem, r); #endif return r == KERN_SUCCESS; #elif USE_POSIX_SEM if (!IsOk()) return false; int r; #if DEBUG_THREADING int val = 1111; r = sem_getvalue (Sem, &val); printf("%s:%i - calling sem_post(%i) in %i (val=%i, name=%s)\n", _FL, Sem, GetCurrentThreadId(), val, Name()); #endif r = sem_post(Sem); #if DEBUG_THREADING sem_getvalue (Sem, &val); printf("%s:%i - sem_post(%i) = %i in %i (val=%i, name=%s)\n", _FL, Sem, r, GetCurrentThreadId(), val, Name()); #endif if (r) { printf("%s:%i - sem_post failed.\n", _FL); return false; } else { #if DEBUG_THREADING printf("%p::LThreadEvent signal\n", this); #endif } #elif defined(POSIX) int e = pthread_mutex_lock(&Mutex); if (e) printf("%s:%i - pthread_mutex_lock failed %i\n", _FL, e); e = pthread_mutex_unlock(&Mutex); if (e) printf("%s:%i - pthread_mutex_unlock failed %i\n", _FL, e); e = pthread_cond_signal(&Cond); /* signal SendThread */ if (e) printf("%s:%i - pthread_cond_signal failed %i\n", _FL, e); #elif defined(WIN32) if (Event) { BOOL b = SetEvent(Event); if (!b) { DWORD e = GetLastError(); LgiTrace("%s:%i - SetEvent failed with: %u\n", _FL, e); } } else { LgiAssert(!"No event handle"); return false; } #else #error "Impl me." #endif return true; } LThreadEvent::WaitStatus LThreadEvent::Wait(int32 Timeout) { #if USE_MACH_SEM if (Timeout > 0) { mach_timespec_t Ts; Ts.tv_sec = Timeout / 1000; Ts.tv_nsec = (Timeout % 1000) * 1000000; while (true) { kern_return_t r = semaphore_timedwait(Sem, Ts); #if DEBUG_THREADING printf("%s:%i - semaphore_timedwait(%x)=%i\n", _FL, (int)Sem, r); #endif switch(r) { case KERN_SUCCESS: return WaitSignaled; case KERN_OPERATION_TIMED_OUT: return WaitTimeout; case KERN_ABORTED: break; default: return WaitError; } } } else { kern_return_t r = semaphore_wait(Sem); #if DEBUG_THREADING printf("%s:%i - semaphore_wait(%x)=%i\n", _FL, (int)Sem, r); #endif switch(r) { case KERN_SUCCESS: return WaitSignaled; case KERN_OPERATION_TIMED_OUT: return WaitTimeout; default: return WaitError; } } #elif USE_POSIX_SEM if (!IsOk()) return WaitError; int r; if (Timeout < 0) { #if DEBUG_THREADING printf("%s:%i - starting sem_wait(%i) in %i (name=%s)\n", _FL, Sem, GetCurrentThreadId(), Name()); #endif r = sem_wait(Sem); #if DEBUG_THREADING printf("%s:%i - sem_wait(%i) = %i in %i (name=%s)\n", _FL, Sem, r, GetCurrentThreadId(), Name()); #endif if (r) { printf("%s:%i - sem_wait failed with %i.\n", _FL, errno); return WaitError; } #if DEBUG_THREADING printf("%p::LThreadEvent signalled\n", this); #endif } else { timespec to; TimeoutToTimespec(to, Timeout); #if DEBUG_THREADING printf("%s:%i - starting sem_timedwait(%i) in %i\n", _FL, Sem, GetCurrentThreadId()); #endif r = sem_timedwait(Sem, &to); int ErrCode = r ? errno : 0; #if DEBUG_THREADING LError Err(ErrCode); printf("%s:%i - sem_timedwait(%i) = %i (in %i, errno=%s)\n", _FL, Sem, r, GetCurrentThreadId(), Err.GetMsg().Get()); #endif if (ErrCode == ETIMEDOUT) return WaitTimeout; if (r) { // printf("%s:%i - sem_wait failed with %i.\n", _FL, errno); return WaitError; } } return WaitSignaled; #elif defined(POSIX) int e = pthread_mutex_lock(&Mutex); if (e) printf("%s:%i - pthread_mutex_lock failed %i\n", _FL, e); int result; if (Timeout < 0) { result = pthread_cond_wait(&Cond, &Mutex); } else { timespec to; TimeoutToTimespec(to, Timeout); result = pthread_cond_timedwait(&Cond, &Mutex, &to); } e = pthread_mutex_unlock(&Mutex); if (e) printf("%s:%i - pthread_mutex_unlock failed %i\n", _FL, e); if (result == ETIMEDOUT) return WaitTimeout; else if (result == 0) return WaitSignaled; else LastError = result; #elif defined(WIN32) DWORD Status = WaitForSingleObject(Event, Timeout < 0 ? INFINITE : Timeout); switch (Status) { case WAIT_OBJECT_0: return WaitSignaled; case WAIT_TIMEOUT: return WaitTimeout; default: LastError = GetLastError(); return WaitError; } #else #error "Impl me.") #endif return WaitError; } -uint32 LThreadEvent::GetError() +uint32_t LThreadEvent::GetError() { return LastError; } diff --git a/src/common/Skins/Gel/Gel.cpp b/src/common/Skins/Gel/Gel.cpp --- a/src/common/Skins/Gel/Gel.cpp +++ b/src/common/Skins/Gel/Gel.cpp @@ -1,983 +1,983 @@ #include #include #include "Lgi.h" #include "GSkinEngine.h" #include "GPath.h" #include "GButton.h" #include "GCombo.h" #include "GCheckBox.h" #include "GRadioGroup.h" #include "GDisplayString.h" #include "GCssTools.h" #include "LStringLayout.h" #ifdef WIN32 #define BTN_TEXT_OFFSET_Y -1 #else #define BTN_TEXT_OFFSET_Y 0 #endif #define GREY24 Rgb24(44, 44, 44) #define GREY32(a) Rgba32(44, 44, 44, a) #define CHECK_BORDER 2 #define CHECK_RADIUS 3 #define Btn_Value 0x1 #define Btn_Enabled 0x2 #define Btn_Max ((Btn_Value|Btn_Enabled)+1) #if defined(WIN32) && !defined(LGI_SDL) // Wine can't handle 32 bit DIB sections... :( #define OsDefaultCs System24BitColourSpace #else // 32bit is marginally faster to render to. #define OsDefaultCs System32BitColourSpace #endif #define CUSTOM_COLOURS 0 class GelSkin : public GSkinEngine { GApp *App; COLOUR c80; COLOUR c160; COLOUR c172; COLOUR c222; COLOUR c232; COLOUR c253; COLOUR c255; GMemDC *CheckBox[Btn_Max]; GMemDC *RadioBtn[Btn_Max]; COLOUR LgiLighten(COLOUR c32, int Amount) { System32BitPixel *p = (System32BitPixel*)&c32; p->r += ((255 - p->r) * Amount) >> 8; p->g += ((255 - p->g) * Amount) >> 8; p->b += ((255 - p->b) * Amount) >> 8; return Rgba32(p->r, p->g, p->b, p->a); } COLOUR LgiDarken(COLOUR c32, int Amount) { System32BitPixel *p = (System32BitPixel*)&c32; p->r = (p->r * Amount) >> 8; p->g = (p->g * Amount) >> 8; p->b = (p->b * Amount) >> 8; return Rgba32(p->r, p->g, p->b, p->a); } void FillPath(GPath *Path, GSurface *pDC, bool Down, bool Enabled = true) { if (pDC) { GRect r(0, 0, pDC->X()-1, pDC->Y()-1); #if 0 COLOUR Top = LgiLighten(Rgb24To32(LC_MED), 62 * 256 / 63); // Rgba32(253, 253, 253, 255); COLOUR Mid = LgiLighten(Rgb24To32(LC_MED), 49 * 256 / 63); // Rgba32(239, 239, 239, 255); COLOUR Mid2 = LgiLighten(Rgb24To32(LC_MED), 31 * 256 / 63); // Rgba32(222, 222, 222, 255); #endif COLOUR Top = c253; COLOUR Mid = c232; COLOUR Mid2 = c222; COLOUR Bot = c255; if (!Enabled) { Top = LgiDarken(Top, 230); Mid = LgiDarken(Mid, 230); Mid2 = LgiDarken(Mid2, 230); Bot = LgiDarken(Bot, 230); } // Draw background GPath e; e.Rectangle(r.x1, r.y1, r.x2+1, r.y2+1); GPointF c1(r.x1, r.y1); GPointF d1(r.x1, r.y2+1); if (Down) { GBlendStop s1[] = { {0.0, Rgba32(192, 192, 192, 255)}, {0.1, Top}, {0.6, Mid}, {0.601, Mid2}, {1.0, Bot}, }; GLinearBlendBrush b1(c1, d1, CountOf(s1), s1); e.Fill(pDC, b1); } else { GBlendStop s1[] = { {0.0, Top}, {0.5, Mid}, {0.501, Mid2}, {1.0, Bot}, }; GLinearBlendBrush b1(c1, d1, CountOf(s1), s1); e.Fill(pDC, b1); } pDC->Colour(GREY32(255), 32); pDC->Line(0, pDC->Y()-1, pDC->X()-1, pDC->Y()-1); pDC->Colour(Rgba32(0xc6, 0xc6, 0xc6, 255), 32); pDC->Line(0, pDC->Y()-2, pDC->X()-2, pDC->Y()-2); pDC->Line(pDC->X()-1, 0, pDC->X()-1, pDC->Y()-2); } } void DrawBtn(GSurface *pDC, GRect &r, GColour *Base, bool Down, bool Enabled, bool Default = false) { if (!pDC) return; #if LGI_SDL pDC->Colour(Base?*Base:GColour(192, 192, 192)); pDC->Rectangle(&r); pDC->Colour(GColour(96, 96, 96)); if (Down) { pDC->Line(r.x1, r.y1, r.x2, r.y1); pDC->Line(r.x1, r.y1, r.x1, r.y2); } else { pDC->Line(r.x1, r.y2, r.x2, r.y2); pDC->Line(r.x2, r.y1, r.x2, r.y2); } #else GRect Client = r; { // Edge GPath e; GRectF r(Client); // r.y2++; e.RoundRect(r, 6); COLOUR EdgeColour = Default ? Rgba32(40, 40, 40, 255) : Rgba32(114, 114, 114, 255); GSolidBrush b(EdgeColour); e.Fill(pDC, b); } { // Border GPath e; GRectF r(Client); // r.y2++; int Resize = Default ? 2 : 1; r.Size(Resize, Resize); if (Down) { r.x1 = r.x1 + 1; r.y1 = r.y1 + 1; } e.RoundRect(r, 6 - Resize); // Fill COLOUR Top = c253; COLOUR Mid = c232; COLOUR Mid2 = c222; COLOUR Bot = c255; if (Base) { Top = Base->c32(); Mid = Base->c32(); Mid2 = Base->c32(); Bot = Base->c32(); } else { if (!Enabled) { Top = LgiDarken(Top, 230); Mid = LgiDarken(Mid, 230); Mid2 = LgiDarken(Mid2, 230); Bot = LgiDarken(Bot, 230); } } GPointF c1(r.x1, r.y1); GPointF d1(r.x1, r.y2); if (Down) { GBlendStop s1[] = { {0.0, Rgba32(192, 192, 192, 255)}, {0.1, Top}, {0.6, Mid}, {0.601, Mid2}, {1.0, Bot}, }; GLinearBlendBrush b1(c1, d1, CountOf(s1), s1); e.Fill(pDC, b1); } else { GBlendStop s1[] = { {0.0, Top}, {0.5, Mid}, {0.501, Mid2}, {1.0, Bot}, }; GLinearBlendBrush b1(c1, d1, CountOf(s1), s1); e.Fill(pDC, b1); } double Round = (r.X()-13)/r.X(); if (Round < 0.6) Round = 0.6; int Sa = Down ? 128 : 50; GBlendStop s3[] = { {Round, GREY32(0)}, {1.0, GREY32(Sa)}, }; // Rounded corners GPointF c3(r.x1 + (r.X()/2), r.y1 + (r.Y()/2)); GPointF d3(r.x1, r.y1); GRadialBlendBrush b3(c3, d3, CountOf(s3), s3); e.Fill(pDC, b3); } #endif } GMemDC *DrawCtrl(GViewI *Ctrl, int Flags, bool Round) { GMemDC *Mem = new GMemDC; if (Mem && Mem->Create(14, 14, OsDefaultCs)) { // blank out background GCss::ColorDef Back; if (Ctrl->GetCss()) Back = Ctrl->GetCss()->BackgroundColor(); if (Back.Type == GCss::ColorRgb) Mem->Colour(Back.Rgb32, 32); else Mem->Colour(LC_MED, 24); Mem->Rectangle(); GRectF Box(0, 0, Mem->X(), Mem->Y()); double Radius = Box.X()/2; GPointF Center(Box.X()/2, Box.Y()/2); int Grey = R24(LC_MED); bool Enabled = (Flags & Btn_Enabled) != 0; int BorderCol = Enabled ? 80 : 160; if (Enabled) { // draw sunken border GRectF r = Box; GPath p; if (Round) p.Circle(Center, Radius); else p.RoundRect(r, CHECK_RADIUS + CHECK_BORDER); // gradient from 169,169,169 at the top through to 225,225,225 int Dark = MAX(0, Grey - 40); int Light = MIN(255, Grey + 40); GPointF a(0, 0), b(0, 15); GBlendStop s[] = { {0, c172}, {1, c253} }; GLinearBlendBrush c(a, b, 2, s); p.Fill(Mem, c); } if (Enabled) { // draw button center GRectF r = Box; r.Size(CHECK_BORDER+1, CHECK_BORDER+1); GPath p; if (Round) p.Circle(Center, r.X()/2); else p.RoundRect(r, CHECK_RADIUS-1); if (Enabled) { GPointF a(0, r.y1), b(0, r.y2); GBlendStop s[] = { {1.0/15.0, c255}, {1.0/14.0, c253}, {1, c232} }; GLinearBlendBrush c(a, b, CountOf(s), s); p.Fill(Mem, c); } else { GSolidBrush c(Rgb24To32(LC_MED)); p.Fill(Mem, c); } } else { // draw button hightlight, a white outline shifted down 1 pixel GRectF r = Box; r.Size(CHECK_BORDER, CHECK_BORDER); r.Offset(0, 1); GPointF Cntr = Center; Cntr.y = Cntr.y + 1; GPath p; if (Round) p.Circle(Cntr, r.X()/2); else p.RoundRect(r, CHECK_RADIUS); r.Size(1, 1); if (Round) p.Circle(Cntr, r.X()/2); else p.RoundRect(r, CHECK_RADIUS-1); GSolidBrush c(Rgb24To32(LC_LIGHT)); p.Fill(Mem, c); } { // draw button outline GRectF r = Box; r.Size(CHECK_BORDER, CHECK_BORDER); GPath p; if (Round) p.Circle(Center, r.X()/2); else p.RoundRect(r, CHECK_RADIUS); r.Size(1, 1); if (Round) p.Circle(Center, r.X()/2); else p.RoundRect(r, CHECK_RADIUS-1); GSolidBrush c(Enabled ? c80 : c160); p.Fill(Mem, c); } if (Flags & Btn_Value) { // draw the check mark GRectF r = Box; r.Size(CHECK_BORDER+2, CHECK_BORDER+2); double Cx = r.x1 + (r.X() / 2); double Cy = r.y1 + (r.Y() / 2); double A = r.X() / 6; double B = (r.X() / 2) - A; GPath p; if (Round) { p.Circle(Center, r.X()/3); } else { p.MoveTo(r.x1, r.y1); p.LineTo(r.x1 + A, r.y1); p.LineTo(Cx, r.y1 + B); p.LineTo(r.x2 - A, r.y1); p.LineTo(r.x2, r.y1); p.LineTo(r.x2, r.y1 + A); p.LineTo(r.x2 - B, Cy); p.LineTo(r.x2, r.y2 - A); p.LineTo(r.x2, r.y2); p.LineTo(r.x2 - A, r.y2); p.LineTo(Cx, r.y2 - B); p.LineTo(r.x1 + A, r.y2); p.LineTo(r.x1, r.y2); p.LineTo(r.x1, r.y2 - A); p.LineTo(r.x1 + B, Cy); p.LineTo(r.x1, r.y1 + A); p.LineTo(r.x1, r.y1); } GSolidBrush c(Enabled ? c80 : c160); p.Fill(Mem, c); } } return Mem; } void DrawText(GSkinState *State, int x, int y, GRect &rcFill, bool Enabled, GView *Ctrl, GCssTools &Tools) { GCss::ColorDef CssFore, CssBack; GColour Fore = Tools.GetFore(), Back = Tools.GetBack(), Light, Low; if (!Enabled) { Light.Set(LC_LIGHT, 24); Low.Set(LC_LOW, 24); } GRegion Rgn; Rgn = rcFill; GArray *Text = State->AllText(); GSurface *pDC = State->pScreen; if (Text && Text->Length() > 0 && rcFill.X() > 3) { for (unsigned i=0; iLength(); i++) { LLayoutString *t = dynamic_cast((*Text)[i]); if (!t) break; GRect c; c.ZOff(t->X() - 1, t->Y() - 1); c.Offset(x + (t->Fx >> GDisplayString::FShift), y + t->y); Rgn.Subtract(&c); GFont *f = t->GetFont(); if (Enabled) { f->Colour(Fore, Back); if (Ctrl->Focus()) { pDC->Colour(LC_MIDGREY, 24); pDC->Box(&c); c.Size(1, 1); pDC->Colour(Back); pDC->Rectangle(&c); c.Size(-1, -1); f->Transparent(true); t->Draw(pDC, c.x1, c.y1, &c); } else { f->Transparent(!Back.IsValid()); t->Draw(pDC, c.x1, c.y1, &c); } } else { f->Transparent(!Back.IsValid()); f->Colour(Light, Back); t->Draw(pDC, c.x1 + 1, c.y1 + 1, &c); f->Transparent(true); f->Colour(Low, Back); t->Draw(pDC, c.x1, c.y1, &c); } } } if (Back.IsValid()) { pDC->Colour(Back); for (GRect *rc = Rgn.First(); rc; rc = Rgn.Next()) pDC->Rectangle(rc); } } public: GelSkin(GApp *a) { // printf("Skin @ %i bits\n", GdcD->GetBits()); App = a; ZeroObj(CheckBox); ZeroObj(RadioBtn); COLOUR Med = Rgb24To32(LC_MED); c80 = LgiDarken(Med, 80 * 256 / 192); c160 = LgiDarken(Med, 160 * 256 / 192); c172 = LgiDarken(Med, 172 * 256 / 192); c222 = Med; c232 = LgiLighten(Med, 33 * 256 / 63); c253 = LgiLighten(Med, 60 * 256 / 63); c255 = Rgba32(255, 255, 255, 255); } ~GelSkin() { int i; for (i=0; iX(), Ctrl->Y(), OsDefaultCs)) { // Font if (Ctrl->GetFont() == SysFont) { Ctrl->SetFont(SysBold); } // Background GCssTools Tools(Ctrl->GetCss(), Ctrl->GetFont()); GColour DefaultBack; GColour &Fore = Tools.GetFore(), &Back = Tools.GetBack(&DefaultBack); GColour NoPaint(LC_MED, 24); if (Ctrl->GetCss()) { GCss::ColorDef np = Ctrl->GetCss()->NoPaintColor(); if (np.Type == GCss::ColorRgb) NoPaint.Set(np.Rgb32, 32); else NoPaint.Empty(); } if (NoPaint.IsValid()) Mem.Colour(NoPaint); else Mem.Colour(0, 32); Mem.Rectangle(); DrawBtn(&Mem, Ctrl->GetClient(), Back.IsValid() ? &Back : NULL, Ctrl->Value() != 0, Ctrl->Enabled(), Ctrl->Default()); GSurface *Out = &Mem; GArray *Txt = State->AllText(); int ContentX = 0; int SpacingPx = 4; if (State->Image) ContentX += State->Image->X(); int MaxTxt = 0; if (Txt) { for (unsigned i=0; iLength(); i++) { MaxTxt = MAX(MaxTxt, (*Txt)[i]->X()); } ContentX += MaxTxt; } if (State->Image && Txt && Txt->Length() > 0) ContentX += SpacingPx; int CurX = (Ctrl->X() - ContentX) >> 1; int Off = Ctrl->Value() ? 1 : 0; if (State->Image) { int CurY = (Ctrl->Y() - State->Image->Y()) >> 1; int Op = Out->Op(GDC_ALPHA); Out->Blt(CurX+Off, CurY+Off, State->Image); Out->Op(Op); CurX += State->Image->X() + SpacingPx; } if (Txt && Txt->Length() > 0) { GDisplayString *First = (*Txt)[0]; int sx = MaxTxt, sy = (int) Txt->Length() * First->Y(); int ty = (Ctrl->Y()-sy) >> 1; GFont *f = First->GetFont(); f->Transparent(true); for (unsigned i=0; iLength(); i++) { GDisplayString *Text = (*Txt)[i]; if (Ctrl->Enabled()) { f->Colour(Fore, Back); Text->Draw(Out, CurX+Off, ty+Off+BTN_TEXT_OFFSET_Y); } else { f->Colour(GColour(LC_LIGHT, 24), Back); Text->Draw(Out, CurX+Off+1, ty+Off+1+BTN_TEXT_OFFSET_Y); f->Colour(GColour(LC_LOW, 24), Back); Text->Draw(Out, CurX+Off, ty+Off+BTN_TEXT_OFFSET_Y); } ty += Text->Y(); } if (Ctrl->Focus()) { GRect b(CurX-2, ty, CurX + sx + 1, ty + sy - 2); b.Offset(Off, Off); Out->Colour(Rgb24(180, 180, 180), 24); Out->Box(&b); } } int Op = State->pScreen->Op(GDC_ALPHA); State->pScreen->Blt(0, 0, &Mem); State->pScreen->Op(Op); } else { State->pScreen->Colour(Rgb24(255, 0, 255), 24); State->pScreen->Rectangle(); } } void OnPaint_ListColumn(ProcColumnPaint Callback, void *UserData, GSkinState *State) { // Setup memory context GRect r = State->Rect; GMemDC Mem(r.X(), r.Y(), OsDefaultCs); if (Mem[0]) { r.Offset(-r.x1, -r.y1); GPath e; e.Rectangle(r.x1, r.y1, r.x2, r.y2); static bool LastEnabled = true; FillPath(&e, &Mem, State ? State->Value != 0 : false, State ? LastEnabled = State->Enabled : LastEnabled); if (State && State->Value) { Mem.Colour(Rgb24(0xc0, 0xc0, 0xc0), 24); Mem.Line(r.x1, r.y1, r.x1, r.y2-1); Mem.Colour(Rgb24(0xe0, 0xe0, 0xe0), 24); Mem.Line(r.x1+1, r.y1+1, r.x1+1, r.y2-2); } r.Size(2, 2); if (Callback) { Mem.Op(GDC_ALPHA); Callback(UserData, &Mem, r, false); } State->pScreen->Blt(State->Rect.x1, State->Rect.y1, &Mem); } } void OnPaint_GCombo(GCombo *Ctrl, GSkinState *State) { GMemDC Mem; if (Mem.Create(Ctrl->X(), Ctrl->Y(), OsDefaultCs)) { // Font if (Ctrl->GetFont() == SysFont) Ctrl->SetFont(SysBold); // Back GColour TextDefault(GREY24, 24); GCssTools Tools(Ctrl->GetCss(), Ctrl->GetFont()); GColour &Fore = Tools.GetFore(&TextDefault), &Back = Tools.GetBack(); if (Back.IsValid()) { Mem.Colour(Back); Mem.Rectangle(); } DrawBtn(&Mem, Ctrl->GetClient(), NULL, false, State->Enabled); int n = 22; GColour DkGrey(LC_DKGREY, 24); if (Ctrl->X() > 32) { GDisplayString *Text = State->FirstText(); if (Text) { int sx = Text->X(), sy = Text->Y(); int tx = GCombo::Pad.x1; int ty = (Ctrl->Y()-sy) >> 1; int Off = 0; GRect c = Ctrl->GetClient(); c.x1 += 8; c.x2 -= n + 3; int Cx = Ctrl->X(); int PadX = GCombo::Pad.x1 + GCombo::Pad.x2; if (Text->X() > PadX) { // Make the text fit Text->TruncateWithDots(Cx - PadX); } GFont *f = Text->GetFont(); f->Transparent(true); if (Ctrl->Enabled()) { f->Colour(Fore, Back); Text->Draw(&Mem, tx+Off, ty+Off+BTN_TEXT_OFFSET_Y, &c); } else { f->Colour(LC_LIGHT, LC_MED); Text->Draw(&Mem, tx+Off+1, ty+Off+1+BTN_TEXT_OFFSET_Y, &c); f->Colour(LC_LOW, LC_MED); Text->Draw(&Mem, tx+Off, ty+Off+BTN_TEXT_OFFSET_Y, &c); } if (Ctrl->Focus() && c.X() > 4) { GRect b(tx-2, ty, tx + sx + 1, ty + sy - 2); b.Offset(Off, Off); c.Size(-2, 0); b.Bound(&c); Mem.Colour(Rgb24(180, 180, 180), 24); Mem.Box(&b); } } // Draw separator Mem.Colour(Rgba32(180, 180, 180, 255), 32); Mem.Line(Mem.X()-n, 1, Mem.X()-n, Mem.Y()-2); } Mem.Colour(State->Enabled ? Fore : DkGrey); int Bx = Mem.X() < 26 ? Mem.X()/2 : Mem.X()-13, By = (Mem.Y() + 4) >> 1; for (int i=0; i<5; i++) { Mem.Line(Bx-i, By-i, Bx+i, By-i); } State->pScreen->Blt(0, 0, &Mem); } else { State->pScreen->Colour(Rgb24(255, 0, 255), 24); State->pScreen->Rectangle(); } } void OnPaint_GCheckBox(GCheckBox *Ctrl, GSkinState *State) { int Flags = (Ctrl->Value() ? Btn_Value : 0) | (Ctrl->Enabled() ? Btn_Enabled : 0); // Create the bitmaps in cache if not already there GCssTools Tools(Ctrl); GColour &Back = Tools.GetBack(); GMemDC *Temp = 0; GMemDC *&Mem = Back.IsValid() ? Temp : CheckBox[Flags]; if (!Mem) { Mem = DrawCtrl(Ctrl, Flags, false); } // Output to screen if (Mem) { // Draw icon int FontY = Ctrl->GetFont()->GetHeight(); GRect Box(0, 0, Mem->X()-1, Mem->Y()-1); if (FontY > Mem->Y()) Box.Offset(0, FontY - Mem->Y()); State->pScreen->Blt(Box.x1, Box.y1, Mem); GRect Box1(Box.x1, 0, Box.x2, Box.y1 - 1); GRect Box2(Box.x1, Box.y2 + 1, Box.x2, Ctrl->Y()-1); if (Back.IsValid()) { State->pScreen->Colour(Back); if (Box.y1 > 0) State->pScreen->Rectangle(&Box1); if (Box.y2 < Ctrl->Y() - 1) State->pScreen->Rectangle(&Box2); } else { State->pScreen->Colour(LC_MED, 24); if (Box.y1 > 0) State->pScreen->Rectangle(&Box1); if (Box.y2 < Ctrl->Y() - 1) State->pScreen->Rectangle(&Box2); } // Draw text GRect t(Mem->X(), 0, Ctrl->X()-1, Ctrl->Y()-1); if (t.Valid()) { DrawText(State, Mem->X() + 4, 0, t, (Flags & Btn_Enabled) != 0, Ctrl, Tools); } } else { State->pScreen->Colour(Rgb24(255, 0, 255), 24); State->pScreen->Rectangle(); } DeleteObj(Temp); } void OnPaint_GRadioButton(GRadioButton *Ctrl, GSkinState *State) { int Flags = (Ctrl->Value() ? Btn_Value : 0) | (Ctrl->Enabled() ? Btn_Enabled : 0); // Create the bitmaps in cache if not already there GMemDC *&Mem = RadioBtn[Flags]; if (!Mem) { Mem = DrawCtrl(Ctrl, Flags, true); } // Output to screen if (Mem) { // Draw icon GRect ico; GCssTools Tools(Ctrl); GColour &Fore = Tools.GetFore(), &Back = Tools.GetBack(); ico.ZOff(Mem->X()-1, Mem->Y()-1); if (ico.Y() < Ctrl->Y()) ico.Offset(0, (Ctrl->Y() - ico.Y()) >> 1); State->pScreen->Blt(ico.x1, ico.y1, Mem); if (Back.IsValid()) { State->pScreen->Colour(Back); if (ico.y1 > 0) State->pScreen->Rectangle(0, 0, ico.x2, ico.y1-1); if (ico.y2 < Ctrl->Y()) State->pScreen->Rectangle(0, ico.y2+1, ico.x2, Ctrl->Y()-1); } // Draw text GRect t(Mem->X(), 0, Ctrl->X()-1, Ctrl->Y()-1); if (t.Valid()) { int y = 0; if (State->TextObjects()) { GDisplayString *ds = State->FirstText(); if (ds && t.Y() > ds->Y()) { y = (t.Y() - ds->Y()) >> 1; } } DrawText(State, Mem->X() + 4, y, t, (Flags & Btn_Enabled) != 0, Ctrl, Tools); } } else { State->pScreen->Colour(Rgb24(255, 0, 255), 24); State->pScreen->Rectangle(); } } GFont *GetDefaultFont(char *Class) { if (Class && stricmp(Class, Res_Button) == 0) { return SysBold; } return SysFont; } }; GSkinEngine * CreateSkinEngine(class GApp *App) { return new GelSkin(App); } diff --git a/src/common/Text/GDocView.cpp b/src/common/Text/GDocView.cpp --- a/src/common/Text/GDocView.cpp +++ b/src/common/Text/GDocView.cpp @@ -1,226 +1,226 @@ #include #include "Lgi.h" #include "GDocView.h" #define SubtractPtr(a, b) ((a)-(b)) const char *GDocView::WhiteSpace = " \t\r\n"; const char *GDocView::Delimiters = "!@#$%^&*()'\":;,.<>/?[]{}-=+\\|`~"; const char *GDocView::UrlDelim = "!~/:%+-?@&$#._=,;*()|"; GDocumentEnv::GDocumentEnv(GDocView *v) { if (v) { Viewers.Add(v); } } GDocumentEnv::~GDocumentEnv() { - for (uint32 i=0; iEnvironment = 0; } } int GDocumentEnv::NextUid() { if (!Lock(_FL)) return -1; int Uid = 0; for (auto v : Viewers) Uid = MAX(Uid, v->GetDocumentUid() + 1); Unlock(); return Uid; } void GDocumentEnv::OnDone(GAutoPtr j) { LoadJob *ld = dynamic_cast(j.Get()); if (ld) { if (Lock(_FL)) { GDocView *View = NULL; for (unsigned i=0; iGetDocumentUid(); if (Uid == ld->UserUid) { View = Viewers[i]; break; } } if (View) { View->OnContent(ld); j.Release(); } Unlock(); } } else LgiAssert(!"RTTI failed."); } ////////////////////////////////////////////////////////////////////////////////// char16 *ConvertToCrLf(char16 *Text) { if (Text) { // add '\r's int Lfs = 0; int Len = 0; char16 *s=Text; for (; *s; s++) { if (*s == '\n') Lfs++; Len++; } char16 *Temp = new char16[Len+Lfs+1]; if (Temp) { char16 *d=Temp; s = Text; for (; *s; s++) { if (*s == '\n') { *d++ = 0x0d; *d++ = 0x0a; } else if (*s == '\r') { // ignore } else { *d++ = *s; } } *d++ = 0; DeleteObj(Text); return Temp; } } return Text; } ////////////////////////////////////////////////////////////////////////////////// #include "INet.h" GDocumentEnv::LoadType GDefaultDocumentEnv::GetContent(LoadJob *&j) { if (!j || !ValidStr(j->Uri)) return LoadError; char p[MAX_PATH]; char *FullPath = NULL; char *FileName = NULL; GUri u(j->Uri); if (u.Protocol && !_stricmp(u.Protocol, "file")) FileName = u.Path; else FileName = j->Uri; if (FileName) { LGetSystemPath(LSP_APP_INSTALL, p, sizeof(p)); LgiMakePath(p, sizeof(p), p, FileName); if (FileExists(p)) { FullPath = p; } else { GAutoString f(LgiFindFile(FileName)); if (f) strcpy_s(FullPath = p, sizeof(p), f); } } if (FileExists(FullPath)) { char Mt[256] = ""; LGetFileMimeType(FullPath, Mt, sizeof(Mt)); if (stristr(Mt, "image/")) { j->pDC.Reset(GdcD->Load(p)); return LoadImmediate; } j->Filename = FullPath; return LoadImmediate; } return LoadError; } bool GDefaultDocumentEnv::OnNavigate(GDocView *Parent, const char *Uri) { if (Uri) { if ( _strnicmp(Uri, "mailto:", 7) == 0 || ( strchr(Uri, '@') != 0 && strchr(Uri, '/') == 0 ) ) { // email GArray Apps; if (LgiGetAppsForMimeType("application/email", Apps)) { GAppInfo *First = Apps[0]; GStringPipe a; char *Arg = First->Params ? strstr(First->Params, "%1") : 0; if (Arg) { // change '%1' into the email address in question a.Write(First->Params, (int) (Arg-First->Params)); a.Print("%s%s", Uri, Arg + 2); } else { // no argument place holder... just pass as a normal arg a.Print(" %s", Uri); } GAutoString Exe(TrimStr(First->Path, "\"\'")); GAutoString Args(a.NewStr()); GAutoString ErrorMsg; if (LgiExecute(Exe, Args, ".", &ErrorMsg)) return true; LgiMsg(Parent, "Failed to open '%s':\n%s", LgiApp->GBase::Name(), MB_OK, Exe.Get(), ErrorMsg.Get()); } else { LgiMsg(Parent, "Couldn't get app to handle email.", LgiApp->GBase::Name(), MB_OK); } } else { // webpage GAutoString ErrorMsg; if (LgiExecute(Uri, NULL, NULL, &ErrorMsg)) return true; LgiMsg(Parent, "Failed to open '%s':\n%s", LgiApp->GBase::Name(), MB_OK, Uri, ErrorMsg.Get()); } } return false; } diff --git a/src/common/Text/GString.cpp b/src/common/Text/GString.cpp --- a/src/common/Text/GString.cpp +++ b/src/common/Text/GString.cpp @@ -1,1001 +1,1001 @@ #include #include #include #include "GMem.h" #include "LgiOsDefs.h" #include "LgiClass.h" #include "GString.h" #include "GContainers.h" #include "LgiCommon.h" char WhiteSpace[] = " \t\r\n"; #if !defined(_MSC_VER) char *strncpy_s(char *dest, size_t dest_size, const char *src, size_t src_size) { if (dest && src) { char *end = dest + dest_size - 1; while (dest < end && *src && src_size-- > 0) { *dest++ = *src++; } *dest++ = 0; } return dest; } char *strcpy_s(char *dest, size_t dest_size, const char *src) { if (!dest || !src) return NULL; char *Start = dest; // Copy the string while (*src && dest_size > 1) { *dest++ = *src++; dest_size--; } // NULL terminate if (dest_size > 0) { *dest++ = 0; dest_size--; } // Assert if we ran out of buffer. if (*src != 0) LgiAssert(!"Buffer too small"); return Start; } char *strcat_s(char *dest, size_t dest_size, const char *src) { if (!dest || !src) return NULL; char *Start = dest; // Scan over the existing string while (*dest && dest_size > 0) { dest++; dest_size--; } // Write as much as we can while (*src && dest_size > 1) { *dest++ = *src++; dest_size--; } // NULL terminate... if (dest_size > 0) { *dest++ = NULL; dest_size--; } // Assert if we ran out of buffer. if (*src != 0) LgiAssert(!"Buffer too small"); return Start; } #endif // 8 Bit char *strnchr(const char *s, char c, NativeInt Len) { if (s && Len >= 0) { for (ssize_t i=0; i= SLen) { int i; for (i=0; i= SLen) { int i; for (i=0; a[i] && i 0) { for (Cmp = 0; i-- && Cmp == 0; ) { Cmp += tolower(*a) - tolower(*b); if (!*a || !*b) break; a++; b++; } } return Cmp; } char *stristr(const char *a, const char *b) { if (a && b) { while (a && *a) { if (tolower(*a) == tolower(*b)) { int i; for (i=0; a[i] && tolower(a[i]) == tolower(b[i]); i++) ; if (b[i] == 0) return (char*)a; } a++; } } return NULL; } int strcmp(char *a, char *b) { int c = -1; if (a && b) { c = 0; while (!c) { c = *a - *b; if (!*a || !*b) break; a++; b++; } } return c; } #ifndef WIN32 int stricmp(const char *a, const char *b) { int c = -1; if (a && b) { c = 0; while (!c) { c = tolower(*a) - tolower(*b); if (!*a || !*b) break; a++; b++; } } return c; } char *strupr(char *a) { for (char *s = a; s && *s; s++) { *s = toupper(*s); } return a; } char *strlwr(char *a) { for (char *s = a; s && *s; s++) { *s = tolower(*s); } return a; } #endif char *TrimStr(const char *s, const char *Delim) { if (!s) return NULL; const char *Start = s; while (*Start && strchr(Delim, *Start)) { Start++; } size_t StartLen = strlen(Start); if (StartLen == 0) return NULL; const char *End = Start + strlen(Start) - 1; while (*End && End > Start && strchr(Delim, *End)) { End--; } if (*Start == 0) return NULL; size_t Len = (End - Start) + 1; char *n = new char[Len+1]; if (!n) return NULL; memcpy(n, Start, Len); n[Len] = 0; return n; } bool ValidStr(const char *s) { if (s) { while (*s) { if (*s != ' ' && *s != '\t' && *s != '\r' && *s != '\n' && ((uchar)*s) != 0xa0) { return true; } s++; } } return false; } char *NewStr(const char *s, ptrdiff_t Len) { if (s) { if (Len < 0) Len = strlen(s); NativeInt Bytes = Len + 1; char *Ret = new char[LGI_ALLOC_ALIGN(Bytes)]; if (Ret) { if (Len > 0) memcpy(Ret, s, Len); Ret[Len] = 0; return Ret; } } return NULL; } #ifdef BEOS #define toupper(c) ( ((c)>='a' && (c)<='z') ? (c)-'a'+'A' : (c) ) #define tolower(c) ( ((c)>='A' && (c)<='Z') ? (c)-'A'+'a' : (c) ) #endif bool MatchStr(const char *Template, const char *Data) { if (!Template) { return false; } if (_stricmp(Template, (char*)"*") == 0) { // matches anything return true; } if (!Data) { return false; } while (*Template && *Data) { if (*Template == '*') { Template++; if (*Template) { const char *EndA; for (EndA = Template; *EndA && *EndA!='?' && *EndA!='*'; EndA++); size_t SegLen = EndA - Template; char *Seg = NewStr(Template, SegLen); if (!Seg) return false; // find end of non match while (*Data) { if (_strnicmp(Data, Seg, SegLen) == 0) { break; } Data++; } DeleteArray(Seg); if (*Data) { Template += SegLen; Data += SegLen; } else { // can't find any matching part in Data return false; } } else { // '*' matches everything return true; } } else if (*Template == '?') { Template++; Data++; } else { if (tolower(*Template) != tolower(*Data)) { return false; } Template++; Data++; } } return ((*Template == 0) || (_stricmp(Template, (char*)"*") == 0)) && (*Data == 0); } ////////////////////////////////////////////////////////////////////////// // UTF-16 #define CompareDefault (a && b ? *a - *b : -1) char16 *StrchrW(const char16 *s, char16 c) { if (s) { while (*s) { if (*s == c) return (char16*) s; s++; } } return 0; } char16 *StrnchrW(char16 *s, char16 c, int Len) { if (s) { while (*s && Len > 0) { if (*s == c) return s; s++; Len--; } } return 0; } char16 *StrrchrW(char16 *s, char16 c) { char16 *Last = 0; while (s && *s) { if (*s == c) Last = s; s++; } return Last; } char16 *StrstrW(char16 *a, const char16 *b) { if (a && b) { size_t Len = StrlenW(b); for (char16 *s=a; *s; s++) { if (*s == *b) { // check match if (StrncmpW(s+1, b+1, Len-1) == 0) return s; } } } return 0; } char16 *StristrW(char16 *a, const char16 *b) { if (a && b) { size_t Len = StrlenW(b); for (char16 *s=a; *s; s++) { if (tolower(*s) == tolower(*b)) { // check match if (StrnicmpW(s+1, b+1, Len-1) == 0) return s; } } } return 0; } char16 *StrnstrW(char16 *a, const char16 *b, ssize_t n) { if (a && b) { ssize_t Len = StrlenW(b); for (char16 *s=a; n >= Len && *s; s++, n--) { if (*s == *b) { // check match if (StrncmpW(s+1, b+1, Len-1) == 0) return s; } } } return 0; } char16 *StrnistrW(char16 *a, const char16 *b, ssize_t n) { if (a && b) { ssize_t Len = StrlenW(b); for (char16 *s=a; n >= Len && *s; s++, n--) { if (*s == *b) { // check match if (StrnicmpW(s+1, b+1, Len-1) == 0) return s; } } } return 0; } int StrcmpW(const char16 *a, const char16 *b) { if (a && b) { while (true) { if (!*a || !*b || *a != *b) return *a - *b; a++; b++; } return 0; } return -1; } int StricmpW(const char16 *a, const char16 *b) { if (a && b) { while (true) { char16 A = tolower(*a); char16 B = tolower(*b); if (!A || !B || A != B) return A - B; a++; b++; } return 0; } return -1; } int StrncmpW(const char16 *a, const char16 *b, ssize_t n) { if (a && b) { while (n > 0) { if (!*a || !*b || *a != *b) return *a - *b; a++; b++; n--; } return 0; } return -1; } int StrnicmpW(const char16 *a, const char16 *b, ssize_t n) { if (a && b) { while (n > 0) { char16 A = tolower(*a); char16 B = tolower(*b); if (!A || !B || A != B) return A - B; a++; b++; n--; } return 0; } return -1; } char16 *StrcpyW(char16 *a, const char16 *b) { if (a && b) { do { *a++ = *b++; } while (*b); *a++ = 0; } return a; } char16 *StrncpyW(char16 *a, const char16 *b, ssize_t n) { if (a && b && n > 0) { while (*b) { if (--n <= 0) { break; } *a++ = *b++; } *a++ = 0; // always NULL terminate } return a; } void StrcatW(char16 *a, const char16 *b) { if (a && b) { // Seek to end of string while (*a) { a++; } // Append 'b' while (*b) { *a++ = *b++; } *a++ = 0; } } ssize_t StrlenW(const char16 *a) { if (!a) return 0; ssize_t i = 0; while (*a++) { i++; } return i; } int AtoiW(const char16 *a) { int i = 0; while (a && *a >= '0' && *a <= '9') { i *= 10; i += *a - '0'; a++; } return i; } int HtoiW(const char16 *a) { int i = 0; if (a) { while (*a) { if (*a >= '0' && *a <= '9') { i <<= 4; i |= *a - '0'; } else if (*a >= 'a' && *a <= 'f') { i <<= 4; i |= *a - 'a' + 10; } else if (*a >= 'A' && *a <= 'F') { i <<= 4; i |= *a - 'A' + 10; } else break; a++; } } return i; } char16 *TrimStrW(const char16 *s, const char16 *Delim) { if (!Delim) { static char16 Def[] = {' ', '\r', '\n', '\t', 0}; Delim = Def; } if (s) { // Leading delim while (StrchrW(Delim, *s)) { s++; } // Trailing delim size_t i = StrlenW(s); while (i > 0 && StrchrW(Delim, s[i-1])) { i--; } return NewStrW(s, i); } return 0; } bool MatchStrW(const char16 *a, const char16 *b) { LgiAssert(0); return 0; } char16 *NewStrW(const char16 *Str, ssize_t l) { char16 *s = 0; if (Str) { if (l < 0) l = StrlenW(Str); s = new char16[l+1]; if (s) { memcpy(s, Str, l * sizeof(char16)); s[l] = 0; } } return s; } bool ValidStrW(const char16 *s) { if (s) { for (const char16 *c=s; *c; c++) { if (*c != ' ' && *c != '\t' && *c != 0xfeff && *c != 0xfffe) return true; } } return false; } char *LgiDecodeUri(const char *uri, int len) { GStringPipe p; if (uri) { const char *end = len >= 0 ? uri + len : 0; for (const char *s=uri; s && *s; ) { int Len; const char *e = s; for (Len = 0; *e && *e != '%' && (!end || e < end); e++) Len++; p.Push(s, Len); if ((!end || e < end) && *e) { e++; if (e[0] && e[1]) { char h[3] = { e[0], e[1], 0 }; char c = htoi(h); p.Push(&c, 1); e += 2; s = e; } else break; } else { break; } } } return p.NewStr(); } char *LgiEncodeUri(const char *uri, int len) { GStringPipe p; if (uri) { const char *end = len >= 0 ? uri + len : 0; for (const char *s=uri; s && *s; ) { int Len; const char *e = s; for ( Len = 0; *e && (!end || e < end) && *e > ' ' && (uchar)*e < 0x7f && strchr("$&+,/:;=?@\'\"<>#%{}|\\^~[]`", *e) == 0; e++ ) { Len++; } p.Push(s, Len); if ((!end || e < end) && *e) { char h[4]; sprintf_s(h, sizeof(h), "%%%2.2X", (uchar)*e); p.Push(h, 3); s = ++e; } else { break; } } } return p.NewStr(); } GString LUrlEncode(const char *s, const char *delim) { if (!s || !*s) return GString(); char buf[256]; int ch = 0; GString out; while (*s) { if (*s == '%' || strchr(delim, *s)) - ch += sprintf_s(buf+ch, sizeof(buf)-ch, "%%%02.2x", (uint8)*s); + ch += sprintf_s(buf+ch, sizeof(buf)-ch, "%%%02.2x", (uint8_t)*s); else buf[ch++] = *s; s++; if (ch > sizeof(buf) - 6) { buf[ch] = 0; out += buf; ch = 0; } } buf[ch] = 0; if (ch > 0) out += buf; return out; } GString LUrlDecode(const char *s) { if (!s || !*s) return GString(); char buf[256]; int ch = 0; GString out; while (*s) { if (*s == '%') { s++; if (!s[0] || !s[1]) break; char h[3] = {s[0], s[1], 0}; buf[ch++] = htoi(h); s++; } else buf[ch++] = *s; s++; if (ch > sizeof(buf) - 6) { buf[ch] = 0; out += buf; ch = 0; } } buf[ch] = 0; if (ch > 0) out += buf; return out; } diff --git a/src/common/Text/GTextView3.cpp b/src/common/Text/GTextView3.cpp --- a/src/common/Text/GTextView3.cpp +++ b/src/common/Text/GTextView3.cpp @@ -1,5327 +1,5327 @@ #include #include #include #include "Lgi.h" #include "GTextView3.h" #include "GInput.h" #include "GScrollBar.h" #ifdef WIN32 #include #endif #include "GClipBoard.h" #include "GDisplayString.h" #include "GViewPriv.h" #include "GCssTools.h" #include "LgiRes.h" #include "Mail.h" #ifdef _DEBUG #define FEATURE_HILIGHT_ALL_MATCHES 1 #else #define FEATURE_HILIGHT_ALL_MATCHES 0 #endif #define DefaultCharset "utf-8" #define SubtractPtr(a, b) ((a) - (b)) #define GDCF_UTF8 -1 #define LUIS_DEBUG 0 #define POUR_DEBUG 0 #define PROFILE_POUR 0 #define PROFILE_PAINT 0 #define DRAW_LINE_BOXES 0 #define WRAP_POUR_TIMEOUT 90 // ms #define PULSE_TIMEOUT 250 // ms #define CURSOR_BLINK 1000 // ms #define ALLOC_BLOCK 64 #define IDC_VS 1000 #ifndef IDM_OPEN #define IDM_OPEN 1 #endif #ifndef IDM_NEW #define IDM_NEW 2 #endif #ifndef IDM_COPY #define IDM_COPY 3 #endif #ifndef IDM_CUT #define IDM_CUT 4 #endif #ifndef IDM_PASTE #define IDM_PASTE 5 #endif #define IDM_COPY_URL 6 #define IDM_AUTO_INDENT 7 #define IDM_UTF8 8 #define IDM_PASTE_NO_CONVERT 9 #ifndef IDM_UNDO #define IDM_UNDO 10 #endif #ifndef IDM_REDO #define IDM_REDO 11 #endif #define IDM_FIXED 12 #define IDM_SHOW_WHITE 13 #define IDM_HARD_TABS 14 #define IDM_INDENT_SIZE 15 #define IDM_TAB_SIZE 16 #define IDM_DUMP 17 #define IDM_RTL 18 #define PAINT_BORDER Back #if DRAW_LINE_BOXES #define PAINT_AFTER_LINE GColour(240, 240, 240) #else #define PAINT_AFTER_LINE Back #endif #define CODEPAGE_BASE 100 #define CONVERT_CODEPAGE_BASE 200 #if !defined(WIN32) && !defined(toupper) #define toupper(c) (((c)>='a'&&(c)<='z') ? (c)-'a'+'A' : (c)) #endif #define THREAD_CHECK() \ if (!InThread()) \ { \ LgiTrace("%s:%i - %s called out of thread.\n", _FL, __FUNCTION__); \ return false; \ } static char SelectWordDelim[] = " \t\n.,()[]<>=?/\\{}\"\';:+=-|!@#$%^&*"; ////////////////////////////////////////////////////////////////////// class GDocFindReplaceParams3 : public GDocFindReplaceParams, public LMutex { public: // Find/Replace History GAutoWString LastFind; GAutoWString LastReplace; bool MatchCase; bool MatchWord; bool SelectionOnly; bool SearchUpwards; GDocFindReplaceParams3() { MatchCase = false; MatchWord = false; SelectionOnly = false; SearchUpwards = false; } }; class GTextView3Private : public GCss, public LMutex { public: GTextView3 *View; GRect rPadding; int PourX; bool LayoutDirty; ssize_t DirtyStart, DirtyLen; GColour UrlColour; bool CenterCursor; ssize_t WordSelectMode; GString Eol; GString LastError; // Find/Replace Params bool OwnFindReplaceParams; GDocFindReplaceParams3 *FindReplaceParams; // Map buffer ssize_t MapLen; char16 *MapBuf; // // Thread safe Name(char*) impl GString SetName; // #ifdef _DEBUG GString PourLog; #endif GTextView3Private(GTextView3 *view) : LMutex("GTextView3Private") { View = view; WordSelectMode = -1; PourX = -1; DirtyStart = DirtyLen = 0; UrlColour.Rgb(0, 0, 255); - uint32 c24 = 0; + uint32_t c24 = 0; if (_lgi_read_colour_config("colour.LC_URL", &c24)) UrlColour.c24(c24); CenterCursor = false; LayoutDirty = true; rPadding.ZOff(0, 0); MapBuf = 0; MapLen = 0; OwnFindReplaceParams = true; FindReplaceParams = new GDocFindReplaceParams3; } ~GTextView3Private() { if (OwnFindReplaceParams) { DeleteObj(FindReplaceParams); } DeleteArray(MapBuf); } void SetDirty(ssize_t Start, ssize_t Len = 0) { LayoutDirty = true; DirtyStart = Start; DirtyLen = Len; } void OnChange(PropType Prop) { if (Prop == GCss::PropPadding || Prop == GCss::PropPaddingLeft || Prop == GCss::PropPaddingRight || Prop == GCss::PropPaddingTop || Prop == GCss::PropPaddingBottom) { GCssTools t(this, View->GetFont()); rPadding.ZOff(0, 0); rPadding = t.ApplyPadding(rPadding); } } }; ////////////////////////////////////////////////////////////////////// enum UndoType { UndoDelete, UndoInsert, UndoChange }; struct Change : public GRange { UndoType Type; GArray Txt; }; struct GTextView3Undo : public GUndoEvent { GTextView3 *View; GArray Changes; GTextView3Undo(GTextView3 *view) { View = view; } void AddChange(ssize_t At, ssize_t Len, UndoType Type) { Change &c = Changes.New(); c.Start = At; c.Len = Len; c.Txt.Add(View->Text + At, Len); c.Type = Type; } void OnChange() { for (auto &c : Changes) { size_t Len = c.Len; if (View->Text) { char16 *t = View->Text + c.Start; for (size_t i=0; id->SetDirty(c.Start, c.Len); } } // GUndoEvent void ApplyChange() { View->UndoOn = false; for (auto &c : Changes) { switch (c.Type) { case UndoInsert: { View->Insert(c.Start, c.Txt.AddressOf(), c.Len); View->Cursor = c.Start + c.Len; break; } case UndoDelete: { View->Delete(c.Start, c.Len); View->Cursor = c.Start; break; } case UndoChange: { OnChange(); break; } } } View->UndoOn = true; View->Invalidate(); } void RemoveChange() { View->UndoOn = false; for (auto &c : Changes) { switch (c.Type) { case UndoInsert: { View->Delete(c.Start, c.Len); break; } case UndoDelete: { View->Insert(c.Start, c.Txt.AddressOf(), c.Len); break; } case UndoChange: { OnChange(); break; } } View->Cursor = c.Start; } View->UndoOn = true; View->Invalidate(); } }; void GTextView3::GStyle::RefreshLayout(size_t Start, ssize_t Len) { View->PourText(Start, Len); View->PourStyle(Start, Len); } ////////////////////////////////////////////////////////////////////// GTextView3::GTextView3( int Id, int x, int y, int cx, int cy, GFontType *FontType) : ResObject(Res_Custom) { // init vars GView::d->Css.Reset(d = new GTextView3Private(this)); PourEnabled = true; PartialPour = false; AdjustStylePos = true; BlinkTs = 0; LineY = 1; MaxX = 0; TextCache = 0; UndoOn = true; UndoCur = NULL; Font = 0; FixedWidthFont = false; FixedFont = 0; ShowWhiteSpace = false; ObscurePassword = false; TabSize = TAB_SIZE; IndentSize = TAB_SIZE; HardTabs = true; CanScrollX = false; Blink = true; // setup window SetId(Id); // default options Dirty = false; #if WINNATIVE CrLf = true; SetDlgCode(DLGC_WANTALLKEYS); #else CrLf = false; #endif Underline = NULL; Bold = NULL; d->Padding(GCss::Len(GCss::LenPx, 2)); #ifdef _DEBUG // debug times _PourTime = 0; _StyleTime = 0; _PaintTime = 0; #endif // Data Alloc = ALLOC_BLOCK; Text = new char16[Alloc]; if (Text) *Text = 0; Cursor = 0; Size = 0; // Display SelStart = SelEnd = -1; DocOffset = 0; ScrollX = 0; if (FontType) { Font = FontType->Create(); } else { GFontType Type; if (Type.GetSystemFont("Fixed")) Font = Type.Create(); else printf("%s:%i - failed to create font.\n", _FL); } if (Font) { SetTabStop(true); Underline = new GFont; if (Underline) { *Underline = *Font; Underline->Underline(true); if (d->UrlColour.IsValid()) Underline->Fore(d->UrlColour.c24()); Underline->Create(); } Bold = new GFont; if (Bold) { *Bold = *Font; Bold->Bold(true); Bold->Create(); } OnFontChange(); } else { LgiTrace("%s:%i - Failed to create font, FontType=%p\n", _FL, FontType); Font = SysFont; } CursorPos.ZOff(1, LineY-1); CursorPos.Offset(d->rPadding.x1, d->rPadding.y1); GRect r; r.ZOff(cx-1, cy-1); r.Offset(x, y); SetPos(r); LgiResources::StyleElement(this); } GTextView3::~GTextView3() { Line.DeleteObjects(); Style.Empty(); DeleteArray(TextCache); DeleteArray(Text); if (Font != SysFont) DeleteObj(Font); DeleteObj(FixedFont); DeleteObj(Underline); DeleteObj(Bold); // 'd' is owned by the GView::Css auto ptr } char16 *GTextView3::MapText(char16 *Str, ssize_t Len, bool RtlTrailingSpace) { if (ObscurePassword /*|| ShowWhiteSpace*/ || RtlTrailingSpace) { if (Len > d->MapLen) { DeleteArray(d->MapBuf); d->MapBuf = new char16[Len + RtlTrailingSpace]; d->MapLen = Len; } if (d->MapBuf) { int n = 0; if (RtlTrailingSpace) { d->MapBuf[n++] = ' '; for (int i=0; iMapBuf[n++] = Str[i]; } } else if (ObscurePassword) { for (int i=0; iMapBuf[n++] = '*'; } } /* else if (ShowWhiteSpace) { for (int i=0; iMapBuf[n++] = 0xb7; } else if (Str[i] == '\t') { d->MapBuf[n++] = 0x2192; } else { d->MapBuf[n++] = Str[i]; } } } */ return d->MapBuf; } } return Str; } void GTextView3::SetFixedWidthFont(bool i) { if (FixedWidthFont ^ i) { if (i) { GFontType Type; if (Type.GetSystemFont("Fixed")) { GFont *f = FixedFont; FixedFont = Font; Font = f; if (!Font) { Font = Type.Create(); if (Font) { Font->PointSize(FixedFont->PointSize()); } } GDocView::SetFixedWidthFont(i); } } else if (FixedFont) { GFont *f = FixedFont; FixedFont = Font; Font = f; GDocView::SetFixedWidthFont(i); } OnFontChange(); Invalidate(); } } void GTextView3::SetReadOnly(bool i) { GDocView::SetReadOnly(i); #if WINNATIVE SetDlgCode(i ? DLGC_WANTARROWS : DLGC_WANTALLKEYS); #endif } void GTextView3::SetCrLf(bool crlf) { CrLf = crlf; } -void GTextView3::SetTabSize(uint8 i) +void GTextView3::SetTabSize(uint8_t i) { TabSize = limit(i, 2, 32); OnFontChange(); OnPosChange(); Invalidate(); } void GTextView3::SetWrapType(LDocWrapType i) { GDocView::SetWrapType(i); CanScrollX = i != TEXTED_WRAP_REFLOW; OnPosChange(); Invalidate(); } GFont *GTextView3::GetFont() { return Font; } GFont *GTextView3::GetBold() { return Bold; } void GTextView3::SetFont(GFont *f, bool OwnIt) { if (!f) return; if (OwnIt) { if (Font != SysFont) DeleteObj(Font); Font = f; } else if (!Font || Font == SysFont) { Font = new GFont(*f); } else { *Font = *f; } if (Font) { if (!Underline) Underline = new GFont; if (Underline) { *Underline = *Font; Underline->Underline(true); Underline->Create(); if (d->UrlColour.IsValid()) Underline->Fore(d->UrlColour.c24()); } if (!Bold) Bold = new GFont; if (Bold) { *Bold = *Font; Bold->Bold(true); Bold->Create(); } } OnFontChange(); } void GTextView3::OnFontChange() { if (Font) { // get line height // int OldLineY = LineY; if (!Font->Handle()) Font->Create(); LineY = Font->GetHeight(); if (LineY < 1) LineY = 1; // get tab size char Spaces[32]; memset(Spaces, 'A', TabSize); Spaces[TabSize] = 0; GDisplayString ds(Font, Spaces); Font->TabSize(ds.X()); // repour doc d->SetDirty(0, Size); // validate blue underline font if (Underline) { *Underline = *Font; Underline->Underline(true); Underline->Create(); } #if WINNATIVE // Set the IME font. HIMC hIMC = ImmGetContext(Handle()); if (hIMC) { COMPOSITIONFORM Cf; Cf.dwStyle = CFS_POINT; Cf.ptCurrentPos.x = CursorPos.x1; Cf.ptCurrentPos.y = CursorPos.y1; LOGFONT FontInfo; GetObject(Font->Handle(), sizeof(FontInfo), &FontInfo); ImmSetCompositionFont(hIMC, &FontInfo); ImmReleaseContext(Handle(), hIMC); } #endif } } void GTextView3::LogLines() { int Idx = 0; LgiTrace("DocSize: %i\n", (int)Size); for (auto i : Line) { LgiTrace(" [%i]=%p, %i+%i, %s\n", Idx, i, (int)i->Start, (int)i->Len, i->r.GetStr()); Idx++; } #ifdef _DEBUG if (d->PourLog) LgiTrace("%s", d->PourLog.Get()); #endif } bool GTextView3::ValidateLines(bool CheckBox) { size_t Pos = 0; char16 *c = Text; size_t Idx = 0; GTextLine *Prev = NULL; for (auto i : Line) { GTextLine *l = i; if (l->Start != Pos) { LogLines(); LgiAssert(!"Incorrect start."); return false; } char16 *e = c; if (WrapType == TEXTED_WRAP_NONE) { while (*e && *e != '\n') e++; } else { char16 *end = Text + l->Start + l->Len; while (*e && *e != '\n' && e < end) e++; } ssize_t Len = e - c; if (l->Len != Len) { LogLines(); LgiAssert(!"Incorrect length."); return false; } if (CheckBox && Prev && Prev->r.y2 != l->r.y1 - 1) { LogLines(); LgiAssert(!"Lines not joined vertically"); } if (*e) { if (*e == '\n') e++; else if (WrapType == TEXTED_WRAP_REFLOW) e++; } Pos = e - Text; c = e; Idx++; Prev = l; } if (WrapType == TEXTED_WRAP_NONE && Pos != Size) { LogLines(); LgiAssert(!"Last line != end of doc"); return false; } return true; } int GTextView3::AdjustStyles(ssize_t Start, ssize_t Diff, bool ExtendStyle) { int Changes = 0; for (auto &s : Style) { if (s.Start == Start) { if (Diff < 0 || ExtendStyle) s.Len += Diff; else s.Start += Diff; Changes++; } else if (s.Start > Start) { s.Start += Diff; Changes++; } } return Changes; } // break array, break out of loop when we hit these chars #define ExitLoop(c) ( (c) == 0 || \ (c) == '\n' || \ (c) == ' ' || \ (c) == '\t' \ ) // extra breaking opportunities #define ExtraBreak(c) ( ( (c) >= 0x3040 && (c) <= 0x30FF ) || \ ( (c) >= 0x3300 && (c) <= 0x9FAF ) \ ) /* Prerequisite: The Line list must have either the objects with the correct Start/Len or be missing the lines altogether... */ void GTextView3::PourText(size_t Start, ssize_t Length /* == 0 means it's a delete */) { #if PROFILE_POUR char _txt[256]; sprintf_s(_txt, sizeof(_txt), "%p::PourText Lines=%i Sz=%i", this, (int)Line.Length(), (int)Size); GProfile Prof(_txt); #endif LgiAssert(InThread()); GRect Client = GetClient(); int Mx = Client.X() - d->rPadding.x1 - d->rPadding.x2; int Cy = 0; MaxX = 0; ssize_t Idx = -1; GTextLine *Cur = GetTextLine(Start, &Idx); // LgiTrace("Pour %i:%i Cur=%p Idx=%i\n", (int)Start, (int)Length, (int)Cur, (int)Idx); if (!Cur || !Cur->r.Valid()) { // Find the last line that has a valid position... for (auto i = Idx >= 0 ? Line.begin(Idx) : Line.rbegin(); *i; i--, Idx--) { Cur = *i; if (Cur->r.Valid()) { Cy = Cur->r.y1; if (Idx < 0) Idx = Line.IndexOf(Cur); break; } } } if (Cur && !Cur->r.Valid()) Cur = NULL; if (Cur) { Cy = Cur->r.y1; Start = Cur->Start; Length = Size - Start; // LgiTrace("Reset start to %i:%i because Cur!=NULL\n", (int)Start, (int)Length); } else { Idx = 0; Start = 0; Length = Size; } if (!Text || !Font || Mx <= 0) return; // Tracking vars ssize_t e; //int LastX = 0; int WrapCol = GetWrapAtCol(); GDisplayString Sp(Font, " ", 1); int WidthOfSpace = Sp.X(); if (WidthOfSpace < 1) { printf("%s:%i - WidthOfSpace test failed.\n", _FL); return; } // Alright... lets pour! uint64 StartTs = LgiCurrentTime(); if (WrapType == TEXTED_WRAP_NONE) { // Find the dimensions of each line that is missing a rect #if PROFILE_POUR Prof.Add("NoWrap: ExistingLines"); #endif #ifdef _DEGBUG GStringPipe Log(1024); Log.Printf("Pour: " LPrintfSizeT ", " LPrintfSSizeT ", partial=%i\n", Start, Length, PartialPour); #endif ssize_t Pos = 0; for (auto i = Line.begin(Idx); *i; i++, Idx++) { GTextLine *l = *i; #ifdef _DEGBUG Log.Printf(" [%i] exist: r.val=%i\n", Idx, l->r.Valid()); #endif if (!l->r.Valid()) // If the layout is not valid... { GDisplayString ds(Font, Text + l->Start, l->Len); l->r.x1 = d->rPadding.x1; l->r.x2 = l->r.x1 + ds.X(); MaxX = MAX(MaxX, l->r.X()); } // Adjust the y position anyway... it's free. l->r.y1 = Cy; l->r.y2 = l->r.y1 + LineY - 1; Cy = l->r.y2 + 1; Pos = l->Start + l->Len; if (Text[Pos] == '\n') Pos++; } // Now if we are missing lines as well, create them and lay them out #if PROFILE_POUR Prof.Add("NoWrap: NewLines"); #endif while (Pos < Size) { GTextLine *l = new GTextLine; l->Start = Pos; char16 *c = Text + Pos; char16 *e = c; while (*e && *e != '\n') e++; l->Len = e - c; #ifdef _DEGBUG Log.Printf(" [%i] new: start=" LPrintfSSizeT ", len=" LPrintfSSizeT "\n", Idx, l->Start, l->Len); #endif l->r.x1 = d->rPadding.x1; l->r.y1 = Cy; l->r.y2 = l->r.y1 + LineY - 1; if (l->Len) { GDisplayString ds(Font, Text + l->Start, l->Len); l->r.x2 = l->r.x1 + ds.X(); } else { l->r.x2 = l->r.x1; } Line.Insert(l); if (*e == '\n') e++; MaxX = MAX(MaxX, l->r.X()); Cy = l->r.y2 + 1; Pos = e - Text; Idx++; } #ifdef _DEGBUG d->PourLog = Log.NewGStr(); #endif PartialPour = false; } else // Wrap text { int DisplayStart = ScrollYLine(); int DisplayLines = (Client.Y() + LineY - 1) / LineY; int DisplayEnd = DisplayStart + DisplayLines; // Pouring is split into 2 parts... // 1) pouring to the end of the displayed text. // 2) pouring from there to the end of the document. // potentially taking several goes to complete the full pour // This allows the document to display and edit faster.. bool PourToDisplayEnd = Line.Length() < DisplayEnd; #if 0 LgiTrace("Idx=%i, DisplayStart=%i, DisplayLines=%i, DisplayEnd=%i, PourToDisplayEnd=%i\n", Idx, DisplayStart, DisplayLines, DisplayEnd, PourToDisplayEnd); #endif if ((ssize_t)Line.Length() > Idx) { for (auto i = Line.begin(Idx); *i; i++) delete *i; Line.Length(Idx); Cur = NULL; } int Cx = 0; ssize_t i; for (i=Start; i= Size || Text[e] == '\n' || (e-i) >= WrapCol) { break; } e++; } // Seek back some characters if we are mid word size_t OldE = e; if (e < Size && Text[e] != '\n') { while (e > i) { if (ExitLoop(Text[e]) || ExtraBreak(Text[e])) { break; } e--; } } if (e == i) { // No line break at all, so seek forward instead for (e=OldE; e < Size && Text[e] != '\n'; e++) { if (ExitLoop(Text[e]) || ExtraBreak(Text[e])) break; } } // Calc the width GDisplayString ds(Font, Text + i, e - i); Width = ds.X(); } else { // Wrap to edge of screen ssize_t PrevExitChar = -1; int PrevX = -1; while (true) { if (e >= Size || ExitLoop(Text[e]) || ExtraBreak(Text[e])) { GDisplayString ds(Font, Text + i, e - i); if (ds.X() + Cx > Mx) { if (PrevExitChar > 0) { e = PrevExitChar; Width = PrevX; } else { Width = ds.X(); } break; } else if (e >= Size || Text[e] == '\n') { Width = ds.X(); break; } PrevExitChar = e; PrevX = ds.X(); } e++; } } // Create layout line GTextLine *l = new GTextLine; if (l) { l->Start = i; l->Len = e - i; l->r.x1 = d->rPadding.x1; l->r.x2 = l->r.x1 + Width - 1; l->r.y1 = Cy; l->r.y2 = l->r.y1 + LineY - 1; Line.Insert(l); if (PourToDisplayEnd) { if (Line.Length() > DisplayEnd) { // We have reached the end of the displayed area... so // exit out temporarily to display the layout to the user PartialPour = true; break; } } else { // Otherwise check if we are taking too long... if (Line.Length() % 20 == 0) { uint64 Now = LgiCurrentTime(); if (Now - StartTs > WRAP_POUR_TIMEOUT) { PartialPour = true; // LgiTrace("Pour timeout...\n"); break; } } } MaxX = MAX(MaxX, l->r.X()); Cy += LineY; if (e < Size) e++; } } if (i >= Size) PartialPour = false; SendNotify(GNotifyCursorChanged); } #ifdef _DEBUG ValidateLines(true); #endif #if PROFILE_POUR Prof.Add("LastLine"); #endif if (!PartialPour) { GTextLine *Last = Line.Length() ? Line.Last() : 0; if (!Last || Last->Start + Last->Len < Size) { GTextLine *l = new GTextLine; if (l) { l->Start = Size; l->Len = 0; l->r.x1 = l->r.x2 = d->rPadding.x1; l->r.y1 = Cy; l->r.y2 = l->r.y1 + LineY - 1; Line.Insert(l); MaxX = MAX(MaxX, l->r.X()); Cy += LineY; } } } bool ScrollYNeeded = Client.Y() < (Line.Length() * LineY); bool ScrollChange = ScrollYNeeded ^ (VScroll != NULL); d->LayoutDirty = WrapType != TEXTED_WRAP_NONE && ScrollChange; #if PROFILE_POUR static GString _s; _s.Printf("ScrollBars dirty=%i", d->LayoutDirty); Prof.Add(_s); #endif if (ScrollChange) { // printf("%s:%i - SetScrollBars(%i)\n", _FL, ScrollYNeeded); SetScrollBars(false, ScrollYNeeded); } UpdateScrollBars(); #if 0 // def _DEBUG if (GetWindow()) { static char s[256]; sprintf_s(s, sizeof(s), "Pour: %.2f sec", (double)_PourTime / 1000); GetWindow()->PostEvent(M_TEXTVIEW_DEBUG_TEXT, (GMessage::Param)s); } #endif #if POUR_DEBUG printf("Lines=%i\n", Line.Length()); int Index = 0; for (GTextLine *l=Line.First(); l; l=Line.Next(), Index++) { printf("\t[%i] %i,%i (%s)\n", Index, l->Start, l->Len, l->r.Describe()); } #endif } bool GTextView3::InsertStyle(GAutoPtr s) { if (!s) return false; LgiAssert(s->Start >= 0); LgiAssert(s->Len > 0); ssize_t Last = 0; // int n = 0; // LgiTrace("StartStyle=%i,%i(%i) %s\n", (int)s->Start, (int)s->Len, (int)(s->Start+s->Len), s->Fore.GetStr()); if (Style.Length() > 0) { // Optimize for last in the list auto Last = Style.rbegin(); if (s->Start >= (ssize_t)Last->End()) { Style.Insert(*s); return true; } } for (auto i = Style.begin(); i != Style.end(); i++) { if (s->Overlap(*i)) { if (s->Owner > i->Owner) { // Fail the insert return false; } else { // Replace mode... *i = *s; return true; } } if (s->Start >= Last && s->Start < i->Start) { Style.Insert(*s, i); return true; } } Style.Insert(*s); return true; } GTextView3::GStyle *GTextView3::GetNextStyle(StyleIter &s, ssize_t Where) { if (Where >= 0) s = Style.begin(); else s++; while (s != Style.end()) { // determine whether style is relevant.. // styles in the selected region are ignored ssize_t Min = MIN(SelStart, SelEnd); ssize_t Max = MAX(SelStart, SelEnd); if (SelStart >= 0 && s->Start >= Min && s->Start+s->Len < Max) { // style is completely inside selection: ignore s++; } else if (Where >= 0 && s->Start+s->Len < Where) { s++; } else { return &(*s); } } return NULL; } #if 0 CURSOR_CHAR GetCursor() { #ifdef WIN32 GArray Ver; int Os = LgiGetOs(&Ver); if ((Os == LGI_OS_WIN32 || Os == LGI_OS_WIN64) && Ver[0] >= 5) { return MAKEINTRESOURCE(32649); // hand } else { return IDC_ARROW; } #endif return 0; } #endif GTextView3::GStyle *GTextView3::HitStyle(ssize_t i) { for (auto &s : Style) { if (i >= s.Start && i < (ssize_t)s.End()) { return &s; } } return NULL; } void GTextView3::PourStyle(size_t Start, ssize_t EditSize) { #ifdef _DEBUG int64 StartTime = LgiCurrentTime(); #endif LgiAssert(InThread()); if (!Text || Size < 1) return; ssize_t Length = MAX(EditSize, 0); // Expand re-style are to word boundaries before and after the area of change while (Start > 0 && UrlChar(Text[Start-1])) { // Move the start back Start--; Length++; } while ((ssize_t)Start + Length < Size && UrlChar(Text[Start+Length])) { // Move the end back Length++; } // Delete all the styles that we own inside the changed area for (StyleIter s = Style.begin(); s != Style.end();) { if (s->Owner == STYLE_NONE) { if (EditSize > 0) { if (s->Overlap(Start, EditSize < 0 ? -EditSize : EditSize)) { Style.Delete(s); continue; } } else { if (s->Overlap(Start, -EditSize)) { Style.Delete(s); continue; } } } s++; } if (UrlDetect) { GArray Links; LgiAssert((ssize_t)Start + Length <= Size); if (LgiDetectLinks(Links, Text + Start, Length)) { - for (uint32 i=0; i Url(new GStyle(STYLE_URL)); if (Url) { Url->View = this; Url->Start = Inf.Start + Start; Url->Len = Inf.Len; // Url->Email = Inf.Email; Url->Font = Underline; Url->Fore = d->UrlColour; InsertStyle(Url); } } } } #ifdef _DEBUG _StyleTime = LgiCurrentTime() - StartTime; #endif } bool GTextView3::Insert(size_t At, const char16 *Data, ssize_t Len) { GProfile Prof("GTextView3::Insert"); Prof.HideResultsIfBelow(1000); LgiAssert(InThread()); if (!ReadOnly && Len > 0) { if (!Data) return false; // limit input to valid data At = MIN(Size, (ssize_t)At); // make sure we have enough memory size_t NewAlloc = Size + Len + 1; NewAlloc += ALLOC_BLOCK - (NewAlloc % ALLOC_BLOCK); if (NewAlloc != Alloc) { char16 *NewText = new char16[NewAlloc]; if (NewText) { if (Text) { // copy any existing data across memcpy(NewText, Text, (Size + 1) * sizeof(char16)); } DeleteArray(Text); Text = NewText; Alloc = NewAlloc; } else { // memory allocation error return false; } } Prof.Add("MemChk"); if (Text) { // Insert the data // Move the section after the insert to make space... memmove(Text+(At+Len), Text+At, (Size-At) * sizeof(char16)); Prof.Add("Cpy"); // Copy new data in... memcpy(Text+At, Data, Len * sizeof(char16)); Size += Len; Text[Size] = 0; // NULL terminate Prof.Add("Undo"); // Add the undo object... if (UndoOn) { GAutoPtr Obj(new GTextView3Undo(this)); GTextView3Undo *u = UndoCur ? UndoCur : Obj; if (u) u->AddChange(At, Len, UndoInsert); if (Obj) UndoQue += Obj.Release(); } // Clear layout info for the new text ssize_t Idx = -1; GTextLine *Cur = NULL; if (Line.Length() == 0) { // Empty doc... set up the first line Line.Insert(Cur = new GTextLine); Idx = 0; Cur->Start = 0; } else { Cur = GetTextLine(At, &Idx); } if (Cur) { if (WrapType == TEXTED_WRAP_NONE) { // Clear layout for current line... Cur->r.ZOff(-1, -1); Prof.Add("NoWrap add lines"); // Add any new lines that we need... char16 *e = Text + At + Len; char16 *c; for (c = Text + At; c < e; c++) { if (*c == '\n') { // Set the size of the current line... size_t Pos = c - Text; Cur->Len = Pos - Cur->Start; // Create a new line... Cur = new GTextLine(); if (!Cur) return false; Cur->Start = Pos + 1; Line.Insert(Cur, ++Idx); } } Prof.Add("CalcLen"); // Make sure the last Line's length is set.. Cur->CalcLen(Text); Prof.Add("UpdatePos"); // Now update all the positions of the following lines... for (auto i = Line.begin(++Idx); *i; i++) (*i)->Start += Len; } else { // Clear all lines to the end of the doc... for (auto i = Line.begin(Idx); *i; i++) delete *i; Line.Length(Idx); } } else { // If wrap is on then this can happen when an Insert happens before the // OnPulse event has laid out the new text. Probably not a good thing in // non-wrap mode if (WrapType == TEXTED_WRAP_NONE) { GTextLine *l = Line.Last(); printf("%s:%i - Insert error: no cur, At=%i, Size=%i, Lines=%i, WrapType=%i\n", _FL, (int)At, (int)Size, (int)Line.Length(), (int)WrapType); if (l) printf("Last=%i, %i\n", (int)l->Start, (int)l->Len); } } #ifdef _DEBUG // Prof.Add("Validate"); // ValidateLines(); #endif if (AdjustStylePos) AdjustStyles(At, Len); Dirty = true; if (PourEnabled) { Prof.Add("PourText"); PourText(At, Len); Prof.Add("PourStyle"); PourStyle(At, Len); } SendNotify(GNotifyDocChanged); return true; } } return false; } bool GTextView3::Delete(size_t At, ssize_t Len) { bool Status = false; LgiAssert(InThread()); if (!ReadOnly) { // limit input At = MAX(At, 0); At = MIN((ssize_t)At, Size); Len = MIN(Size-(ssize_t)At, Len); if (Len > 0) { int HasNewLine = 0; for (int i=0; i Obj(new GTextView3Undo(this)); GTextView3Undo *u = UndoCur ? UndoCur : Obj; if (u) u->AddChange(At, Len, UndoDelete); if (Obj) UndoQue += Obj.Release(); } memmove(Text+At, Text+(At+Len), (Size-At-Len) * sizeof(char16)); Size -= Len; Text[Size] = 0; if (WrapType == TEXTED_WRAP_NONE) { ssize_t Idx = -1; GTextLine *Cur = GetTextLine(At, &Idx); if (Cur) { Cur->r.ZOff(-1, -1); // Delete some lines... for (int i=0; iCalcLen(Text); // Shift all further lines down... for (auto i = Line.begin(Idx + 1); *i; i++) (*i)->Start -= Len; } } else { ssize_t Index; GTextLine *Cur = GetTextLine(At, &Index); if (Cur) { for (auto i = Line.begin(Index); *i; i++) delete *i; Line.Length(Index); } } Dirty = true; Status = true; #ifdef _DEBUG ValidateLines(); #endif if (AdjustStylePos) AdjustStyles(At, -Len); if (PourEnabled) { PourText(At, -Len); PourStyle(At, -Len); } if (Cursor >= (ssize_t)At && Cursor <= (ssize_t)At + Len) { SetCaret(At, false, HasNewLine != 0); } // Handle repainting in flowed mode, when the line starts change if (WrapType == TEXTED_WRAP_REFLOW) { ssize_t Index; GTextLine *Cur = GetTextLine(At, &Index); if (Cur) { GRect r = Cur->r; r.x2 = GetClient().x2; r.y2 = GetClient().y2; Invalidate(&r); } } SendNotify(GNotifyDocChanged); Status = true; } } return Status; } void GTextView3::DeleteSelection(char16 **Cut) { if (SelStart >= 0) { ssize_t Min = MIN(SelStart, SelEnd); ssize_t Max = MAX(SelStart, SelEnd); if (Cut) { *Cut = NewStrW(Text + Min, Max - Min); } Delete(Min, Max - Min); SetCaret(Min, false, true); } } GTextView3::GTextLine *GTextView3::GetTextLine(ssize_t Offset, ssize_t *Index) { int i = 0; for (GTextLine *l = Line.First(); l; l = Line.Next(), i++) { if (Offset >= l->Start && Offset <= l->Start+l->Len) { if (Index) { *Index = i; } return l; } } return NULL; } int64 GTextView3::Value() { char *n = Name(); #ifdef _MSC_VER return (n) ? _atoi64(n) : 0; #else return (n) ? atoll(n) : 0; #endif } void GTextView3::Value(int64 i) { char Str[32]; sprintf_s(Str, sizeof(Str), LPrintfInt64, i); Name(Str); } GString GTextView3::operator[](ssize_t LineIdx) { if (LineIdx <= 0 || LineIdx > (ssize_t)GetLines()) return GString(); GTextLine *Ln = Line[LineIdx-1]; if (!Ln) return GString(); GString s(Text + Ln->Start, Ln->Len); return s; } char *GTextView3::Name() { UndoQue.Empty(); DeleteArray(TextCache); TextCache = WideToUtf8(Text); return TextCache; } bool GTextView3::Name(const char *s) { if (InThread()) { UndoQue.Empty(); DeleteArray(TextCache); DeleteArray(Text); Line.DeleteObjects(); Style.Empty(); LgiAssert(LgiIsUtf8(s)); Text = Utf8ToWide(s); if (!Text) { Text = new char16[1]; if (Text) *Text = 0; } Size = Text ? StrlenW(Text) : 0; Alloc = Size + 1; Cursor = MIN(Cursor, Size); if (Text) { // Remove '\r's char16 *o = Text; for (char16 *i=Text; *i; i++) { if (*i != '\r') { *o++ = *i; } else Size--; } *o++ = 0; } // update everything else d->SetDirty(0, Size); PourText(0, Size); PourStyle(0, Size); UpdateScrollBars(); Invalidate(); } else if (d->Lock(_FL)) { if (IsAttached()) { d->SetName = s; PostEvent(M_TEXT_UPDATE_NAME); } else LgiAssert(!"Can't post event to detached/virtual window."); d->Unlock(); } return true; } char16 *GTextView3::NameW() { return Text; } bool GTextView3::NameW(const char16 *s) { DeleteArray(Text); Size = s ? StrlenW(s) : 0; Alloc = Size + 1; Text = new char16[Alloc]; Cursor = MIN(Cursor, Size); if (Text) { memcpy(Text, s, Size * sizeof(char16)); // remove LF's int In = 0, Out = 0; CrLf = false; for (; InSetDirty(0, Size); PourText(0, Size); PourStyle(0, Size); UpdateScrollBars(); Invalidate(); return true; } GRange GTextView3::GetSelectionRange() { GRange r; if (HasSelection()) { r.Start = MIN(SelStart, SelEnd); ssize_t End = MAX(SelStart, SelEnd); r.Len = End - r.Start; } return r; } char *GTextView3::GetSelection() { GRange s = GetSelectionRange(); if (s.Len > 0) { return (char*)LgiNewConvertCp("utf-8", Text + s.Start, LGI_WideCharset, s.Len*sizeof(Text[0]) ); } return 0; } bool GTextView3::HasSelection() { return (SelStart >= 0) && (SelStart != SelEnd); } void GTextView3::SelectAll() { SelStart = 0; SelEnd = Size; Invalidate(); } void GTextView3::UnSelectAll() { bool Update = HasSelection(); SelStart = -1; SelEnd = -1; if (Update) { Invalidate(); } } size_t GTextView3::GetLines() { return Line.Length(); } void GTextView3::GetTextExtent(int &x, int &y) { PourText(0, Size); x = MaxX + d->rPadding.x1; y = (int)(Line.Length() * LineY); } bool GTextView3::GetLineColumnAtIndex(GdcPt2 &Pt, ssize_t Index) { ssize_t FromIndex = 0; GTextLine *From = GetTextLine(Index < 0 ? Cursor : Index, &FromIndex); if (!From) return false; Pt.x = (int) (Cursor - From->Start); Pt.y = (int) FromIndex; return true; } ssize_t GTextView3::GetCaret(bool Cur) { if (Cur) { return Cursor; } return 0; } ssize_t GTextView3::IndexAt(int x, int y) { GTextLine *l = Line.ItemAt(y); if (l) { return l->Start + MIN(x, l->Len); } return 0; } bool GTextView3::ScrollToOffset(size_t Off) { bool ForceFullUpdate = false; ssize_t ToIndex = 0; GTextLine *To = GetTextLine(Off, &ToIndex); if (VScroll && To) { GRect Client = GetClient(); int DisplayLines = Client.Y() / LineY; if (ToIndex < VScroll->Value()) { // Above the visible region... if (d->CenterCursor) { ssize_t i = ToIndex - (DisplayLines >> 1); VScroll->Value(MAX(0, i)); } else { VScroll->Value(ToIndex); } ForceFullUpdate = true; } if (ToIndex >= VScroll->Value() + DisplayLines) { int YOff = d->CenterCursor ? DisplayLines >> 1 : DisplayLines; ssize_t v = MIN(ToIndex - YOff + 1, (ssize_t)Line.Length() - DisplayLines); if (v != VScroll->Value()) { // Below the visible region VScroll->Value(v); ForceFullUpdate = true; } } } return ForceFullUpdate; } void GTextView3::SetCaret(size_t i, bool Select, bool ForceFullUpdate) { // int _Start = LgiCurrentTime(); Blink = true; // Bound the new cursor position to the document if ((ssize_t)i > Size) i = Size; // Store the old selection and cursor ssize_t s = SelStart, e = SelEnd, c = Cursor; // If there is going to be a selected area if (Select && i != SelStart) { // Then set the start if (SelStart < 0) { // We are starting a new selection SelStart = Cursor; } // And end SelEnd = i; } else { // Clear the selection SelStart = SelEnd = -1; } ssize_t FromIndex = 0; GTextLine *From = GetTextLine(Cursor, &FromIndex); Cursor = i; // check the cursor is on the screen ForceFullUpdate |= ScrollToOffset(Cursor); // check whether we need to update the screen ssize_t ToIndex = 0; GTextLine *To = GetTextLine(Cursor, &ToIndex); if (ForceFullUpdate || !To || !From) { // need full update Invalidate(); } else if ( ( SelStart != s || SelEnd != e ) ) { // Update just the selection bounds GRect Client = GetClient(); size_t Start, End; if (SelStart >= 0 && s >= 0) { // Selection has changed, union the before and after regions Start = MIN(Cursor, c); End = MAX(Cursor, c); } else if (SelStart >= 0) { // Selection created... Start = MIN(SelStart, SelEnd); End = MAX(SelStart, SelEnd); } else if (s >= 0) { // Selection removed... Start = MIN(s, e); End = MAX(s, e); } else { LgiAssert(0); return; } GTextLine *SLine = GetTextLine(Start); GTextLine *ELine = GetTextLine(End); GRect u; if (SLine && ELine) { if (SLine->r.Valid()) { u = DocToScreen(SLine->r); } else u.Set(0, 0, Client.X()-1, 1); // Start of visible page GRect b(0, Client.Y()-1, Client.X()-1, Client.Y()-1); if (ELine->r.Valid()) { b = DocToScreen(ELine->r); } else { b.Set(0, Client.Y()-1, Client.X()-1, Client.Y()-1); } u.Union(&b); u.x1 = 0; u.x2 = X(); } else { /* printf("%s,%i - Couldn't get SLine and ELine: %i->%p, %i->%p\n", _FL, (int)Start, SLine, (int)End, ELine); */ u = Client; } Invalidate(&u); } else if (Cursor != c) { // just the cursor has moved // update the line the cursor moved to GRect r = To->r; r.Offset(-ScrollX, d->rPadding.y1-DocOffset); r.x2 = X(); Invalidate(&r); if (To != From) { // update the line the cursor came from, // if it's a different line from the "to" r = From->r; r.Offset(-ScrollX, d->rPadding.y1-DocOffset); r.x2 = X(); Invalidate(&r); } } if (c != Cursor) { // Send off notify SendNotify(GNotifyCursorChanged); } //int _Time = LgiCurrentTime() - _Start; //printf("Setcursor=%ims\n", _Time); } void GTextView3::SetBorder(int b) { } bool GTextView3::Cut() { bool Status = false; char16 *Txt16 = 0; DeleteSelection(&Txt16); if (Txt16) { #ifdef WIN32 Txt16 = ConvertToCrLf(Txt16); #endif char *Txt8 = (char*)LgiNewConvertCp(LgiAnsiToLgiCp(), Txt16, LGI_WideCharset); GClipBoard Clip(this); Clip.Text(Txt8); Status = Clip.TextW(Txt16, false); DeleteArray(Txt8); DeleteArray(Txt16); } return Status; } bool GTextView3::Copy() { bool Status = true; if (SelStart >= 0) { ssize_t Min = MIN(SelStart, SelEnd); ssize_t Max = MAX(SelStart, SelEnd); char16 *Txt16 = NewStrW(Text+Min, Max-Min); #ifdef WIN32 Txt16 = ConvertToCrLf(Txt16); #endif char *Txt8 = (char*)LgiNewConvertCp(LgiAnsiToLgiCp(), Txt16, LGI_WideCharset); GClipBoard Clip(this); Clip.Text(Txt8); Clip.TextW(Txt16, false); DeleteArray(Txt8); DeleteArray(Txt16); } else LgiTrace("%s:%i - No selection.\n", _FL); return Status; } bool GTextView3::Paste() { GClipBoard Clip(this); GAutoWString Mem; char16 *t = Clip.TextW(); if (!t) // ala Win9x { char *s = Clip.Text(); if (s) { Mem.Reset(Utf8ToWide(s)); t = Mem; } } if (!t) return false; if (SelStart >= 0) { DeleteSelection(); } // remove '\r's char16 *s = t, *d = t; for (; *s; s++) { if (*s != '\r') { *d++ = *s; } } *d++ = 0; // insert text ssize_t Len = StrlenW(t); Insert(Cursor, t, Len); SetCaret(Cursor+Len, false, true); // Multiline return true; } bool GTextView3::ClearDirty(bool Ask, char *FileName) { if (Dirty) { int Answer = (Ask) ? LgiMsg(this, LgiLoadString(L_TEXTCTRL_ASK_SAVE, "Do you want to save your changes to this document?"), LgiLoadString(L_TEXTCTRL_SAVE, "Save"), MB_YESNOCANCEL) : IDYES; if (Answer == IDYES) { GFileSelect Select; Select.Parent(this); if (!FileName && Select.Save()) { FileName = Select.Name(); } Save(FileName); } else if (Answer == IDCANCEL) { return false; } } return true; } bool GTextView3::Open(const char *Name, const char *CharSet) { bool Status = false; GFile f; if (f.Open(Name, O_READ|O_SHARE)) { DeleteArray(Text); int64 Bytes = f.GetSize(); if (Bytes < 0 || Bytes & 0xffff000000000000LL) { LgiTrace("%s:%i - Invalid file size: " LPrintfInt64 "\n", _FL, Bytes); return false; } SetCaret(0, false); Line.DeleteObjects(); char *c8 = new char[Bytes + 4]; if (c8) { if (f.Read(c8, (int)Bytes) == Bytes) { char *DataStart = c8; c8[Bytes] = 0; c8[Bytes+1] = 0; c8[Bytes+2] = 0; c8[Bytes+3] = 0; if ((uchar)c8[0] == 0xff && (uchar)c8[1] == 0xfe) { // utf-16 if (!CharSet) { CharSet = "utf-16"; DataStart += 2; } } // Convert to unicode first.... if (Bytes == 0) { Text = new char16[1]; if (Text) Text[0] = 0; } else { Text = (char16*)LgiNewConvertCp(LGI_WideCharset, DataStart, CharSet ? CharSet : DefaultCharset); } if (Text) { // Remove LF's char16 *In = Text, *Out = Text; CrLf = false; Size = 0; while (*In) { if (*In >= ' ' || *In == '\t' || *In == '\n') { *Out++ = *In; Size++; } else if (*In == '\r') { CrLf = true; } In++; } Size = (int) (Out - Text); *Out = 0; Alloc = Size + 1; Dirty = false; if (Text && Text[0] == 0xfeff) // unicode byte order mark { memmove(Text, Text+1, Size * sizeof(*Text)); Size--; } PourText(0, Size); PourStyle(0, Size); UpdateScrollBars(true); Status = true; } } DeleteArray(c8); } else { Alloc = Size = 0; } Invalidate(); } return Status; } bool GTextView3::Save(const char *Name, const char *CharSet) { GFile f; GString TmpName; bool Status = false; d->LastError.Empty(); if (f.Open(Name, O_WRITE)) { if (f.SetSize(0) != 0) { // Can't resize file, fall back to renaming it and // writing a new file... f.Close(); TmpName = Name; TmpName += ".tmp"; if (!FileDev->Move(Name, TmpName)) { LgiTrace("%s:%i - Failed to move '%s'.\n", _FL, Name); return false; } if (!f.Open(Name, O_WRITE)) { LgiTrace("%s:%i - Failed to open '%s' for writing.\n", _FL, Name); return false; } } if (Text) { char *c8 = (char*)LgiNewConvertCp(CharSet ? CharSet : DefaultCharset, Text, LGI_WideCharset, Size * sizeof(char16)); if (c8) { int Len = (int)strlen(c8); if (CrLf) { Status = true; int BufLen = 1 << 20; GAutoString Buf(new char[BufLen]); char *b = Buf; char *e = Buf + BufLen; char *c = c8; while (*c) { if (b > e - 10) { ptrdiff_t Bytes = b - Buf; if (f.Write(Buf, (int)Bytes) != Bytes) { Status = false; break; } b = Buf; } if (*c == '\n') { *b++ = '\r'; *b++ = '\n'; } else { *b++ = *c; } c++; } ptrdiff_t Bytes = b - Buf; if (f.Write(Buf, (int)Bytes) != Bytes) Status = false; } else { Status = f.Write(c8, Len) == Len; } DeleteArray(c8); } Dirty = false; } } else { int Err = f.GetError(); GAutoString sErr = LgiErrorCodeToString(Err); d->LastError.Printf("Failed to open '%s' for writing: %i - %s\n", Name, Err, sErr.Get()); } if (TmpName) FileDev->Delete(TmpName); return Status; } const char *GTextView3::GetLastError() { return d->LastError; } void GTextView3::UpdateScrollBars(bool Reset) { if (VScroll) { GRect Before = GetClient(); int DisplayLines = Y() / LineY; ssize_t Lines = GetLines(); // printf("SetLimits %i, %i\n", 0, (int)Lines); VScroll->SetLimits(0, Lines); if (VScroll) { VScroll->SetPage(DisplayLines); ssize_t Max = Lines - DisplayLines + 1; bool Inval = false; if (VScroll->Value() > Max) { VScroll->Value(Max); Inval = true; } if (Reset) { VScroll->Value(0); SelStart = SelEnd = -1; } GRect After = GetClient(); if (Before != After && GetWrapType()) { d->SetDirty(0, Size); Inval = true; } if (Inval) { Invalidate(); } } } } bool GTextView3::DoCase(bool Upper) { if (Text) { ssize_t Min = MIN(SelStart, SelEnd); ssize_t Max = MAX(SelStart, SelEnd); if (Min < Max) { if (UndoOn) { GAutoPtr Obj(new GTextView3Undo(this)); GTextView3Undo *u = UndoCur ? UndoCur : Obj; if (u) u->AddChange(Min, Max - Min, UndoChange); if (Obj) UndoQue += Obj.Release(); } for (ssize_t i=Min; i= 'a' && Text[i] <= 'z') Text[i] = Text[i] - 'a' + 'A'; } else { if (Text[i] >= 'A' && Text[i] <= 'Z') Text[i] = Text[i] - 'A' + 'a'; } } Dirty = true; d->SetDirty(Min, 0); Invalidate(); SendNotify(GNotifyDocChanged); } } return true; } ssize_t GTextView3::GetLine() { ssize_t Idx = 0; GetTextLine(Cursor, &Idx); return Idx + 1; } void GTextView3::SetLine(int i) { GTextLine *l = Line.ItemAt(i - 1); if (l) { d->CenterCursor = true; SetCaret(l->Start, false); d->CenterCursor = false; } } bool GTextView3::DoGoto() { GInput Dlg(this, "", LgiLoadString(L_TEXTCTRL_GOTO_LINE, "Goto line:"), "Text"); if (Dlg.DoModal() == IDOK && Dlg.GetStr()) { SetLine(atoi(Dlg.GetStr())); } return true; } GDocFindReplaceParams *GTextView3::CreateFindReplaceParams() { return new GDocFindReplaceParams3; } void GTextView3::SetFindReplaceParams(GDocFindReplaceParams *Params) { if (Params) { if (d->OwnFindReplaceParams) { DeleteObj(d->FindReplaceParams); } d->OwnFindReplaceParams = false; d->FindReplaceParams = (GDocFindReplaceParams3*) Params; } } bool GTextView3::DoFindNext() { bool Status = false; if (InThread()) { if (d->FindReplaceParams->Lock(_FL)) { if (d->FindReplaceParams->LastFind) Status = OnFind(d->FindReplaceParams->LastFind, d->FindReplaceParams->MatchWord, d->FindReplaceParams->MatchCase, d->FindReplaceParams->SelectionOnly, d->FindReplaceParams->SearchUpwards); d->FindReplaceParams->Unlock(); } } else if (IsAttached()) { Status = PostEvent(M_TEXTVIEW_FIND); } return Status; } bool Text3_FindCallback(GFindReplaceCommon *Dlg, bool Replace, void *User) { GTextView3 *v = (GTextView3*) User; if (v->d->FindReplaceParams && v->d->FindReplaceParams->Lock(_FL)) { v->d->FindReplaceParams->MatchWord = Dlg->MatchWord; v->d->FindReplaceParams->MatchCase = Dlg->MatchCase; v->d->FindReplaceParams->SelectionOnly = Dlg->SelectionOnly; v->d->FindReplaceParams->SearchUpwards = Dlg->SearchUpwards; v->d->FindReplaceParams->LastFind.Reset(Utf8ToWide(Dlg->Find)); v->d->FindReplaceParams->Unlock(); } return v->DoFindNext(); } bool GTextView3::DoFind() { GString u; if (HasSelection()) { ssize_t Min = MIN(SelStart, SelEnd); ssize_t Max = MAX(SelStart, SelEnd); u = GString(Text + Min, Max - Min); } else { u = d->FindReplaceParams->LastFind.Get(); } #ifdef BEOS GFindDlg *Dlg = new GFindDlg(this, u, Text3_FindCallback, this); if (Dlg) Dlg->DoModeless(); #else GFindDlg Dlg(this, u, Text3_FindCallback, this); Dlg.DoModal(); Focus(true); #endif return false; } bool GTextView3::DoReplace() { bool SingleLineSelection = false; SingleLineSelection = HasSelection(); if (SingleLineSelection) { GRange Sel = GetSelectionRange(); for (ssize_t i = Sel.Start; i < Sel.End(); i++) { if (Text[i] == '\n') { SingleLineSelection = false; break; } } } char *LastFind8 = SingleLineSelection ? GetSelection() : WideToUtf8(d->FindReplaceParams->LastFind); char *LastReplace8 = WideToUtf8(d->FindReplaceParams->LastReplace); GReplaceDlg Dlg(this, LastFind8, LastReplace8); Dlg.MatchWord = d->FindReplaceParams->MatchWord; Dlg.MatchCase = d->FindReplaceParams->MatchCase; Dlg.SelectionOnly = HasSelection(); int Action = Dlg.DoModal(); DeleteArray(LastFind8); DeleteArray(LastReplace8); if (Action != IDCANCEL) { d->FindReplaceParams->LastFind.Reset(Utf8ToWide(Dlg.Find)); d->FindReplaceParams->LastReplace.Reset(Utf8ToWide(Dlg.Replace)); d->FindReplaceParams->MatchWord = Dlg.MatchWord; d->FindReplaceParams->MatchCase = Dlg.MatchCase; d->FindReplaceParams->SelectionOnly = Dlg.SelectionOnly; } switch (Action) { case IDC_FR_FIND: { OnFind( d->FindReplaceParams->LastFind, d->FindReplaceParams->MatchWord, d->FindReplaceParams->MatchCase, d->FindReplaceParams->SelectionOnly, d->FindReplaceParams->SearchUpwards); break; } case IDOK: case IDC_FR_REPLACE: { OnReplace( d->FindReplaceParams->LastFind, d->FindReplaceParams->LastReplace, Action == IDOK, d->FindReplaceParams->MatchWord, d->FindReplaceParams->MatchCase, d->FindReplaceParams->SelectionOnly, d->FindReplaceParams->SearchUpwards); break; } } return false; } void GTextView3::SelectWord(size_t From) { for (SelStart = From; SelStart > 0; SelStart--) { if (strchr(SelectWordDelim, Text[SelStart])) { SelStart++; break; } } for (SelEnd = From; SelEnd < Size; SelEnd++) { if (strchr(SelectWordDelim, Text[SelEnd])) { break; } } Invalidate(); } typedef int (*StringCompareFn)(const char16 *a, const char16 *b, ssize_t n); ptrdiff_t GTextView3::MatchText(const char16 *Find, bool MatchWord, bool MatchCase, bool SelectionOnly, bool SearchUpwards) { if (!ValidStrW(Find)) return -1; ssize_t FindLen = StrlenW(Find); // Setup range to search ssize_t Begin, End; if (SelectionOnly && HasSelection()) { Begin = MIN(SelStart, SelEnd); End = MAX(SelStart, SelEnd); } else { Begin = 0; End = Size; } // Look through text... ssize_t i; bool Wrap = false; if (Cursor > End - FindLen) { Wrap = true; if (SearchUpwards) i = End - FindLen; else i = Begin; } else { i = Cursor; } if (i < Begin) i = Begin; if (i > End) i = End; StringCompareFn CmpFn = MatchCase ? StrncmpW : StrnicmpW; char16 FindCh = MatchCase ? Find[0] : toupper(Find[0]); for (; SearchUpwards ? i >= Begin : i <= End - FindLen; i += SearchUpwards ? -1 : 1) { if ( (MatchCase ? Text[i] : toupper(Text[i])) == FindCh ) { char16 *Possible = Text + i; if (CmpFn(Possible, Find, FindLen) == 0) { if (MatchWord) { // Check boundaries if (Possible > Text) // Check off the start { if (!IsWordBoundry(Possible[-1])) continue; } if (i + FindLen < Size) // Check off the end { if (!IsWordBoundry(Possible[FindLen])) continue; } } GRange r(Possible - Text, FindLen); if (!r.Overlap(Cursor)) return r.Start; } } if (!Wrap && (i + 1 > End - FindLen)) { Wrap = true; i = Begin; End = Cursor; } } return -1; } bool GTextView3::OnFind(const char16 *Find, bool MatchWord, bool MatchCase, bool SelectionOnly, bool SearchUpwards) { THREAD_CHECK(); // Not sure what this is doing??? if (HasSelection() && SelEnd < SelStart) { Cursor = SelStart; } #if FEATURE_HILIGHT_ALL_MATCHES // Clear existing styles for matches for (StyleIter s = Style.begin(); s != Style.end(); ) { if (s->Owner == STYLE_FIND_MATCHES) Style.Delete(s); else s++; } ssize_t FindLen = StrlenW(Find); ssize_t FirstLoc = MatchText(Find, MatchWord, MatchCase, false, SearchUpwards), Loc; if (FirstLoc >= 0) { SetCaret(FirstLoc, false); SetCaret(FirstLoc + FindLen, true); } ssize_t Old = Cursor; if (!SearchUpwards) Cursor += FindLen; while ((Loc = MatchText(Find, MatchWord, MatchCase, false, false)) != FirstLoc) { GAutoPtr s(new GStyle(STYLE_FIND_MATCHES)); s->Start = Loc; s->Len = FindLen; s->Fore.Set(LC_FOCUS_SEL_FORE, 24); s->Back = GColour(LC_FOCUS_SEL_BACK, 24).Mix(GColour(LC_WORKSPACE, 24)); InsertStyle(s); Cursor = Loc + FindLen; } Cursor = Old; ScrollToOffset(Cursor); Invalidate(); #else ssize_t Loc = MatchText(Find, MatchWord, MatchCase, SelectionOnly, SearchUpwards); if (Loc >= 0) { SetCaret(Loc, false); SetCaret(Loc + StrlenW(Find), true); return true; } #endif return false; } bool GTextView3::OnReplace(const char16 *Find, const char16 *Replace, bool All, bool MatchWord, bool MatchCase, bool SelectionOnly, bool SearchUpwards) { THREAD_CHECK(); if (ValidStrW(Find)) { // int Max = -1; ssize_t FindLen = StrlenW(Find); ssize_t ReplaceLen = StrlenW(Replace); // size_t OldCursor = Cursor; ptrdiff_t First = -1; while (true) { ptrdiff_t Loc = MatchText(Find, MatchWord, MatchCase, SelectionOnly, SearchUpwards); if (First < 0) { First = Loc; } else if (Loc == First) { break; } if (Loc >= 0) { ssize_t OldSelStart = SelStart; ssize_t OldSelEnd = SelEnd; Delete(Loc, FindLen); Insert(Loc, Replace, ReplaceLen); SelStart = OldSelStart; SelEnd = OldSelEnd - FindLen + ReplaceLen; Cursor = Loc + ReplaceLen; } if (!All) { return Loc >= 0; } if (Loc < 0) break; } } return false; } ssize_t GTextView3::SeekLine(ssize_t Offset, GTextViewSeek Where) { THREAD_CHECK(); switch (Where) { case PrevLine: { for (; Offset > 0 && Text[Offset] != '\n'; Offset--) ; if (Offset > 0) Offset--; for (; Offset > 0 && Text[Offset] != '\n'; Offset--) ; if (Offset > 0) Offset++; break; } case NextLine: { for (; Offset < Size && Text[Offset] != '\n'; Offset++) ; Offset++; break; } case StartLine: { for (; Offset > 0 && Text[Offset] != '\n'; Offset--) ; if (Offset > 0) Offset++; break; } case EndLine: { for (; Offset < Size && Text[Offset] != '\n'; Offset++) ; break; } default: { LgiAssert(false); break; } } return Offset; } bool GTextView3::OnMultiLineTab(bool In) { bool Status = false; ssize_t Min = MIN(SelStart, SelEnd); ssize_t Max = MAX(SelStart, SelEnd), i; Min = SeekLine(Min, StartLine); int Ls = 0; GArray p; for (i=Min; i=0; i--) { if (In) { // <- ssize_t n = Indexes[i], Space = 0; for (; Space ssize_t Len = Indexes[i]; for (; Text[Len] != '\n' && Len Indexes[i]) { if (HardTabs) { char16 Tab[] = {'\t', 0}; Insert(Indexes[i], Tab, 1); Max++; } else { char16 *Sp = new char16[IndentSize]; if (Sp) { for (int n=0; nChanges.Length()) { UndoQue += UndoCur; UndoCur = NULL; } else { DeleteObj(UndoCur); } SelStart = Min; SelEnd = Cursor = Max; PourEnabled = true; PourText(Min, Max - Min); PourStyle(Min, Max - Min); d->SetDirty(Min, Max-Min); Invalidate(); Status = true; return Status; } void GTextView3::OnSetHidden(int Hidden) { } void GTextView3::OnPosChange() { static bool Processing = false; if (!Processing) { Processing = true; GLayout::OnPosChange(); GRect c = GetClient(); bool ScrollYNeeded = c.Y() < (Line.Length() * LineY); bool ScrollChange = ScrollYNeeded ^ (VScroll != NULL); if (ScrollChange) { // printf("%s:%i - SetScrollBars(%i)\n", _FL, ScrollYNeeded); SetScrollBars(false, ScrollYNeeded); } UpdateScrollBars(); if (GetWrapType() && d->PourX != X()) { d->PourX = X(); d->SetDirty(0, Size); } Processing = false; } } int GTextView3::WillAccept(List &Formats, GdcPt2 Pt, int KeyState) { for (char *s = Formats.First(); s; ) { if (!_stricmp(s, "text/uri-list") || !_stricmp(s, "text/html") || !_stricmp(s, "UniformResourceLocatorW")) { s = Formats.Next(); } else { // LgiTrace("Ignoring format '%s'\n", s); Formats.Delete(s); DeleteArray(s); s = Formats.Current(); } } return Formats.Length() ? DROPEFFECT_COPY : DROPEFFECT_NONE; } int GTextView3::OnDrop(GArray &Data, GdcPt2 Pt, int KeyState) { int Status = DROPEFFECT_NONE; for (unsigned i=0; iIsBinary()) { OsChar *e = (OsChar*) ((char*)Data->Value.Binary.Data + Data->Value.Binary.Length); OsChar *s = (OsChar*) Data->Value.Binary.Data; int len = 0; while (s < e && s[len]) { len++; } GAutoWString w ( (char16*)LgiNewConvertCp ( LGI_WideCharset, s, ( sizeof(OsChar) == 1 ? "utf-8" : LGI_WideCharset ), len * sizeof(*s) ) ); Insert(Cursor, w, len); Invalidate(); return DROPEFFECT_COPY; } } else if (dd.IsFileDrop()) { // We don't directly handle file drops... pass up to the parent for (GViewI *p = GetParent(); p; p = p->GetParent()) { GDragDropTarget *t = p->DropTarget(); if (t) { Status = t->OnDrop(Data, Pt, KeyState); break; } } } } return Status; } void GTextView3::OnCreate() { SetWindow(this); DropTarget(true); SetPulse(PULSE_TIMEOUT); } void GTextView3::OnEscape(GKey &K) { } bool GTextView3::OnMouseWheel(double l) { if (VScroll) { int64 NewPos = VScroll->Value() + (int)l; NewPos = limit(NewPos, 0, (ssize_t)GetLines()); VScroll->Value(NewPos); Invalidate(); } return true; } void GTextView3::OnFocus(bool f) { Invalidate(); } ssize_t GTextView3::HitText(int x, int y, bool Nearest) { if (!Text) return 0; bool Down = y >= 0; int Y = (VScroll) ? (int)VScroll->Value() : 0; GTextLine *l = Line.ItemAt(Y); y += (l) ? l->r.y1 : 0; while (l) { if (l->r.Overlap(x, y)) { // Over a line int At = x - l->r.x1; ssize_t Char = 0; GDisplayString Ds(Font, MapText(Text + l->Start, l->Len), l->Len, 0); Char = Ds.CharAt(At, Nearest ? LgiNearest : LgiTruncate); return l->Start + Char; } else if (y >= l->r.y1 && y <= l->r.y2) { // Click horizontally before of after line if (x < l->r.x1) { return l->Start; } else if (x > l->r.x2) { return l->Start + l->Len; } } l = (Down) ? Line.Next() : Line.Prev(); Y++; } // outside text area if (Down) { l = Line.Last(); if (l) { if (y > l->r.y2) { // end of document return Size; } } } return 0; } void GTextView3::Undo() { int Old = UndoQue.GetPos(); UndoQue.Undo(); if (Old && !UndoQue.GetPos()) { Dirty = false; SendNotify(GNotifyDocChanged); } } void GTextView3::Redo() { UndoQue.Redo(); } void GTextView3::DoContextMenu(GMouse &m) { GSubMenu RClick; GAutoString ClipText; { GClipBoard Clip(this); ClipText.Reset(NewStr(Clip.Text())); } #if LUIS_DEBUG RClick.AppendItem("Dump Layout", IDM_DUMP, true); RClick.AppendSeparator(); #endif GStyle *s = HitStyle(HitText(m.x, m.y, true)); if (s) { if (OnStyleMenu(s, &RClick)) { RClick.AppendSeparator(); } } RClick.AppendItem(LgiLoadString(L_TEXTCTRL_CUT, "Cut"), IDM_CUT, HasSelection()); RClick.AppendItem(LgiLoadString(L_TEXTCTRL_COPY, "Copy"), IDM_COPY, HasSelection()); RClick.AppendItem(LgiLoadString(L_TEXTCTRL_PASTE, "Paste"), IDM_PASTE, ClipText != 0); RClick.AppendSeparator(); RClick.AppendItem(LgiLoadString(L_TEXTCTRL_UNDO, "Undo"), IDM_UNDO, UndoQue.CanUndo()); RClick.AppendItem(LgiLoadString(L_TEXTCTRL_REDO, "Redo"), IDM_REDO, UndoQue.CanRedo()); RClick.AppendSeparator(); GMenuItem *i = RClick.AppendItem(LgiLoadString(L_TEXTCTRL_FIXED, "Fixed Width Font"), IDM_FIXED, true); if (i) i->Checked(GetFixedWidthFont()); i = RClick.AppendItem(LgiLoadString(L_TEXTCTRL_AUTO_INDENT, "Auto Indent"), IDM_AUTO_INDENT, true); if (i) i->Checked(AutoIndent); i = RClick.AppendItem(LgiLoadString(L_TEXTCTRL_SHOW_WHITESPACE, "Show Whitespace"), IDM_SHOW_WHITE, true); if (i) i->Checked(ShowWhiteSpace); i = RClick.AppendItem(LgiLoadString(L_TEXTCTRL_HARD_TABS, "Hard Tabs"), IDM_HARD_TABS, true); if (i) i->Checked(HardTabs); RClick.AppendItem(LgiLoadString(L_TEXTCTRL_INDENT_SIZE, "Indent Size"), IDM_INDENT_SIZE, true); RClick.AppendItem(LgiLoadString(L_TEXTCTRL_TAB_SIZE, "Tab Size"), IDM_TAB_SIZE, true); if (Environment) Environment->AppendItems(&RClick); int Id = 0; m.ToScreen(); switch (Id = RClick.Float(this, m)) { #if LUIS_DEBUG case IDM_DUMP: { int n=0; for (GTextLine *l=Line.First(); l; l=Line.Next(), n++) { LgiTrace("[%i] %i,%i (%s)\n", n, l->Start, l->Len, l->r.Describe()); char *s = WideToUtf8(Text + l->Start, l->Len); if (s) { LgiTrace("%s\n", s); DeleteArray(s); } } break; } #endif case IDM_FIXED: { SetFixedWidthFont(!GetFixedWidthFont()); SendNotify(GNotifyFixedWidthChanged); break; } case IDM_CUT: { Cut(); break; } case IDM_COPY: { Copy(); break; } case IDM_PASTE: { Paste(); break; } case IDM_UNDO: { Undo(); break; } case IDM_REDO: { Redo(); break; } case IDM_AUTO_INDENT: { AutoIndent = !AutoIndent; break; } case IDM_SHOW_WHITE: { ShowWhiteSpace = !ShowWhiteSpace; Invalidate(); break; } case IDM_HARD_TABS: { HardTabs = !HardTabs; break; } case IDM_INDENT_SIZE: { char s[32]; sprintf_s(s, sizeof(s), "%i", IndentSize); GInput i(this, s, "Indent Size:", "Text"); if (i.DoModal()) { IndentSize = atoi(i.GetStr()); } break; } case IDM_TAB_SIZE: { char s[32]; sprintf_s(s, sizeof(s), "%i", TabSize); GInput i(this, s, "Tab Size:", "Text"); if (i.DoModal()) { SetTabSize(atoi(i.GetStr())); } break; } default: { if (s) { OnStyleMenuClick(s, Id); } if (Environment) { Environment->OnMenu(this, Id, 0); } break; } } } bool GTextView3::OnStyleClick(GStyle *style, GMouse *m) { switch (style->Owner) { case STYLE_URL: { if ( (!m) || (m->Left() && m->Down() && m->Double()) ) { GString s(Text + style->Start, style->Len); if (s) OnUrl(s); return true; } break; } default: break; } return false; } bool GTextView3::OnStyleMenu(GStyle *style, GSubMenu *m) { switch (style->Owner) { case STYLE_URL: { GString s(Text + style->Start, style->Len); if (LIsValidEmail(s)) m->AppendItem(LgiLoadString(L_TEXTCTRL_EMAIL_TO, "New Email to..."), IDM_NEW, true); else m->AppendItem(LgiLoadString(L_TEXTCTRL_OPENURL, "Open URL"), IDM_OPEN, true); m->AppendItem(LgiLoadString(L_TEXTCTRL_COPYLINK, "Copy link location"), IDM_COPY_URL, true); return true; } default: break; } return false; } void GTextView3::OnStyleMenuClick(GStyle *style, int i) { switch (style->Owner) { case STYLE_URL: { GString s(Text + style->Start, style->Len); switch (i) { case IDM_NEW: case IDM_OPEN: { if (s) OnUrl(s); break; } case IDM_COPY_URL: { if (s) { GClipBoard Clip(this); Clip.Text(s); } break; } } break; } default: break; } } void GTextView3::OnMouseClick(GMouse &m) { bool Processed = false; m.x += ScrollX; if (m.Down()) { if (!m.IsContextMenu()) { Focus(true); ssize_t Hit = HitText(m.x, m.y, true); if (Hit >= 0) { SetCaret(Hit, m.Shift()); GStyle *s = HitStyle(Hit); if (s) Processed = OnStyleClick(s, &m); } if (!Processed && m.Double()) { d->WordSelectMode = Cursor; SelectWord(Cursor); } else { d->WordSelectMode = -1; } } else { DoContextMenu(m); return; } } if (!Processed) { Capture(m.Down()); } } int GTextView3::OnHitTest(int x, int y) { #ifdef WIN32 if (GetClient().Overlap(x, y)) { return HTCLIENT; } #endif return GView::OnHitTest(x, y); } void GTextView3::OnMouseMove(GMouse &m) { m.x += ScrollX; ssize_t Hit = HitText(m.x, m.y, true); if (IsCapturing()) { if (d->WordSelectMode < 0) { SetCaret(Hit, m.Left()); } else { ssize_t Min = Hit < d->WordSelectMode ? Hit : d->WordSelectMode; ssize_t Max = Hit > d->WordSelectMode ? Hit : d->WordSelectMode; for (SelStart = Min; SelStart > 0; SelStart--) { if (strchr(SelectWordDelim, Text[SelStart])) { SelStart++; break; } } for (SelEnd = Max; SelEnd < Size; SelEnd++) { if (strchr(SelectWordDelim, Text[SelEnd])) { break; } } Cursor = SelEnd; Invalidate(); } } } LgiCursor GTextView3::GetCursor(int x, int y) { GRect c = GetClient(); c.Offset(-c.x1, -c.y1); GStyle *s = NULL; if (c.Overlap(x, y)) { ssize_t Hit = HitText(x, y, true); s = HitStyle(Hit); } return s ? s->Cursor : LCUR_Ibeam; } int GTextView3::GetColumn() { int x = 0; GTextLine *l = GetTextLine(Cursor); if (l) { for (ssize_t i=l->Start; i> 1); m.Target = this; DoContextMenu(m); } else if (k.IsChar) { switch (k.vkey) { default: { // process single char input if ( !GetReadOnly() && ( (k.c16 >= ' ' || k.c16 == VK_TAB) && k.c16 != 127 ) ) { if (k.Down()) { // letter/number etc if (SelStart >= 0) { bool MultiLine = false; if (k.c16 == VK_TAB) { size_t Min = MIN(SelStart, SelEnd), Max = MAX(SelStart, SelEnd); for (size_t i=Min; iLen : 0; if (l && k.c16 == VK_TAB && (!HardTabs || IndentSize != TabSize)) { int x = GetColumn(); int Add = IndentSize - (x % IndentSize); if (HardTabs && ((x + Add) % TabSize) == 0) { int Rx = x; size_t Remove; for (Remove = Cursor; Text[Remove - 1] == ' ' && Rx % TabSize != 0; Remove--, Rx--); ssize_t Chars = (ssize_t)Cursor - Remove; Delete(Remove, Chars); Insert(Remove, &k.c16, 1); Cursor = Remove + 1; Invalidate(); } else { char16 *Sp = new char16[Add]; if (Sp) { for (int n=0; nLen : 0; SetCaret(Cursor + Add, false, Len != NewLen - 1); } DeleteArray(Sp); } } } else { char16 In = k.GetChar(); if (In == '\t' && k.Shift() && Cursor > 0) { l = GetTextLine(Cursor); if (Cursor > l->Start) { if (Text[Cursor-1] == '\t') { Delete(Cursor - 1, 1); SetCaret(Cursor, false, false); } else if (Text[Cursor-1] == ' ') { ssize_t Start = (ssize_t)Cursor - 1; while (Start >= l->Start && strchr(" \t", Text[Start-1])) Start--; int Depth = SpaceDepth(Text + Start, Text + Cursor); int NewDepth = Depth - (Depth % IndentSize); if (NewDepth == Depth && NewDepth > 0) NewDepth -= IndentSize; int Use = 0; while (SpaceDepth(Text + Start, Text + Start + Use + 1) < NewDepth) Use++; Delete(Start + Use, Cursor - Start - Use); SetCaret(Start + Use, false, false); } } } else if (In && Insert(Cursor, &In, 1)) { l = GetTextLine(Cursor); size_t NewLen = (l) ? l->Len : 0; SetCaret(Cursor + 1, false, Len != NewLen - 1); } } } return true; } break; } case VK_RETURN: { if (GetReadOnly()) break; if (k.Down() && k.IsChar) { OnEnter(k); } return true; break; } case VK_BACKSPACE: { if (GetReadOnly()) break; if (k.Ctrl()) { // Ctrl+H } else if (k.Down()) { if (SelStart >= 0) { // delete selection DeleteSelection(); } else { char Del = Cursor > 0 ? Text[Cursor-1] : 0; if (Del == ' ' && (!HardTabs || IndentSize != TabSize)) { // Delete soft tab int x = GetColumn(); int Max = x % IndentSize; if (Max == 0) Max = IndentSize; ssize_t i; for (i=Cursor-1; i>=0; i--) { if (Max-- <= 0 || Text[i] != ' ') { i++; break; } } if (i < 0) i = 0; if (i < Cursor - 1) { ssize_t Del = (ssize_t)Cursor - i; Delete(i, Del); // SetCursor(i, false, false); // Invalidate(); break; } } else if (Del == '\t' && HardTabs && IndentSize != TabSize) { int x = GetColumn(); Delete(--Cursor, 1); for (int c=GetColumn(); c 0) { Delete(Cursor - 1, 1); } } } return true; break; } } } else // not a char { switch (k.vkey) { case VK_TAB: return true; case VK_RETURN: { return !GetReadOnly(); } case VK_BACKSPACE: { if (!GetReadOnly()) { if (k.Alt()) { if (k.Down()) { if (k.Ctrl()) { Redo(); } else { Undo(); } } } else if (k.Ctrl()) { if (k.Down()) { ssize_t Start = Cursor; while (IsWhiteSpace(Text[Cursor-1]) && Cursor > 0) Cursor--; while (!IsWhiteSpace(Text[Cursor-1]) && Cursor > 0) Cursor--; Delete(Cursor, Start - Cursor); Invalidate(); } } return true; } break; } case VK_F3: { if (k.Down()) { DoFindNext(); } return true; break; } case VK_LEFT: { if (k.Down()) { if (SelStart >= 0 && !k.Shift()) { SetCaret(MIN(SelStart, SelEnd), false); } else if (Cursor > 0) { ssize_t n = Cursor; #ifdef MAC if (k.System()) { goto Jump_StartOfLine; } else #endif if (k.Ctrl() || k.Alt()) { // word move/select bool StartWhiteSpace = IsWhiteSpace(Text[n]); bool LeftWhiteSpace = n > 0 && IsWhiteSpace(Text[n-1]); if (!StartWhiteSpace || Text[n] == '\n') { n--; } // Skip ws for (; n > 0 && strchr(" \t", Text[n]); n--) ; if (Text[n] == '\n') { n--; } else if (!StartWhiteSpace || !LeftWhiteSpace) { if (IsDelimiter(Text[n])) { for (; n > 0 && IsDelimiter(Text[n]); n--); } else { for (; n > 0; n--) { //IsWordBoundry(Text[n]) if (IsWhiteSpace(Text[n]) || IsDelimiter(Text[n])) { break; } } } } if (n > 0) n++; } else { // single char n--; } SetCaret(n, k.Shift()); } } return true; break; } case VK_RIGHT: { if (k.Down()) { if (SelStart >= 0 && !k.Shift()) { SetCaret(MAX(SelStart, SelEnd), false); } else if (Cursor < Size) { ssize_t n = Cursor; #ifdef MAC if (k.System()) { goto Jump_EndOfLine; } else #endif if (k.Ctrl() || k.Alt()) { // word move/select if (IsWhiteSpace(Text[n])) { for (; nStart, Cursor-l->Start); int ScreenX = CurLine.X(); GDisplayString PrevLine(Font, Text + Prev->Start, Prev->Len); ssize_t CharX = PrevLine.CharAt(ScreenX); SetCaret(Prev->Start + MIN(CharX, Prev->Len), k.Shift()); } } } return true; break; } case VK_DOWN: { if (k.Alt()) return false; if (k.Down()) { #ifdef MAC if (k.Ctrl()) goto GTextView3_PageDown; #endif GTextLine *l = GetTextLine(Cursor); if (l) { GTextLine *Next = Line.Next(); if (Next) { GDisplayString CurLine(Font, Text + l->Start, Cursor-l->Start); int ScreenX = CurLine.X(); GDisplayString NextLine(Font, Text + Next->Start, Next->Len); ssize_t CharX = NextLine.CharAt(ScreenX); SetCaret(Next->Start + MIN(CharX, Next->Len), k.Shift()); } } } return true; break; } case VK_END: { if (k.Down()) { if (k.Ctrl()) { SetCaret(Size, k.Shift()); } else { #ifdef MAC Jump_EndOfLine: #endif GTextLine *l = GetTextLine(Cursor); if (l) { SetCaret(l->Start + l->Len, k.Shift()); } } } return true; break; } case VK_HOME: { if (k.Down()) { if (k.Ctrl()) { SetCaret(0, k.Shift()); } else { #ifdef MAC Jump_StartOfLine: #endif GTextLine *l = GetTextLine(Cursor); if (l) { char16 *Line = Text + l->Start; char16 *s; char16 SpTab[] = {' ', '\t', 0}; for (s = Line; (SubtractPtr(s,Line) < l->Len) && StrchrW(SpTab, *s); s++); ssize_t Whitespace = SubtractPtr(s, Line); if (l->Start + Whitespace == Cursor) { SetCaret(l->Start, k.Shift()); } else { SetCaret(l->Start + Whitespace, k.Shift()); } } } } return true; break; } case VK_PAGEUP: { #ifdef MAC GTextView3_PageUp: #endif if (k.Down()) { GTextLine *l = GetTextLine(Cursor); if (l) { int DisplayLines = Y() / LineY; ssize_t CurLine = Line.IndexOf(l); GTextLine *New = Line.ItemAt(MAX(CurLine - DisplayLines, 0)); if (New) { SetCaret(New->Start + MIN(Cursor - l->Start, New->Len), k.Shift()); } } } return true; break; } case VK_PAGEDOWN: { #ifdef MAC GTextView3_PageDown: #endif if (k.Down()) { GTextLine *l = GetTextLine(Cursor); if (l) { int DisplayLines = Y() / LineY; ssize_t CurLine = Line.IndexOf(l); GTextLine *New = Line.ItemAt(MIN(CurLine + DisplayLines, (ssize_t)GetLines()-1)); if (New) { SetCaret(New->Start + MIN(Cursor - l->Start, New->Len), k.Shift()); } } } return true; break; } case VK_INSERT: { if (k.Down()) { if (k.Ctrl()) { Copy(); } else if (k.Shift()) { if (!GetReadOnly()) { Paste(); } } } return true; break; } case VK_DELETE: { if (!GetReadOnly()) { if (k.Down()) { if (SelStart >= 0) { if (k.Shift()) { Cut(); } else { DeleteSelection(); } } else if (Cursor < Size && Delete(Cursor, 1)) { Invalidate(); } } return true; } break; } default: { if (k.c16 == 17) break; if (k.Modifier() && !k.Alt()) { switch (k.GetChar()) { case 0xbd: // Ctrl+'-' { if (k.Down() && Font->PointSize() > 1) { Font->PointSize(Font->PointSize() - 1); OnFontChange(); Invalidate(); } break; } case 0xbb: // Ctrl+'+' { if (k.Down() && Font->PointSize() < 100) { Font->PointSize(Font->PointSize() + 1); OnFontChange(); Invalidate(); } break; } case 'a': case 'A': { if (k.Down()) { // select all SelStart = 0; SelEnd = Size; Invalidate(); } return true; break; } case 'y': case 'Y': { if (!GetReadOnly()) { if (k.Down()) { Redo(); } return true; } break; } case 'z': case 'Z': { if (!GetReadOnly()) { if (k.Down()) { if (k.Shift()) { Redo(); } else { Undo(); } } return true; } break; } case 'x': case 'X': { if (!GetReadOnly()) { if (k.Down()) { Cut(); } return true; } break; } case 'c': case 'C': { if (k.Shift()) return false; if (k.Down()) Copy(); return true; break; } case 'v': case 'V': { if (!k.Shift() && !GetReadOnly()) { if (k.Down()) { Paste(); } return true; } break; } case 'f': { if (k.Down()) { DoFind(); } return true; break; } case 'g': case 'G': { if (k.Down()) { DoGoto(); } return true; break; } case 'h': case 'H': { if (k.Down()) { DoReplace(); } return true; break; } case 'u': case 'U': { if (!GetReadOnly()) { if (k.Down()) { DoCase(k.Shift()); } return true; } break; } case VK_RETURN: { if (!GetReadOnly() && !k.Shift()) { if (k.Down()) { OnEnter(k); } return true; } break; } } } break; } } } return false; } void GTextView3::OnEnter(GKey &k) { // enter if (SelStart >= 0) { DeleteSelection(); } char16 InsertStr[256] = {'\n', 0}; GTextLine *CurLine = GetTextLine(Cursor); if (CurLine && AutoIndent) { int WsLen = 0; for (; WsLen < CurLine->Len && WsLen < (Cursor - CurLine->Start) && strchr(" \t", Text[CurLine->Start + WsLen]); WsLen++); if (WsLen > 0) { memcpy(InsertStr+1, Text+CurLine->Start, WsLen * sizeof(char16)); InsertStr[WsLen+1] = 0; } } if (Insert(Cursor, InsertStr, StrlenW(InsertStr))) { SetCaret(Cursor + StrlenW(InsertStr), false, true); } } int GTextView3::TextWidth(GFont *f, char16 *s, int Len, int x, int Origin) { int w = x; int Size = f->TabSize(); for (char16 *c = s; SubtractPtr(c, s) < Len; ) { if (*c == 9) { w = ((((w-Origin) + Size) / Size) * Size) + Origin; c++; } else { char16 *e; for (e = c; SubtractPtr(e, s) < Len && *e != 9; e++); GDisplayString ds(f, c, SubtractPtr(e, c)); w += ds.X(); c = e; } } return w - x; } int GTextView3::ScrollYLine() { return (VScroll) ? (int)VScroll->Value() : 0; } int GTextView3::ScrollYPixel() { return ScrollYLine() * LineY; } GRect GTextView3::DocToScreen(GRect r) { r.Offset(0, d->rPadding.y1 - ScrollYPixel()); return r; } void GTextView3::OnPaintLeftMargin(GSurface *pDC, GRect &r, GColour &colour) { pDC->Colour(colour); pDC->Rectangle(&r); } void GTextView3::OnPaint(GSurface *pDC) { #if LGI_EXCEPTIONS try { #endif #if PROFILE_PAINT char s[256]; sprintf_s(s, sizeof(s), "%p::OnPaint Lines=%i Sz=%i", this, (int)Line.Length(), (int)Size); GProfile Prof(s); #endif if (d->LayoutDirty) { #if PROFILE_PAINT Prof.Add("PourText"); #endif PourText(d->DirtyStart, d->DirtyLen); #if PROFILE_PAINT Prof.Add("PourStyle"); #endif PourStyle(d->DirtyStart, d->DirtyLen); } #if PROFILE_PAINT Prof.Add("Setup"); #endif GRect r = GetClient(); r.x2 += ScrollX; int Ox, Oy; pDC->GetOrigin(Ox, Oy); pDC->SetOrigin(Ox+ScrollX, Oy); #if 0 // Coverage testing... pDC->Colour(Rgb24(255, 0, 255), 24); pDC->Rectangle(); #endif GSurface *pOut = pDC; bool DrawSel = false; bool HasFocus = Focus(); GColour SelectedText(HasFocus ? LC_FOCUS_SEL_FORE : LC_NON_FOCUS_SEL_FORE, 24); GColour SelectedBack(HasFocus ? LC_FOCUS_SEL_BACK : LC_NON_FOCUS_SEL_BACK, 24); GCss::ColorDef ForeDef, BkDef; if (GetCss()) { ForeDef = GetCss()->Color(); BkDef = GetCss()->BackgroundColor(); } GColour Fore(ForeDef.Type == GCss::ColorRgb ? Rgb32To24(ForeDef.Rgb32) : LC_TEXT, 24); GColour Back ( /*!ReadOnly &&*/ BkDef.Type == GCss::ColorRgb ? Rgb32To24(BkDef.Rgb32) : Enabled() ? LC_WORKSPACE : LC_MED, 24 ); // GColour Whitespace = Fore.Mix(Back, 0.85f); if (!Enabled()) { Fore.Set(LC_LOW, 24); Back.Set(LC_MED, 24); } #ifdef DOUBLE_BUFFER_PAINT GMemDC *pMem = new GMemDC; pOut = pMem; #endif if (Text && Font #ifdef DOUBLE_BUFFER_PAINT && pMem && pMem->Create(r.X()-d->rPadding.x1, LineY, GdcD->GetBits()) #endif ) { ssize_t SelMin = MIN(SelStart, SelEnd); ssize_t SelMax = MAX(SelStart, SelEnd); // font properties Font->Colour(Fore, Back); // Font->WhitespaceColour(Whitespace); Font->Transparent(false); // draw margins pDC->Colour(PAINT_BORDER); // top margin pDC->Rectangle(0, 0, r.x2, d->rPadding.y1-1); // left margin { GRect LeftMargin(0, d->rPadding.y1, d->rPadding.x1-1, r.y2); OnPaintLeftMargin(pDC, LeftMargin, PAINT_BORDER); } // draw lines of text int k = ScrollYLine(); GTextLine *l=Line.ItemAt(k); int Dy = (l) ? -l->r.y1 : 0; ssize_t NextSelection = (SelStart != SelEnd) ? SelMin : -1; // offset where selection next changes if (l && SelStart >= 0 && SelStart < l->Start && SelEnd > l->Start) { // start of visible area is in selection // init to selection colour DrawSel = true; Font->Colour(SelectedText, SelectedBack); NextSelection = SelMax; } StyleIter Si = Style.begin(); GStyle *NextStyle = GetNextStyle(Si, (l) ? l->Start : 0); DocOffset = (l) ? l->r.y1 : 0; #if PROFILE_PAINT Prof.Add("foreach Line loop"); #endif // loop through all visible lines int y = d->rPadding.y1; for (; l && l->r.y1+Dy < r.Y(); l=Line.Next()) { GRect Tr = l->r; #ifdef DOUBLE_BUFFER_PAINT Tr.Offset(-Tr.x1, -Tr.y1); #else Tr.Offset(0, y - Tr.y1); #endif //GRect OldTr = Tr; // deal with selection change on beginning of line if (NextSelection == l->Start) { // selection change DrawSel = !DrawSel; NextSelection = (NextSelection == SelMin) ? SelMax : -1; } if (DrawSel) { Font->Colour(SelectedText, SelectedBack); } else { GColour fore = l->c.IsValid() ? l->c : Fore; GColour back = l->Back.IsValid() ? l->Back : Back; Font->Colour(fore, back); } // How many chars on this line have we // processed so far: ssize_t Done = 0; bool LineHasSelection = NextSelection >= l->Start && NextSelection < l->Start + l->Len; // Fractional pixels we have moved so far: int MarginF = d->rPadding.x1 << GDisplayString::FShift; int FX = MarginF; int FY = Tr.y1 << GDisplayString::FShift; // loop through all sections of similar text on a line while (Done < l->Len) { // decide how big this block is int RtlTrailingSpace = 0; ssize_t Cur = l->Start + Done; ssize_t Block = l->Len - Done; // check for style change if (NextStyle && (ssize_t)NextStyle->End() <= l->Start) NextStyle = GetNextStyle(Si); if (NextStyle) { // start if (l->Overlap(NextStyle->Start) && NextStyle->Start > Cur && NextStyle->Start - Cur < Block) { Block = NextStyle->Start - Cur; } // end ssize_t StyleEnd = NextStyle->Start + NextStyle->Len; if (l->Overlap(StyleEnd) && StyleEnd > Cur && StyleEnd - Cur < Block) { Block = StyleEnd - Cur; } } // check for next selection change // this may truncate the style if (NextSelection > Cur && NextSelection - Cur < Block) { Block = NextSelection - Cur; } LgiAssert(Block != 0); // sanity check if (NextStyle && // There is a style (Cur < SelMin || Cur >= SelMax) && // && we're not drawing a selection block Cur >= NextStyle->Start && // && we're inside the styled area Cur < NextStyle->Start+NextStyle->Len) { GFont *Sf = NextStyle->Font ? NextStyle->Font : Font; if (Sf) { // draw styled text if (NextStyle->Fore.IsValid()) Sf->Fore(NextStyle->Fore); if (NextStyle->Back.IsValid()) Sf->Back(NextStyle->Back); else if (l->Back.IsValid()) Sf->Back(l->Back); else Sf->Back(Back); Sf->Transparent(false); LgiAssert(l->Start + Done >= 0); GDisplayString Ds( Sf, MapText(Text + (l->Start + Done), Block, RtlTrailingSpace != 0), Block + RtlTrailingSpace); Ds.SetDrawOffsetF(FX - MarginF); Ds.ShowVisibleTab(ShowWhiteSpace); Ds.FDraw(pOut, FX, FY, 0, LineHasSelection); if (NextStyle->Decor == GCss::TextDecorSquiggle) { pOut->Colour(NextStyle->DecorColour.c24(), 24); int x = FX >> GDisplayString::FShift; int End = x + Ds.X(); while (x < End) { pOut->Set(x, Tr.y2-(x%2)); x++; } } FX += Ds.FX(); GColour fore = l->c.IsValid() ? l->c : Fore; GColour back = l->Back.IsValid() ? l->Back : Back; Sf->Colour(fore, back); } else LgiAssert(0); } else { // draw a block of normal text LgiAssert(l->Start + Done >= 0); GDisplayString Ds( Font, MapText(Text + (l->Start + Done), Block, RtlTrailingSpace != 0), Block + RtlTrailingSpace); Ds.SetDrawOffsetF(FX - MarginF); Ds.ShowVisibleTab(ShowWhiteSpace); Ds.FDraw(pOut, FX, FY, 0, LineHasSelection); FX += Ds.FX(); } if (NextStyle && Cur+Block >= NextStyle->Start+NextStyle->Len) { // end of this styled block NextStyle = GetNextStyle(Si); } if (NextSelection == Cur+Block) { // selection change DrawSel = !DrawSel; if (DrawSel) { Font->Colour(SelectedText, SelectedBack); } else { GColour fore = l->c.IsValid() ? l->c : Fore; GColour back = l->Back.IsValid() ? l->Back : Back; Font->Colour(fore, back); } NextSelection = (NextSelection == SelMin) ? SelMax : -1; } Done += Block + RtlTrailingSpace; } // end block loop Tr.x1 = FX >> GDisplayString::FShift; // eol processing ssize_t EndOfLine = l->Start+l->Len; if (EndOfLine >= SelMin && EndOfLine < SelMax) { // draw the '\n' at the end of the line as selected // GColour bk = Font->Back(); pOut->Colour(Font->Back()); pOut->Rectangle(Tr.x2, Tr.y1, Tr.x2+7, Tr.y2); Tr.x2 += 7; } else Tr.x2 = Tr.x1; // draw any space after text pOut->Colour(PAINT_AFTER_LINE); pOut->Rectangle(Tr.x2, Tr.y1, r.x2, Tr.y2); // cursor? if (HasFocus) { // draw the cursor if on this line if (Cursor >= l->Start && Cursor <= l->Start+l->Len) { CursorPos.ZOff(1, LineY-1); ssize_t At = Cursor-l->Start; GDisplayString Ds(Font, MapText(Text+l->Start, At), At); Ds.ShowVisibleTab(ShowWhiteSpace); int CursorX = Ds.X(); CursorPos.Offset(d->rPadding.x1 + CursorX, Tr.y1); if (CanScrollX) { // Cursor on screen check GRect Scr = GetClient(); Scr.Offset(ScrollX, 0); GRect Cur = CursorPos; if (Cur.x2 > Scr.x2 - 5) // right edge check { ScrollX = ScrollX + Cur.x2 - Scr.x2 + 40; Invalidate(); } else if (Cur.x1 < Scr.x1 && ScrollX > 0) { ScrollX = MAX(0, Cur.x1 - 40); Invalidate(); } } if (Blink) { GRect c = CursorPos; #ifdef DOUBLE_BUFFER_PAINT c.Offset(-d->rPadding.x1, -y); #endif pOut->Colour((!ReadOnly) ? Fore : GColour(192, 192, 192)); pOut->Rectangle(&c); } #if WINNATIVE HIMC hIMC = ImmGetContext(Handle()); if (hIMC) { COMPOSITIONFORM Cf; Cf.dwStyle = CFS_POINT; Cf.ptCurrentPos.x = CursorPos.x1; Cf.ptCurrentPos.y = CursorPos.y1; ImmSetCompositionWindow(hIMC, &Cf); ImmReleaseContext(Handle(), hIMC); } #endif } } #if DRAW_LINE_BOXES { uint Style = pDC->LineStyle(GSurface::LineAlternate); GColour Old = pDC->Colour(GColour::Red); pDC->Box(&OldTr); pDC->Colour(Old); pDC->LineStyle(Style); GString s; s.Printf("%i, %i", Line.IndexOf(l), l->Start); GDisplayString ds(SysFont, s); SysFont->Transparent(true); ds.Draw(pDC, OldTr.x2 + 2, OldTr.y1); } #endif #ifdef DOUBLE_BUFFER_PAINT // dump to screen pDC->Blt(d->rPadding.x1, y, pOut); #endif y += LineY; } // end of line loop // draw any space under the lines if (y <= r.y2) { pDC->Colour(Back); // pDC->Colour(GColour(255, 0, 255)); pDC->Rectangle(d->rPadding.x1, y, r.x2, r.y2); } #ifdef DOUBLE_BUFFER_PAINT DeleteObj(pMem); #endif } else { // default drawing: nothing pDC->Colour(Back); pDC->Rectangle(&r); } // _PaintTime = LgiCurrentTime() - StartTime; #ifdef PAINT_DEBUG if (GetNotify()) { char s[256]; sprintf_s(s, sizeof(s), "Pour:%i Style:%i Paint:%i ms", _PourTime, _StyleTime, _PaintTime); GMessage m = CreateMsg(DEBUG_TIMES_MSG, 0, (int)s); GetNotify()->OnEvent(&m); } #endif // printf("PaintTime: %ims\n", _PaintTime); #if LGI_EXCEPTIONS } catch (...) { LgiMsg(this, "GTextView3::OnPaint crashed.", "Lgi"); } #endif } GMessage::Result GTextView3::OnEvent(GMessage *Msg) { switch (MsgCode(Msg)) { case M_TEXT_UPDATE_NAME: { if (d->Lock(_FL)) { Name(d->SetName); d->SetName.Empty(); d->Unlock(); } break; } case M_TEXTVIEW_FIND: { if (InThread()) DoFindNext(); else LgiTrace("%s:%i - Not in thread.\n", _FL); break; } case M_TEXTVIEW_REPLACE: { // DoReplace(); break; } case M_CUT: { Cut(); break; } case M_COPY: { Copy(); break; } case M_PASTE: { Paste(); break; } #if defined WIN32 case WM_GETTEXTLENGTH: { return Size; } case WM_GETTEXT: { int Chars = (int)MsgA(Msg); char *Out = (char*)MsgB(Msg); if (Out) { char *In = (char*)LgiNewConvertCp(LgiAnsiToLgiCp(), NameW(), LGI_WideCharset, Chars); if (In) { int Len = (int)strlen(In); memcpy(Out, In, Len); DeleteArray(In); return Len; } } return 0; } /* This is broken... the IME returns garbage in the buffer. :( case WM_IME_COMPOSITION: { if (Msg->b & GCS_RESULTSTR) { HIMC hIMC = ImmGetContext(Handle()); if (hIMC) { int Size = ImmGetCompositionString(hIMC, GCS_RESULTSTR, NULL, 0); char *Buf = new char[Size]; if (Buf) { ImmGetCompositionString(hIMC, GCS_RESULTSTR, Buf, Size); char16 *Utf = (char16*)LgiNewConvertCp(LGI_WideCharset, Buf, LgiAnsiToLgiCp(), Size); if (Utf) { Insert(Cursor, Utf, StrlenW(Utf)); DeleteArray(Utf); } DeleteArray(Buf); } ImmReleaseContext(Handle(), hIMC); } return 0; } break; } */ #endif } return GLayout::OnEvent(Msg); } int GTextView3::OnNotify(GViewI *Ctrl, int Flags) { if (Ctrl->GetId() == IDC_VSCROLL && VScroll) { if (Flags == GNotifyScrollBar_Create) { UpdateScrollBars(); } Invalidate(); } return 0; } void GTextView3::OnPulse() { if (!ReadOnly) { uint64 Now = LgiCurrentTime(); if (!BlinkTs) BlinkTs = Now; else if (Now - BlinkTs > CURSOR_BLINK) { Blink = !Blink; GRect p = CursorPos; p.Offset(-ScrollX, 0); Invalidate(&p); BlinkTs = Now; } } if (PartialPour) PourText(Size, 0); } void GTextView3::OnUrl(char *Url) { if (Environment) Environment->OnNavigate(this, Url); else { GUri u(Url); bool Email = LIsValidEmail(Url); const char *Proto = Email ? "mailto" : u.Protocol; GString App = LGetAppForProtocol(Proto); if (App) LgiExecute(App, Url); else LgiMsg(this, "Failed to find application for protocol '%s'", "Error", MB_OK, Proto); } } bool GTextView3::OnLayout(GViewLayoutInfo &Inf) { Inf.Width.Min = 32; Inf.Width.Max = -1; Inf.Height.Min = (Font ? Font->GetHeight() : 18) + 4; Inf.Height.Max = -1; return true; } /////////////////////////////////////////////////////////////////////////////// class GTextView3_Factory : public GViewFactory { GView *NewView(const char *Class, GRect *Pos, const char *Text) { if (_stricmp(Class, "GTextView3") == 0) { return new GTextView3(-1, 0, 0, 2000, 2000); } return 0; } } TextView3_Factory; diff --git a/src/common/Text/GUtf8.cpp b/src/common/Text/GUtf8.cpp --- a/src/common/Text/GUtf8.cpp +++ b/src/common/Text/GUtf8.cpp @@ -1,262 +1,262 @@ #include "Lgi.h" ///////////////////////////////////////////////////////////////////////////// static bool Warn = true; GUtf8Ptr::GUtf8Ptr(const void *p) { - Ptr = (uint8*)p; + Ptr = (uint8_t*)p; } GUtf8Ptr::operator int32() { - uint8 *p = Ptr; + uint8_t *p = Ptr; ssize_t l = 6; return LgiUtf8To32(p, l); } void GUtf8Ptr::Add(wchar_t c) { ssize_t l = 6; LgiUtf32To8(c, Ptr, l); } GUtf8Ptr &GUtf8Ptr::operator++() { if (IsUtf8_Lead(*Ptr)) { Ptr++; while (IsUtf8_Trail(*Ptr)) { Ptr++; } } else { if (IsUtf8_Trail(*Ptr)) { if (Warn) { Warn = false; LgiAssert(!"Invalid UTF"); } } Ptr++; } return *this; } GUtf8Ptr &GUtf8Ptr::operator--() { Ptr--; if (IsUtf8_Trail(*Ptr)) { Ptr--; while (IsUtf8_Trail(*Ptr)) { Ptr--; } LgiAssert(IsUtf8_Lead(*Ptr)); } else { LgiAssert((*Ptr & 0x80) == 0); } return *this; } GUtf8Ptr &GUtf8Ptr::operator++(const int i) { if (IsUtf8_Lead(*Ptr)) { Ptr++; while (IsUtf8_Trail(*Ptr)) { Ptr++; } } else { if (IsUtf8_Trail(*Ptr)) { if (Warn) { Warn = false; LgiAssert(!"Invalid UTF"); } } Ptr++; } return *this; } GUtf8Ptr &GUtf8Ptr::operator--(const int i) { Ptr--; if (IsUtf8_Trail(*Ptr)) { Ptr--; while (IsUtf8_Trail(*Ptr)) { Ptr--; } LgiAssert(IsUtf8_Lead(*Ptr)); } else { LgiAssert((*Ptr & 0x80) == 0); } return *this; } GUtf8Ptr &GUtf8Ptr::operator += (ssize_t n) { while (*Ptr && n-- > 0) { (*this)++; } return *this; } GUtf8Ptr &GUtf8Ptr::operator-=(ssize_t n) { while (*Ptr && n-- > 0) { (*this)--; } return *this; } int GUtf8Ptr::GetBytes() { return (int)strlen((char*)Ptr); } int GUtf8Ptr::GetChars() { int Count = 0; - uint8 *p = Ptr; + uint8_t *p = Ptr; while (*p) { if (IsUtf8_Lead(*p)) { p++; while (IsUtf8_Trail(*p)) { p++; } } else { p++; } Count++; } return Count; } ///////////////////////////////////////////////////////////////////////////// GUtf8Str::GUtf8Str(char *utf, int bytes, bool Copy) { Own = Copy; - Ptr = Start = Copy ? (uint8*)NewStr(utf) : (uint8*)utf; + Ptr = Start = Copy ? (uint8_t*)NewStr(utf) : (uint8_t*)utf; End = bytes >= 0 ? Start + bytes : 0; Cur = Start; } GUtf8Str::GUtf8Str(wchar_t *wide, int chars) { Own = true; - Start = (uint8*)WideToUtf8(wide); + Start = (uint8_t*)WideToUtf8(wide); End = chars >= 0 ? Start + chars : 0; Cur = Start; } GUtf8Str::~GUtf8Str() { Empty(); } void GUtf8Str::Empty() { if (Own) { DeleteArray(Start); } else Start = 0; Cur = (char*)0; End = 0; Own = false; } GUtf8Str &GUtf8Str::operator =(char *s) { Empty(); - Start = (uint8*)s; + Start = (uint8_t*)s; Cur = Start; End = 0; return *this; } wchar_t *GUtf8Str::ToWide() { if (End) { size_t Len = End - Ptr; if (Len > 0) { return Utf8ToWide((char*)Ptr, (int)Len); } } else { return Utf8ToWide((char*)Ptr); } return 0; } bool GUtf8Str::Valid() { if (!Start || !Ptr) return false; if (Ptr < Start) return false; if (End && Ptr > End) return false; return true; } bool GUtf8Str::IsStart() { return !Ptr || Ptr == Start; } bool GUtf8Str::IsEnd() { if (End) return Ptr >= End; return !Ptr || *Ptr == 0; } diff --git a/src/common/Text/GXmlTree.cpp b/src/common/Text/GXmlTree.cpp --- a/src/common/Text/GXmlTree.cpp +++ b/src/common/Text/GXmlTree.cpp @@ -1,1587 +1,1587 @@ #include #include #include #include "Lgi.h" #include "GXmlTree.h" #include "GVariant.h" #include "GToken.h" static char GXmlHeader[] = "\n"; ////////////////////////////////////////////////////////////////////////////// int _Pools = 0; int _Normals = 0; char *GXmlAlloc::Alloc(const char *s, ssize_t len) { if (!s) return NULL; if (len < 0) len = (int)strlen(s); ssize_t bytes = len + 1; char *p = (char*) Alloc(LGI_ALLOC_ALIGN(bytes)); if (!p) return 0; memcpy(p, s, len); p[len] = 0; return p; } class XmlPoolAlloc : public GXmlAlloc { struct Block { size_t Used, Len; char *Ptr; Block() { Used = Len = 0; Ptr = 0; } ~Block() { DeleteArray(Ptr); } bool Has(size_t Bytes) { return Used + Bytes <= Len; } }; GArray Mem; public: XmlPoolAlloc() { _Pools++; // LgiTrace("%p::XmlPoolAlloc _Pools=%i, Refs=%i\n", this, _Pools, _GetCount()); } ~XmlPoolAlloc() { _Pools--; // LgiTrace("%p::~XmlPoolAlloc _Pools=%i, Refs=%i\n", this, _Pools, _GetCount()); } void *Alloc(size_t Size) { Block *b = 0; int BlockSize = 32 << 10; if (Mem.Length() == 0 || !Mem[Mem.Length()-1].Has(Size)) { // New block b = &Mem.New(); b->Len = BlockSize; b->Used = 0; b->Ptr = new char[b->Len]; } else b = &Mem[Mem.Length()-1]; char *p = b->Ptr + b->Used; b->Used += Size; return p; } void Free(void *Ptr) { } }; class XmlNormalAlloc : public GXmlAlloc { public: XmlNormalAlloc() { _Normals++; } ~XmlNormalAlloc() { _Normals--; } void *Alloc(size_t Size) { return new char[Size]; } void Free(void *Ptr) { delete [] ((char*)Ptr); } }; ////////////////////////////////////////////////////////////////////////////// class GXmlTreePrivate { public: GXmlFactory *Factory; GXmlTag *Current; GStreamI *File; GString Error; int Flags; LHashTbl,char16> Entities; LHashTbl,bool> NoChildTags; GArray Buf; Progress *Prog; char *StyleFile; char *StyleType; bool NoDom() { return TestFlag(Flags, GXT_NO_DOM); } GXmlTreePrivate() { Factory = 0; File = 0; Flags = 0; StyleFile = 0; StyleType = 0; Current = NULL; Prog = NULL; Entities.Add("lt", '<'); Entities.Add("gt", '>'); Entities.Add("amp", '&'); Entities.Add("quot", '\"'); Entities.Add("apos", '\''); } ~GXmlTreePrivate() { DeleteArray(StyleType); DeleteArray(StyleFile); } }; ////////////////////////////////////////////////////////////////////////////// const char *EncodeEntitiesAttr = "\'<>\"\n"; const char *EncodeEntitiesContent = "\'<>\""; char *GXmlTree::EncodeEntities(char *s, ssize_t len, const char *extra_characters) { GStringPipe p; if (EncodeEntities(&p, s, len, extra_characters)) { return p.NewStr(); } return 0; } bool GXmlTree::EncodeEntities(GStreamI *to, char *start, ssize_t len, const char *extra_characters) { if (!start || !to) return 0; int Amp = (d->Flags & GXT_NO_ENTITIES) ? 10000000 : '&'; char *end = start + len; for (char *s = start; s && *s;) { char *e = s; while ( *e && *e != Amp && *e != '\r' && (!extra_characters || !strchr(extra_characters, *e)) && (len < 0 || e < end)) e++; if (e == end || *e) { to->Write(s, (int) (e - s)); if (e == end) break; switch (*e) { case '<': to->Write((char*)"<", 4); break; case '>': to->Write((char*)">", 4); break; case '&': to->Write((char*)"&", 5); break; case '\'': to->Write((char*)"'", 6); break; case '\"': to->Write((char*)""", 6); break; case '\r': // Do nothing... break; default: { char Ent[32]; sprintf_s(Ent, sizeof(Ent), "&#%i;", (uchar)*e); to->Write(Ent, strlen(Ent)); break; } } s = e + 1; } else { to->Write(s, strlen(s)); break; } } return true; } char *GXmlTree::DecodeEntities(GXmlAlloc *Alloc, char *In, ssize_t Len) { if (!In || !Alloc) { LgiAssert(!"Param error"); return NULL; } // char *OriginalIn = In; // Setup temporary buffer ssize_t BufSize = Len + 32; int BufR = BufSize & 0xff; if (BufR) BufSize += 256 - BufR; if (d->Buf.Length() < BufR) d->Buf.Length(BufR); char *Start = &d->Buf[0]; char *Out = Start; char *OutEnd = Out + d->Buf.Length(); char *InEnd = In + Len; while (*In && In < InEnd) { if (Out >= OutEnd - 4) { // Running out of space, extend the buffer. size_t Cur = Out - Start; d->Buf.Length(d->Buf.Length() + 256); Start = &d->Buf[0]; Out = Start + Cur; OutEnd = Start + d->Buf.Length(); } if (*In != '&') { *Out++ = *In++; continue; } In++; // Skip the '&' char16 c16 = 0; if (*In == '#') { In++; if (*In == 'x') c16 = htoi(++In); else c16 = atoi(In); In = strchr(In, ';'); if (In) { In++; } else { LgiAssert(0); } } else { ssize_t len; char *Col = strnchr(In, ';', 16); if (Col && (len = (Col - In)) < 16) { char tmp[16]; memcpy(tmp, In, len); tmp[len] = 0; c16 = d->Entities.Find(tmp); if (c16) In = Col + 1; } if (!c16) { // Not a real named entity, so just emit the ampersand. *Out++ = '&'; } } if (c16) { GAutoString c8(WideToUtf8(&c16, 1)); if (c8) { for (char *c = c8; *c; c++) *Out++ = *c; } } } return Alloc->Alloc(Start, Out - Start); } ////////////////////////////////////////////////////////////////////////////// GAutoRefPtr TagHeapAllocator(new XmlNormalAlloc); GXmlTag::GXmlTag(const char *tag, GXmlAlloc *alloc) { Allocator = alloc ? alloc : TagHeapAllocator; Write = false; Parent = NULL; Content = NULL; Tag = Allocator->Alloc(tag); } GXmlTag::GXmlTag(const GXmlTag &t) { Allocator = t.Allocator; LgiAssert(Allocator != NULL); Tag = NULL; Write = false; Parent = NULL; Content = NULL; Copy((GXmlTag&)t); } GXmlTag::~GXmlTag() { RemoveTag(); Empty(true); } void GXmlTag::EmptyAttributes() { for (int i=0; iFree(Attr[i].Name); Allocator->Free(Attr[i].Value); } Attr.Length(0); } void GXmlTag::EmptyChildren() { GXmlTag *c; while ((c = Children.First())) { LgiAssert(c->Parent == this); DeleteObj(c); } } void GXmlTag::Empty(bool Deep) { EmptyAttributes(); Allocator->Free(Content); SetTag(NULL); if (Deep) EmptyChildren(); } bool GXmlTag::Copy(GXmlTag &t, bool Deep) { Empty(Deep); Allocator = t.Allocator; Content = NewStr(t.Content); Tag = Allocator->Alloc(t.Tag); Attr.Length(t.Attr.Length()); for (int i=0; iAlloc(a.Name); Attr[i].Value = Allocator->Alloc(a.Value); } if (Deep) { List::I it = t.Children.begin(); for (GXmlTag *c = *it; it.In(); c = *++it) { GXmlTag *n = new GXmlTag; if (n) { n->Copy(*c, Deep); InsertTag(n); } } } return true; } bool GXmlTag::XPath(GArray &Results, const char *Path) { return false; } GXmlTag &GXmlTag::operator =(GXmlTag &t) { Copy(t); return *this; } GXmlTag *GXmlTag::CreateTag(const char *Name, char *Content) { GXmlTag *c = GetChildTag(Name, true); if (c) c->Content = NewStr(Content); return c; } const char *GXmlTag::GetTag() { return Tag; } void GXmlTag::SetTag(const char *Str) { Allocator->Free(Tag); Tag = NULL; if (Str) Tag = Allocator->Alloc(Str); } GXmlTag *GXmlTag::GetChildTag(const char *Name, bool Create, const char *TagSeparator) { GToken p(Name, TagSeparator); GXmlTag *t = this; for (int i=0; i::I n = t->Children.begin(); for (GXmlTag *c = *n; n; c = *++n) { if (c->Tag && stricmp(c->Tag, Part) == 0) { Child = c; break; } } if (!Child) { if (Create) { t->InsertTag( Child = new GXmlTag(p[i]) ); if (!Child) { return 0; } } else return 0; } t = Child; } return t; } bool GXmlTag::GetVariant(const char *Name, GVariant &Value, char *Array) { /* !_stricmp(Name, "'attribute_name'") // Type: String */ if (Name) { List::I n = Children.begin(); for (GXmlTag *c = *n; c; c = *++n) { if (c->Tag && stricmp(c->Tag, Name) == 0) { Value = (GDom*) c; return true; } } char *v = GetAttr(Name); if (v) { if (v[0] == 'b' && v[1] == 'i' && v[2] == 'n' && v[3] == 'a' && v[4] == 'r' && v[5] == 'y' && v[6] == '(') { Value.Empty(); Value.Type = GV_BINARY; int Len = 0; char *Start = v + 7; for (char *s = Start; *s && *s != ')'; s++) Len++; Len >>= 1; Value.Value.Binary.Length = Len; Value.Value.Binary.Data = new uchar[Len]; if (Value.Value.Binary.Data) { char *s = Start; for (int i=0; i= '0' && *s <= '9') ? *s - '0' : \ ((*s >= 'a' && *s <= 'f') ? *s - 'a' + 10 : 0) int High = HexToBin(); s++; int Low = HexToBin(); s++; - ((uint8*)Value.Value.Binary.Data)[i] = High << 4 | Low; + ((uint8_t*)Value.Value.Binary.Data)[i] = High << 4 | Low; } } } else { Value = v; } return true; } if (stricmp(Name, "text") == 0) { Value = Content; return true; } } return false; } bool GXmlTag::SetVariant(const char *Name, GVariant &Value, char *Array) { if (Name) { switch (Value.Type) { case GV_NULL: { DelAttr(Name); break; } case GV_BOOL: { char i[32]; sprintf_s(i, sizeof(i), "%i", Value.Value.Bool); SetAttr(Name, i); break; } case GV_INT32: { char i[32]; sprintf_s(i, sizeof(i), "%i", Value.Value.Int); SetAttr(Name, i); break; } case GV_INT64: { char i[32]; sprintf_s(i, sizeof(i), LPrintfInt64, Value.Value.Int64); SetAttr(Name, i); break; } case GV_DOUBLE: { char i[32]; sprintf_s(i, sizeof(i), "%f", Value.Value.Dbl); SetAttr(Name, i); break; } case GV_STRING: { SetAttr(Name, Value.Str()); break; } case GV_BINARY: { GStringPipe p; p.Print("binary("); for (int i=0; iRemoveTag(); t->Parent = this; Children.Insert(t); } } void GXmlTag::RemoveTag() { if (Parent) { Parent->Children.Delete(this); } Parent = 0; } int64 GXmlTag::CountTags() { uint64 c = 1; List::I it = Children.begin(); for (GXmlTag *t = *it; t; t = *++it) { c += t->CountTags(); } return c; } bool GXmlTag::Dump(int Depth) { #define Tabs() { for (int i=0; iDump(Depth + 1); } LgiTrace("\n"); Depth--; #undef Tabs return true; } int GXmlTag::GetContentAsInt(int Default) { return Content ? atoi(Content) : Default; } bool GXmlTag::SetContent(int i) { char s[32]; return SetContent(s, sprintf_s(s, sizeof(s), "%i", i)); } GXmlAttr *GXmlTag::_Attr(const char *Name, bool Wr) { if (!Name) return 0; // Validate the name... for (const char *c = Name; *c; c++) { if (!IsAlpha(*c) && !IsDigit(*c) && !strchr(":-_()", *c)) { LgiAssert(!"Invalid attribute name."); return 0; } } if (Wr && !Allocator) Allocator = new XmlNormalAlloc; for (int i=0; iName && stricmp(a->Name, Name) == 0) { if (Wr) { Allocator->Free(a->Value); a->Value = 0; } return a; } } if (!Wr) return 0; // Create GXmlAttr &n = Attr.New(); n.Name = Allocator->Alloc(Name); return &n; } bool GXmlTag::DelAttr(const char *Name) { for (int i=0; iFree(a.Name); Allocator->Free(a.Value); Attr.DeleteAt(i); return true; } } return false; } bool GXmlTag::SetContent(const char *s, ssize_t len) { char *n = s ? Allocator->Alloc(s, len > 0 ? len : strlen(s)) : NULL; if (Content) { Allocator->Free(Content); Content = NULL; } Content = n; return s ? Content != NULL : true; } char *GXmlTag::GetAttr(const char *n) { GXmlAttr *a = _Attr(n, false); return a ? a->Value : 0; } int GXmlTag::GetAsInt(const char *n) { GXmlAttr *a = _Attr(n, false); return a ? atoi(a->Value) : -1; } bool GXmlTag::SetAttr(const char *n, const char *Value) { GXmlAttr *a = _Attr(n, true); if (a) { a->Value = Allocator->Alloc(Value); return true; } return false; } bool GXmlTag::SetAttr(const char *n, int Value) { GXmlAttr *a = _Attr(n, true); if (a) { char s[32]; sprintf_s(s, sizeof(s), "%i", Value); a->Value = Allocator->Alloc(s); return true; } return false; } bool GXmlTag::SetAttr(const char *n, int64 Value) { GXmlAttr *a = _Attr(n, true); if (a) { char s[32]; sprintf_s(s, sizeof(s), LPrintfInt64, Value); a->Value = Allocator->Alloc(s); return true; } return false; } bool GXmlTag::SerializeAttr(const char *Attr, int &Int) { GXmlAttr *a = _Attr(Attr, Write); if (a) { if (Write) { char s[256]; sprintf_s(s, sizeof(s), "%i", Int); a->Value = Allocator->Alloc(s); return a->Value != 0; } else if (a->Value) { Int = atoi(a->Value); return true; } } return false; } bool GXmlTag::SerializeAttr(const char *Name, char *&Str) { GXmlAttr *a = _Attr(Name, Write); if (a) { if (Write) { if (ValidStr(Str)) { a->Value = Allocator->Alloc(Str); } else { return DelAttr(Name); } return a->Value != 0; } else if (a->Value) { Str = NewStr(a->Value); return true; } } return false; } bool GXmlTag::SerializeAttr(const char *Attr, double &Dbl) { GXmlAttr *a = _Attr(Attr, Write); if (a) { if (Write) { char s[256]; sprintf_s(s, sizeof(s), "%f", Dbl); a->Value = Allocator->Alloc(s); return a->Value != 0; } else if (a->Value) { Dbl = atof(a->Value); return true; } } return false; } /////////////////////////////////////////////////////////////////////////////////////////////////////////// GXmlTree::GXmlTree(int Flags) { d = new GXmlTreePrivate; d->Flags = Flags; } GXmlTree::~GXmlTree() { DeleteObj(d); } static const char *White = " \t\r\n"; #define SkipWhiteSpace(s) while (*s && strchr(White, *s)) s++ void GXmlTag::ParseAttribute(GXmlTree *Tree, GXmlAlloc *Alloc, char *&t, bool &NoChildren, bool &TypeDef) { while (*t && *t != '>' && *t != '?') { // Skip white SkipWhiteSpace(t); if (*t == '>') break; if (*t == '/') { t++; NoChildren = true; break; } char *AttrName = t; if (TypeDef) { if (*t == '\"') { char *Start = ++t; t = strchr(t, '\"'); if (t) { if (t > Start) { GXmlAttr &At = Attr.New(); At.Name = Alloc->Alloc(Start, t - Start); } t++; continue; } else { break; } } else if (*t == '[') { // Read all defs into children of this tag t++; while (*t && *t != ']') { bool NoChildren = false; GXmlTag *c = Tree->Parse(0, Alloc, t, NoChildren, true); if (c) { InsertTag(c); } else break; } if (*t == ']') t++; continue; } } // Goto the end of the attr name while ( *t && ( IsAlpha(*t) || IsDigit(*t) || strchr("-._:()", *t) != 0 ) ) { t++; } if (t > AttrName) { GXmlAttr &At = Attr.New(); At.Name = Alloc->Alloc(AttrName, t-AttrName); // Skip white SkipWhiteSpace(t); // Look for equals if (*t == '=') { t++; // Skip white SkipWhiteSpace(t); if (strchr("\"\'", *t)) { // Delimited string char Delim = *t++; char *End = strchr(t, Delim); if (End) { if (Tree->d->Flags & GXT_NO_ENTITIES) { At.Value = Alloc->Alloc(t, End - t); } else { At.Value = Tree->DecodeEntities(Alloc, t, End - t); } /* if (At.Value) { // Strip out '\r' and '\n' in the attribute value. char *i = At.Value, *o = At.Value; do { if (*i != '\n' && *i != '\r') *o++ = *i; else *o++ = ' '; } while (*i++); } */ t = End + 1; } else { break; } } else { // Non delimited string char *End = t; while (*End && !strchr(White, *End) && *End != '>' && *End != '/') End++; At.Value = Tree->DecodeEntities(Alloc, t, End - t); t = End; } } } else { LgiTrace("%s:%i - Error tagname='%s'\n", _FL, Tag); LgiAssert(0); break; } // Skip white SkipWhiteSpace(t); } } GXmlTag *GXmlTree::Parse(GXmlTag *Tag, GXmlAlloc *Alloc, char *&t, bool &NoChildren, bool InTypeDef) { bool KeepWs = TestFlag(d->Flags, GXT_KEEP_WHITESPACE); char *Start = t; GStringPipe Before; // Skip white ParsingStart: while (*t) { if (*t == '<' && t[1] != ' ') { if (t[1] == '!') { if (t[2] == '-' && t[3] == '-') { // Start of comment if (KeepWs) { Before.Write(Start, t - Start); } char *End = strstr(t + 3, "-->"); if (End) { t += 3; OnParseComment(Tag ? Tag : d->Current, t, End - t); t = End + 2; if (KeepWs) { Start = t; } } else { t = 0; break; } } else { char *End = strchr(t + 1, '>'); if (End) { t = End; } } } else { break; } } t++; } if (KeepWs && t > Start) { Before.Write(Start, t - Start); if (Before.GetSize() > 0) { GXmlTag *PreContent = d->Factory ? d->Factory->Create(0) : new GXmlTag; if (PreContent) { PreContent->Allocator = Alloc; NoChildren = true; if (d->Flags & GXT_NO_ENTITIES) PreContent->Content = Before.NewStr(); else { GAutoString Tmp(Before.NewStr()); GAutoRefPtr LocalAlloc(new XmlNormalAlloc); PreContent->Content = DecodeEntities(Tag ? Tag->Allocator : LocalAlloc, Tmp, strlen(Tmp)); } return PreContent; } } } if (*t && *t == '<') { t++; SkipWhiteSpace(t); // Store tagname start char *TagName = t; // bool TypeDef = *t == '!'; if (*t == '/') t++; while ( *t && ( IsAlpha(*t) || *t == '!' || *t == '?' || *t == '-' || *t == '_' || *t == ':' || *t == '.' || *t == '[' || *t == ']' || (t > TagName && IsDigit(*t)) ) ) { t++; } if (t > TagName) { if (TagName[0] == '?') { GAutoString TmpStr(NewStr(TagName, t - TagName)); GXmlTag Temp(TmpStr, Alloc); TmpStr.Reset(); bool bTrue = true; while (*t) { SkipWhiteSpace(t); if (t[0] == '>' || (t[0] == '?' && t[1] == '>')) { break; } Temp.ParseAttribute(this, Alloc, t, bTrue, InTypeDef); } if (Temp.Tag) { if (stricmp(Temp.Tag, "?xml-stylesheet") == 0) { char *Type = Temp.GetAttr("type"); char *File = Temp.GetAttr("href"); if (Type && File) { SetStyleFile(File, Type); } } } Before.Empty(); goto ParsingStart; } else { // Create the tag object char Old = *t; *t = 0; if (!Tag) { Tag = d->Factory ? d->Factory->Create(TagName) : new GXmlTag; if (!Tag) return 0; Tag->Allocator = Alloc; } *t = Old; // Parse attributes into tag if (Tag) { Tag->Empty(false); LgiAssert(Tag->Tag == NULL); Tag->Tag = Tag->Allocator->Alloc(TagName, t - TagName); NoChildren = Tag->Tag ? Tag->Tag[0] == '?' : false; Tag->ParseAttribute(this, Alloc, t, NoChildren, InTypeDef); // Skip white SkipWhiteSpace(t); if (*t == '>') t++; } char *ContentStart = t; while ( *t && *t != '<' && (!InTypeDef || *t != ']') ) { t++; } if (t > ContentStart) { if (d->Flags & GXT_NO_ENTITIES) { Tag->Content = NewStr(ContentStart, t - ContentStart); } else { Tag->Content = DecodeEntities(Tag->Allocator, ContentStart, t - ContentStart); } if (!TestFlag(d->Flags, GXT_KEEP_WHITESPACE) && !ValidStr(Tag->Content)) { Tag->Allocator->Free(Tag->Content); } } } } else if (*t) { printf("Xml: stopped at '%-20s'\n", t); LgiAssert(0); } } return Tag; } bool GXmlTree::Read(GXmlTag *Root, GStreamI *File, GXmlFactory *Factory) { if (!Root) { d->Error = "No root argument."; return false; } if (!File) { d->Error = "No input stream argument."; return false; } GString t = Root->Tag; Root->SetTag(NULL); GAutoRefPtr Allocator(new XmlPoolAlloc); Root->Allocator = Allocator; Root->SetTag(t); int64 Len = File->GetSize(); if (Len <= 0) { d->Error.Printf("Input stream is empty: %" PRId64 " bytes.\n", Len); return false; } char *Str = new char[Len+1]; if (!Str) { d->Error = "Alloc error."; return false; } ssize_t r = File->Read(Str, Len); if (r <= 0) { d->Error.Printf("Failed to read from input stream: %zd\n", r); return false; } Str[r] = 0; char *Ptr = Str; d->Factory = Factory; d->Current = Root; bool First = true; while (d->Current && Ptr && *Ptr) { bool NoChildren; GAutoPtr t ( Parse( First && !TestFlag(d->Flags, GXT_NO_DOM) ? Root : 0, Allocator, Ptr, NoChildren, false) ); First = false; if (t) { if (t->Tag && t->Tag[0] == '!' && strcmp(t->Tag, "!DOCTYPE") == 0) { for (GXmlTag *c=t->Children.First(); c; c=t->Children.Next()) { if (c->Tag && strcmp(c->Tag, "!ENTITY") == 0) { if (c->Attr.Length() == 2) { GXmlAttr &Ent = c->Attr[0]; GXmlAttr &Value = c->Attr[1]; if (Ent.Name && Value.Name && !d->Entities.Find(Ent.Name)) { GVariant v(Value.Name); char16 *w = v.WStr(); if (w) d->Entities.Add(Ent.Name, *w); } } } } } if (t->Tag && t->Tag[0] == '/' && d->Current->Tag) { // End tag if (stricmp(t->Tag + 1, d->Current->Tag) == 0) { d->Current = d->Current->Parent; } else { int Lines = 1; for (char *k = Ptr; k >= Str; k--) { if (*k == '\n') Lines++; } d->Error.Printf("Mismatched '%s' tag, got '%s' instead (Line %i).\n", t->Tag, d->Current->Tag, Lines); #ifdef _DEBUG GXmlTree Dbg; GFile Out; if (Out.Open("c:\\temp\\out.xml", O_WRITE)) { Dbg.Write(Root, &Out); } #endif break; } t.Reset(); } else { t->Serialize(t->Write = false); GXmlTag *NewTag = t; if (t != Root) d->Current->InsertTag(t.Release()); else t.Release(); if (!TestFlag(d->Flags, GXT_NO_DOM) && !NoChildren && !d->NoChildTags.Find(NewTag->Tag)) { d->Current = NewTag; } } } else { break; } } d->Factory = 0; DeleteArray(Str); return true; } void GXmlTree::Output(GXmlTag *t, int Depth) { #define Tabs if (!TestFlag(d->Flags, GXT_NO_PRETTY_WHITESPACE)) \ { for (int i=0; iFile->Write((void*)"\t", 1); } if (d->Prog) d->Prog->Value(d->Prog->Value()+1); t->Serialize(t->Write = true); Tabs // Test to see if the tag is valid bool ValidTag = ValidStr(t->Tag) && !IsDigit(t->Tag[0]); if (ValidTag) GStreamPrint(d->File, "<%s", t->Tag); for (int i=0; iAttr.Length(); i++) { GXmlAttr &a = t->Attr[i]; // Write the attribute name GStreamPrint(d->File, " %s=\"", a.Name); // Encode the value EncodeEntities(d->File, a.Value, -1, EncodeEntitiesAttr); // Write the delimiter d->File->Write((void*)"\"", 1); if (iAttr.Length()-1 && TestFlag(d->Flags, GXT_PRETTY_WHITESPACE)) { d->File->Write((void*)"\n", 1); for (int i=0; i<=Depth; i++) d->File->Write((void*)"\t", 1); } } // Write the child tags bool HasContent = ValidStr(t->Content); GXmlTag *c = t->Children.First(); if (c || HasContent) { if (ValidTag) d->File->Write(">", 1); if (HasContent) { EncodeEntities(d->File, t->Content, -1, EncodeEntitiesContent); if (c) { d->File->Write((char*)"\n", 1); Tabs } } if (c) { if (!HasContent) { d->File->Write((char*)"\n", 1); } for (; c; c=t->Children.Next()) { Output(c, Depth + (d->NoDom() ? 0 : 1)); } Tabs } if (!d->NoDom()) GStreamPrint(d->File, "\n", t->Tag); } else if (ValidTag) { if (d->NoDom()) d->File->Write(">\n", 2); else d->File->Write(" />\n", 4); } #undef Tabs } bool GXmlTree::Write(GXmlTag *Root, GStreamI *File, Progress *Prog) { bool Status = false; if (Root && File) { File->SetSize(0); d->File = File; if ((d->Prog = Prog)) d->Prog->SetLimits(0, Root->CountTags()); if (!TestFlag(d->Flags, GXT_NO_HEADER)) File->Write(GXmlHeader, strlen(GXmlHeader)); if (d->StyleFile && d->StyleType) GStreamPrint(d->File, "\n", d->StyleFile, d->StyleType); Output(Root, 0); d->File = NULL; d->Prog = NULL; Status = true; } return Status; } char *GXmlTree::GetErrorMsg() { return d->Error; } LHashTbl,bool> *GXmlTree::NoChildTags() { return &d->NoChildTags; } LHashTbl,char16> *GXmlTree::GetEntityTable() { return &d->Entities; } char *GXmlTree::GetStyleFile(char **StyleType) { if (StyleType) *StyleType = d->StyleType; return d->StyleFile; } void GXmlTree::SetStyleFile(char *file, const char *type) { DeleteArray(d->StyleFile); DeleteArray(d->StyleType); d->StyleFile = NewStr(file); d->StyleType = NewStr(type); } diff --git a/src/common/Text/LUnicodeString.cpp b/src/common/Text/LUnicodeString.cpp --- a/src/common/Text/LUnicodeString.cpp +++ b/src/common/Text/LUnicodeString.cpp @@ -1,31 +1,31 @@ #include "Lgi.h" #include "LUnicodeString.h" -static uint8 u8[] = { +static uint8_t u8[] = { 'T','h','i','s',' ','i','s',' ','s','o','m','e',' ','a','s','c','i','i',' ', 0xE1,0x9A,0xA0,0xE1,0x9B,0x87,0xE1,0x9A,0xBB,0xE1,0x9B,0xAB,0xE1,0x9B,0x92,0xE1,0x9B,0xA6,0xE1,0x9A,0xA6,0xE1,0x9B,0xAB,0xE1,0x9A,0xA0,0xE1,0x9A,0xB1,0xE1,0x9A, 0xA9,0xE1,0x9A,0xA0,0xE1,0x9A,0xA2,0xE1,0x9A,0xB1,0xE1,0x9B,0xAB,0xE1,0x9A,0xA0,0xE1,0x9B,0x81,0xE1,0x9A,0xB1,0xE1,0x9A,0xAA,0xE1,0x9B,0xAB,0xE1,0x9A,0xB7,0xE1, 0x9B,0x96,0xE1,0x9A,0xBB,0xE1,0x9A,0xB9,0xE1,0x9B,0xA6,0xE1,0x9B,0x9A,0xE1,0x9A,0xB3,0xE1,0x9A,0xA2,0xE1,0x9B,0x97,0 }; static uint16 u16[] = { 0 }; -static uint32 u32[] = { 0 }; +static uint32_t u32[] = { 0 }; bool LUnicodeString_UnitTests() { - LUnicodeString Utf8(u8); + LUnicodeString Utf8(u8); auto b = Utf8.Bytes(); auto w = Utf8.Words(); auto c = Utf8.Chars(); LgiAssert(b == w); LgiAssert(c == 48); LUnicodeString Utf16; - uint32 ch; + uint32_t ch; while ((ch = *Utf8++)) { *Utf16++ = ch; } return false; } diff --git a/src/common/Widgets/GBox.cpp b/src/common/Widgets/GBox.cpp --- a/src/common/Widgets/GBox.cpp +++ b/src/common/Widgets/GBox.cpp @@ -1,741 +1,741 @@ #include "Lgi.h" #include "GBox.h" #include "GCssTools.h" #include "LgiRes.h" #include "GPopup.h" #define DEFAULT_SPACER_PX 5 // #define DEFAULT_SPACER_COLOUR24 LC_MED #define DEFAULT_MINIMUM_SIZE_PX 5 #define ACTIVE_SPACER_SIZE_PX 9 enum GBoxMessages { M_CHILDREN_CHANGED = M_USER + 0x2000 }; struct GBoxPriv { public: bool Vertical; GArray Spacers; GBox::Spacer *Dragging; GdcPt2 DragOffset; bool Dirty; GBoxPriv() { Dirty = false; Vertical = false; Dragging = NULL; } int GetBox(GRect &r) { return Vertical ? r.Y() : r.X(); } GBox::Spacer *HitTest(int x, int y) { for (int i=0; iUnregisterHook(this); DeleteObj(d); } bool GBox::IsVertical() { return d->Vertical; } void GBox::SetVertical(bool v) { if (d->Vertical != v) { d->Vertical = v; OnPosChange(); } } GBox::Spacer *GBox::GetSpacer(int idx) { if (Children.Length()) { while (d->Spacers.Length() < Children.Length() - 1) { Spacer &s = d->Spacers.New(); s.SizePx = DEFAULT_SPACER_PX; // s.Colour.c24(DEFAULT_SPACER_COLOUR24); } } return idx >= 0 && idx < d->Spacers.Length() ? &d->Spacers[idx] : NULL; } GViewI *GBox::GetViewAt(int i) { return Children[i]; } -bool GBox::SetViewAt(uint32 i, GViewI *v) +bool GBox::SetViewAt(uint32_t i, GViewI *v) { if (!v || i > Children.Length()) { return false; } if (v->GetParent()) v->Detach(); v->Visible(true); bool Status; if (i < Children.Length()) { // Remove existing view.. GViewI *existing = Children[i]; if (existing == v) return true; if (existing) existing->Detach(); Status = AddView(v, i); } else { Status = AddView(v); } if (Status) { AttachChildren(); } return Status; } void GBox::OnCreate() { AttachChildren(); OnPosChange(); GWindow *Wnd = GetWindow(); if (Wnd) Wnd->RegisterHook(this, GMouseEvents); } bool GBox::OnViewMouse(GView *v, GMouse &m) { // This hook allows the GBox to catch clicks nearby the splits even if the splits are too small // to grab normally. Consider the case of a split that is 1px wide. The active region needs to // be a little larger than that, however a normal click would go through to the child windows // on either side of the split rather than to the GBox. if (!m.IsMove() && m.Down()) { // Convert click to the local coordinates of this view GMouse Local = m; while (v && v != (GView*)this && v->GetParent()) { if (dynamic_cast(v)) return true; GRect p = v->GetPos(); Local.x += p.x1; Local.y += p.y1; GViewI *vi = v->GetParent(); v = vi ? vi->GetGView() : NULL; } if (v == (GView*)this) { // Is the click over our spacers? Spacer *s = d->HitTest(Local.x, Local.y); if (s) { // Pass the click to ourselves and prevent the normal view from getting it. OnMouseClick(Local); return false; } } } return true; } bool GBox::Pour(GRegion &r) { GRect *p = FindLargest(r); if (!p) return false; SetPos(*p); return true; } void GBox::OnPaint(GSurface *pDC) { if (d->Dirty) { d->Dirty = false; OnPosChange(); } GRect cli = GetClient(); GCssTools tools(GetCss(), GetFont()); cli = tools.PaintBorderAndPadding(pDC, cli); GColour cBack = StyleColour(GCss::PropBackgroundColor, GColour(LC_MED, 24)); size_t ChildViews = Children.Length(); if (ChildViews == 0) { pDC->Colour(cBack); pDC->Rectangle(&cli); } else { #if 0 // coverage check... pDC->Colour(GColour(255, 0, 255)); pDC->Rectangle(&cli); #endif GRegion Painted(cli); for (int i=0; iSpacers.Length(); i++) { Spacer &s = d->Spacers[i]; if (s.Colour.IsValid()) pDC->Colour(s.Colour); else pDC->Colour(cBack); pDC->Rectangle(&s.Pos); Painted.Subtract(&s.Pos); } for (auto c : Children) Painted.Subtract(&c->GetPos()); for (auto r = Painted.First(); r; r = Painted.Next()) { pDC->Colour(cBack); pDC->Rectangle(r); } } } struct BoxRange { int Min, Max; GCss::Len Size; GViewI *View; BoxRange() { Min = Max = 0; View = NULL; } }; void GBox::OnPosChange() { GCssTools tools(GetCss(), GetFont()); GRect client = GetClient(); if (!client.Valid()) return; GRect content = tools.ApplyBorder(client); content = tools.ApplyPadding(content); GetSpacer(0); GAutoPtr views(IterateViews()); int Cur = content.x1, Idx = 0; int AvailablePx = d->GetBox(content); if (AvailablePx <= 0) return; GArray Sizes; int SpacerPx = 0; int FixedPx = 0; int FixedChildren = 0; int PercentPx = 0; int PercentChildren = 0; float PercentCount = 0.0f; int AutoChildren = 0; // Do first pass over children and find their sizes for (GViewI *c = views->First(); c; c = views->Next(), Idx++) { GCss *css = c->GetCss(); BoxRange &box = Sizes.New(); box.View = c; // Get any available CSS size if (css) { if (IsVertical()) box.Size = css->Height(); else box.Size = css->Width(); } // Work out some min and max values if (box.Size.IsValid()) { if (box.Size.Type == GCss::LenPercent) { box.Max = box.Size.ToPx(AvailablePx, GetFont()); PercentPx += box.Max; PercentCount += box.Size.Value; PercentChildren++; } else if (box.Size.IsDynamic()) { AutoChildren++; } else { // Fixed children get first crack at the space box.Min = box.Max = box.Size.ToPx(AvailablePx, GetFont()); FixedPx += box.Min; FixedChildren++; } } else AutoChildren++; // Allocate area for spacers in the Fixed portion if (Idx < Children.Length() - 1) { Spacer &s = d->Spacers[Idx]; SpacerPx += s.SizePx; } } // Convert all the percentage sizes to px int RemainingPx = AvailablePx - SpacerPx - FixedPx; for (int i=0; i RemainingPx) { if (AutoChildren > 0 || PercentChildren > 1) { // Well... ah... we better leave _some_ space for them. int AutoPx = 16 * AutoChildren; float Ratio = ((float)RemainingPx - AutoPx) / PercentPx; int Px = (int) (box.Max * Ratio); box.Size.Type = GCss::LenPx; box.Size.Value = (float) Px; RemainingPx -= Px; } else { // We can just take all the space... box.Size.Type = GCss::LenPx; box.Size.Value = (float) RemainingPx; RemainingPx = 0; } } else { box.Size.Type = GCss::LenPx; box.Size.Value = (float) box.Max; RemainingPx -= box.Max; } } } // Convert auto children to px int AutoPx = AutoChildren > 0 ? RemainingPx / AutoChildren : 0; for (int i=0; i 1) { box.Size.Value = (float) AutoPx; RemainingPx -= AutoPx; } else { box.Size.Value = (float) RemainingPx; RemainingPx = 0; } AutoChildren--; } } for (int i=0; iVertical) { viewPos.y1 = Cur; viewPos.y2 = Cur + Px - 1; } else { viewPos.x1 = Cur; viewPos.x2 = Cur + Px - 1; } box.View->SetPos(viewPos); #ifdef WIN32 // This forces the update, otherwise the child's display lags till the // mouse is released *rolls eyes* box.View->Invalidate((GRect*)NULL, true); #endif Cur += Px; // Allocate area for spacer if (i < Sizes.Length() - 1) { Spacer &s = d->Spacers[i]; s.Pos = content; if (d->Vertical) { s.Pos.y1 = Cur; s.Pos.y2 = Cur + s.SizePx - 1; } else { s.Pos.x1 = Cur; s.Pos.x2 = Cur + s.SizePx - 1; } Cur += s.SizePx; } } } void GBox::OnMouseClick(GMouse &m) { if (m.Down()) { d->Dragging = d->HitTest(m.x, m.y); if (d->Dragging) { d->DragOffset.x = m.x - d->Dragging->Pos.x1; d->DragOffset.y = m.y - d->Dragging->Pos.y1; Capture(d->Dragging != NULL); } } else if (IsCapturing()) { Capture(false); d->Dragging = NULL; } } bool IsValidLen(GCss *c, GCss::PropType p) { if (!c || c->GetType(p) != GCss::TypeLen) return false; GCss::Len *l = (GCss::Len*)c->PropAddress(p); if (!l) return false; return l->IsValid(); } void GBox::OnMouseMove(GMouse &m) { if (!d->Dragging || !IsCapturing()) return; if (!m.Down()) { // Something else got the up click? Capture(false); return; } int DragIndex = (int) (d->Dragging - &d->Spacers[0]); if (DragIndex < 0 || DragIndex >= d->Spacers.Length()) { LgiAssert(0); return; } GViewI *Prev = Children[DragIndex]; if (!Prev) { LgiAssert(0); return; } GViewI *Next = DragIndex < Children.Length() ? Children[DragIndex+1] : NULL; GCssTools tools(GetCss(), GetFont()); GRect Content = tools.ApplyMargin(GetClient()); int ContentPx = d->GetBox(Content); GRect SplitPos = d->Dragging->Pos; GCss *PrevStyle = Prev->GetCss(); GCss::PropType Style = d->Vertical ? GCss::PropHeight : GCss::PropWidth; bool EditPrev = !Next || IsValidLen(PrevStyle, Style); GViewI *Edit = EditPrev ? Prev : Next; LgiAssert(Edit != NULL); GRect ViewPos = Edit->GetPos(); auto *EditCss = Edit->GetCss(true); if (d->Vertical) { // Work out the minimum height of the view GCss::Len MinHeight = EditCss->MinHeight(); int MinPx = MinHeight.IsValid() ? MinHeight.ToPx(ViewPos.Y(), Edit->GetFont()) : DEFAULT_MINIMUM_SIZE_PX; int Offset = m.y - d->DragOffset.y - SplitPos.y1; if (Offset) { // Slide up and down the Y axis // Limit to the min size GRect r = ViewPos; if (EditPrev) { r.y2 += Offset; if (r.Y() < MinPx) { int Diff = MinPx - r.Y(); Offset += Diff; r.y2 += Diff; } } else { r.y1 += Offset; if (r.Y() < MinPx) { int Diff = MinPx - r.Y(); Offset -= Diff; r.y1 -= Diff; } } if (Offset) { SplitPos.Offset(0, Offset); // Save the new height of the view GCss::Len Ht = EditCss->Height(); if (Ht.Type == GCss::LenPercent && ContentPx > 0) { Ht.Value = (float)r.Y() * 100 / ContentPx; } else { Ht.Type = GCss::LenPx; Ht.Value = (float)r.Y(); } EditCss->Height(Ht); } } } else { // Work out the minimum width of the view GCss::Len MinWidth = EditCss->MinWidth(); int MinPx = MinWidth.IsValid() ? MinWidth.ToPx(ViewPos.X(), Edit->GetFont()) : DEFAULT_MINIMUM_SIZE_PX; int Offset = m.x - d->DragOffset.x - SplitPos.x1; if (Offset) { // Slide along the X axis // Limit to the min size GRect r = ViewPos; if (EditPrev) { r.x2 += Offset; int rx = r.X(); if (r.X() < MinPx) { int Diff = MinPx - rx; Offset += Diff; r.x2 += Diff; } } else { r.x1 += Offset; int rx = r.X(); if (r.X() < MinPx) { int Diff = MinPx - rx; Offset -= Diff; r.x1 -= Diff; } } if (Offset) { SplitPos.Offset(Offset, 0); // Save the new height of the view GCss::Len Wid = EditCss->Width(); if (Wid.Type == GCss::LenPercent && ContentPx > 0) { Wid.Value = (float)r.X() * 100 / ContentPx; } else { Wid.Type = GCss::LenPx; Wid.Value = (float)r.X(); } EditCss->Width(Wid); } } } OnPosChange(); Invalidate((GRect*)NULL, true); } void GBox::OnChildrenChanged(GViewI *Wnd, bool Attaching) { #if 0 LgiTrace("GBox(%s)::OnChildrenChanged(%s, %i)\n", Name(), Wnd ? Wnd->GetClass() : NULL, Attaching); for (int i=0; iGetClass(), Children[i]->Handle(), Children[i]->Visible()); #endif d->Dirty = true; if (Handle()) PostEvent(M_CHILDREN_CHANGED); } int64 GBox::Value() { GViewI *v = Children.First(); if (!v) return 0; GCss *css = v->GetCss(); if (!css) return 0; GCss::Len l = d->Vertical ? css->Height() : css->Width(); if (l.Type != GCss::LenPx) return 0; return (int64)l.Value; } void GBox::Value(int64 i) { GViewI *v = Children.First(); if (!v) return; GCss *css = v->GetCss(true); if (!css) return; if (d->Vertical) css->Height(GCss::Len(GCss::LenPx, (float)i)); else css->Width(GCss::Len(GCss::LenPx, (float)i)); OnPosChange(); } LgiCursor GBox::GetCursor(int x, int y) { Spacer *Over = d->HitTest(x, y); if (Over) return (d->Vertical) ? LCUR_SizeVer : LCUR_SizeHor; else return LCUR_Normal; } bool GBox::OnLayout(GViewLayoutInfo &Inf) { Inf.Width.Min = -1; Inf.Width.Max = -1; Inf.Height.Min = -1; Inf.Height.Max = -1; return true; } bool GBox::Serialize(GDom *Dom, const char *OptName, bool Write) { if (Write) { } else { } LgiAssert(0); return false; } bool GBox::SetSize(int ViewIndex, GCss::Len Size) { GViewI *v = Children[ViewIndex]; if (!v) return false; GCss *c = v->GetCss(true); if (!c) return false; c->Width(Size); return true; } GMessage::Result GBox::OnEvent(GMessage *Msg) { if (Msg->Msg() == M_CHILDREN_CHANGED) { if (d->Dirty) { d->Dirty = false; OnPosChange(); } } return GView::OnEvent(Msg); } diff --git a/src/common/Widgets/GPopup.cpp b/src/common/Widgets/GPopup.cpp --- a/src/common/Widgets/GPopup.cpp +++ b/src/common/Widgets/GPopup.cpp @@ -1,1191 +1,1191 @@ #include #include "Lgi.h" #include "GPopup.h" #include "GSkinEngine.h" #include "GDisplayString.h" #include "LThreadEvent.h" #ifdef COCOA #include #endif enum PopupNotifications { POPUP_DELETE = 1, POPUP_VISIBLE, POPUP_HIDE, }; ///////////////////////////////////////////////////////////////////////////////////// #ifndef WH_MOUSE_LL #define WH_MOUSE_LL 14 #endif #if !defined(MAKELONG) #define MAKELONG(low, high) ( ((low) & 0xffff) | ((high) << 16) ) #endif #if defined(__GTK_H__) using namespace Gtk; class GMouseHookPrivate *HookPrivate = 0; #include "LgiWidget.h" bool IsWindow(OsView Wnd) { // Do any available validation of the Wnd we can here #ifdef XWIN // return XWidget::Find(Wnd) != 0; #else return true; #endif } OsView WindowFromPoint(int x, int y) { return NULL; } bool GetWindowRect(OsView Wnd, GRect &rc) { return false; } bool ScreenToClient(OsView Wnd, GdcPt2 &p) { return false; } #elif !defined(WINNATIVE) bool IsWindow(OsView v) { return true; } #endif -uint32 LgiGetViewPid(OsView View) +uint32_t LgiGetViewPid(OsView View) { #if WINNATIVE DWORD hWndProcess = 0; GetWindowThreadProcessId(View, &hWndProcess); return hWndProcess; #endif // FIXME: Linux and BeOS return LgiProcessId(); } class GMouseHookPrivate : public ::LMutex, public ::LThread { public: bool Loop; OsView hMouseOver; List Popups; #ifdef MAC OsView ViewHandle; LThreadEvent Event; #endif GMouseHookPrivate() : LMutex("MouseHookLock"), LThread("MouseHook") { Loop = false; hMouseOver = NULL; #ifdef MAC ViewHandle = NULL; #endif #if defined(LINUX) // LgiTrace("Mouse hook thread not running! (FIXME)\n"); #else Loop = true; Run(); #endif } ~GMouseHookPrivate() { if (Loop) { Loop = false; #ifdef MAC Event.Signal(); #endif while (!IsExited()) { LgiSleep(10); } } } void PostEvent(OsView h, int c, GMessage::Param a, GMessage::Param b) { LgiPostEvent(h, c, a, b); } int Main() { GMouse Old; GView v; while (Loop) { #if defined(MAC) && !defined(LGI_SDL) // Wait for the down click... LThreadEvent::WaitStatus s = Event.Wait(); if (!Loop || s != LThreadEvent::WaitSignaled) { break; } // Now loop for events... GMouse Cur, Prev; Prev.Down(true); do { #if COCOA NSPoint p = [NSEvent mouseLocation]; Cur.x = (int)p.x; Cur.y = (int)p.y; #else HIPoint p; HIGetMousePosition(kHICoordSpaceScreenPixel, NULL, &p); Cur.x = (int)p.x; Cur.y = (int)p.y; Cur.SetModifer(GetCurrentKeyModifiers()); Cur.SetButton(GetCurrentEventButtonState()); Cur.Down(Cur.Left() || Cur.Right() || Cur.Middle()); // Cur.Trace("MouseHook"); if (!Cur.Down() && Prev.Down()) { // Up click... if (ViewHandle) LgiPostEvent(ViewHandle, M_MOUSE_TRACK_UP, 0, 0); else printf("%s:%i - No mouse hook view for up click.\n", _FL); } #endif Prev = Cur; LgiSleep(30); } while (Loop && Cur.Down()); #else GMouse m; v.GetMouse(m, true); if (LockWithTimeout(500, _FL)) { if (m.Down() && !Old.Down()) { // Down click.... uint64 Now = LgiCurrentTime(); GPopup *Over = 0; GPopup *w; for (w = Popups.First(); w; w = Popups.Next()) { if (w->GetPos().Overlap(m.x, m.y)) { Over = w; break; } } for (w = Popups.First(); w; w = Popups.Next()) { #if 0 LgiTrace("PopupLoop: Over=%p w=%p, w->Vis=%i, Time=%i\n", Over, w, w->Visible(), (int) (Now - w->Start)); #endif if (w != Over && w->Visible() && w->Start < Now - 100) { bool Close = true; #if defined WIN32 // This is a bit of a hack to prevent GPopup's with open context menus from // closing when the user clicks on the context menu. // // FIXME: Linux/BeOS // Scan the window under the mouse up the parent tree POINT p = { m.x, m.y }; bool HasPopupInParents = false; for (HWND hnd = WindowFromPoint(p); hnd; hnd = ::GetParent(hnd)) { // Is this a popup? ULONG Style = GetWindowLong(hnd, GWL_STYLE); if (TestFlag(Style, WS_POPUP)) { // No it's a normal window, so close the popup HasPopupInParents = true; break; } } Close = !HasPopupInParents; /* // Are we over a popup? RECT rc; GetWindowRect(hnd, &rc); GRect gr = rc; if (gr.Overlap(m.x, m.y)) { // Yes, so don't close it LgiTrace("Popup: Got a click on a GPopup\n"); Close = false; break; } */ #elif defined LINUX /* for (GViewI *v = Over; v; ) { SubMenuImpl *Sub = dynamic_cast(v); if (Sub) { if (v == w) { Close = false; break; } ::GMenuItem *it = Sub->GetSub()->GetParent(); GSubMenu *mn = it ? it->GetParent() : 0; MenuClickImpl *impl = mn ? mn->Handle() : 0; v = impl ? impl->View() : 0; } else v = 0; } */ #endif if (Close) w->PostEvent(M_SET_VISIBLE, (GMessage::Param)false); } } } Unlock(); } if (m.x != Old.x || m.y != Old.y) { // Mouse moved... OsView hOver = 0; #if WINNATIVE POINT WinPt = { m.x, m.y }; hOver = WindowFromPoint(WinPt); RECT WinRect; GetClientRect(hOver, &WinRect); ScreenToClient(hOver, &WinPt); GRect rc = WinRect; GdcPt2 p(WinPt.x, WinPt.y); #elif defined __GTK_H__ hOver = WindowFromPoint(m.x, m.y); GRect rc; GdcPt2 p(m.x, m.y); if (hOver) { if (!GetWindowRect(hOver, rc)) { LgiTrace("No Rect for over\n"); } if (!ScreenToClient(hOver, p)) { LgiTrace("No conversion for point.\n"); } } else { // LgiTrace("No hOver\n"); } #else // Not implemented. GdcPt2 p; GRect rc; #endif // is the mouse inside the client area? bool Inside = ! (p.x < 0 || p.y < 0 || p.x >= rc.X() || p.y >= rc.Y()); OsView hWnd = (Inside) ? hOver : 0; - uint32 hProcess = LgiProcessId(); - uint32 hWndProcess = 0; + uint32_t hProcess = LgiProcessId(); + uint32_t hWndProcess = 0; if (hWnd != hMouseOver) { // Window has changed if (hMouseOver && IsWindow(hMouseOver)) { // Window is LOSING mouse // Using 'post' because send can cause a deadlock. hWndProcess = LgiGetViewPid(hMouseOver); if (hWndProcess == hProcess) { PostEvent(hMouseOver, M_MOUSEEXIT, 0, (GMessage::Param)MAKELONG((short) p.x, (short) p.y)); } } // Set current window hMouseOver = hWnd; if (hMouseOver && IsWindow(hMouseOver)) { // Window is GETTING mouse // Using 'post' because send can cause a deadlock. hWndProcess = LgiGetViewPid(hMouseOver); if (hWndProcess == hProcess) { PostEvent(hMouseOver, M_MOUSEENTER, (GMessage::Param)Inside, (GMessage::Param)MAKELONG((short) p.x, (short) p.y)); } } } else { hWndProcess = LgiGetViewPid(hMouseOver); } #if WINNATIVE if (hWndProcess == hProcess) { // This code makes sure that non-LGI windows generate mouse move events // for the mouse hook system... // First find the parent LGI window HWND hWnd = hMouseOver; GViewI *v = 0; while (hWnd && !(v = GWindowFromHandle(hMouseOver))) { HWND w = ::GetParent(hWnd); if (w) hWnd = w; else break; } // Get the window... GWindow *w = v ? v->GetWindow() : 0; // Post the event to the window PostEvent(w ? w->Handle() : hWnd, M_HANDLEMOUSEMOVE, MAKELONG((short) p.x, (short) p.y), (LPARAM)hMouseOver); } #endif } Old = m; LgiSleep(40); #endif } return 0; } }; GMouseHook::GMouseHook() { d = new GMouseHookPrivate; } GMouseHook::~GMouseHook() { d->Lock(_FL); DeleteObj(d); } void GMouseHook::TrackClick(GView *v) { #ifdef MAC if (v) { d->ViewHandle = v->Handle(); if (d->ViewHandle) { d->Event.Signal(); } else printf("%s:%i - No view handle.\n", _FL); } else printf("%s:%i - No view ptr.\n", _FL); #endif } bool GMouseHook::OnViewKey(GView *v, GKey &k) { bool Status = false; if (d->Lock(_FL)) { GView *l = d->Popups.Last(); /* if (k.c16 == 13) LgiTrace("GMouseHook::OnViewKey d->p.Items=%i l=%p\n", d->p.Length(), l); */ if (l) { if (l->OnKey(k)) { Status = true; } } d->Unlock(); } return Status; } void GMouseHook::RegisterPopup(GPopup *p) { if (d->Lock(_FL)) { if (!d->Popups.HasItem(p)) { d->Popups.Insert(p); } d->Unlock(); } } void GMouseHook::UnregisterPopup(GPopup *p) { if (d->Lock(_FL)) { d->Popups.Delete(p); d->Unlock(); } } #if defined(WIN32) LRESULT CALLBACK GMouseHook::MouseProc(int Code, WPARAM a, LPARAM b) { return 0; } #elif defined(CARBON) WindowRef CreateBorderlessWindow() { Rect r = {0,0,100,100}; WindowRef wr; OSStatus e = CreateNewWindow ( kDocumentWindowClass, (WindowAttributes) ( kWindowStandardHandlerAttribute | kWindowCompositingAttribute | kWindowNoShadowAttribute | kWindowNoTitleBarAttribute ), &r, &wr ); if (e) { LgiTrace("%s:%i - Error: Creating popup window: %i\n", _FL, e); return NULL; } return wr; } #endif ///////////////////////////////////////////////////////////////////////////////////// class GPopupPrivate { public: bool TakeFocus; GPopupPrivate() { TakeFocus = true; } }; #if !WINNATIVE ::GArray GPopup::CurrentPopups; #endif GPopup::GPopup(GView *owner) #ifdef CARBON : GWindow(CreateBorderlessWindow()) #endif { d = new GPopupPrivate; Start = 0; Cancelled = false; #ifdef _DEBUG // _Debug = true; #endif #ifdef __GTK_H__ Wnd = NULL; #endif #if !WINNATIVE CurrentPopups.Add(this); #endif if ((Owner = owner)) { #ifndef WIN32 Owner->PopupChild() = this; #endif #ifndef MAC _Window = Owner->GetWindow(); #endif SetNotify(Owner); } GView::Visible(false); } GPopup::~GPopup() { // LgiTrace("GPopup::~GPopup %p %p\n", this, d); #if !WINNATIVE CurrentPopups.Delete(this); #endif SendNotify(POPUP_DELETE); if (Owner) { #ifndef WIN32 Owner->PopupChild() = 0; #endif #ifdef MAC GDropDown *dd = dynamic_cast(Owner); if (dd) { dd->Popup = 0; } #endif } #ifdef __GTK_H__ Detach(); if (Wnd) gtk_widget_destroy(Wnd); #endif GMouseHook *Hook = LgiApp->GetMouseHook(); if (Hook) Hook->UnregisterPopup(this); for (GViewI *c; (c = Children.First()); ) { if (!c) break; // ? if (!c->GetParent()) Children.Delete(c); delete c; } DeleteObj(d); } GMessage::Result GPopup::OnEvent(GMessage *Msg) { switch (Msg->Msg()) { #if WINNATIVE case WM_DESTROY: { // LgiTrace("Popup Destroyed.\n"); break; } #endif case M_SET_VISIBLE: { Visible(Msg->A() != 0); break; } } return GView::OnEvent(Msg); } void GPopup::TakeFocus(bool Take) { d->TakeFocus = Take; } #ifdef __GTK_H__ gboolean PopupDestroy(GtkWidget *widget, GPopup *This) { delete This; return true; } gboolean PopupEvent(GtkWidget *widget, GdkEvent *event, GPopup *This) { switch (event->type) { case GDK_CONFIGURE: { GdkEventConfigure *c = (GdkEventConfigure*)event; This->Pos.Set(c->x, c->y, c->x+c->width-1, c->y+c->height-1); This->OnPosChange(); return FALSE; break; } case GDK_FOCUS_CHANGE: { This->OnFocus(event->focus_change.in); break; } case GDK_CLIENT_EVENT: { GMessage m(event); This->OnEvent(&m); break; } case GDK_BUTTON_PRESS: { break; } case GDK_EXPOSE: { GScreenDC s(This); This->OnPaint(&s); break; } default: { // LgiTrace("Unhandled PopupEvent type %i\n", event->type); break; } } return TRUE; } #endif bool GPopup::Attach(GViewI *p) { #if defined(CARBON) return GWindow::Attach(NULL); #else if (p) SetParent(p); else p = GetParent(); #if WINNATIVE SetStyle(WS_POPUP); GView::Attach(p); AttachChildren(); #elif defined __GTK_H__ if (!Wnd) { Wnd = gtk_window_new(GTK_WINDOW_POPUP); gtk_window_set_decorated(GTK_WINDOW(Wnd), FALSE); gtk_widget_add_events(Wnd, GDK_ALL_EVENTS_MASK); if (!p) p = Owner; GtkWidget *toplevel = p ? gtk_widget_get_toplevel(p->Handle()) : NULL; if (GTK_IS_WINDOW(toplevel)) gtk_window_set_transient_for(GTK_WINDOW(Wnd), GTK_WINDOW(toplevel)); else { LgiTrace("%s:%i - toplevel isn't window?\n", _FL); return false; } g_signal_connect( G_OBJECT(Wnd), "button-press-event", G_CALLBACK(PopupEvent), this); g_signal_connect( G_OBJECT(Wnd), "focus-in-event", G_CALLBACK(PopupEvent), this); g_signal_connect( G_OBJECT(Wnd), "focus-out-event", G_CALLBACK(PopupEvent), this); g_signal_connect( G_OBJECT(Wnd), "delete_event", G_CALLBACK(PopupEvent), this); g_signal_connect( G_OBJECT(Wnd), "configure-event", G_CALLBACK(PopupEvent), this); g_signal_connect( G_OBJECT(Wnd), "button-press-event", G_CALLBACK(PopupEvent), this); g_signal_connect( G_OBJECT(Wnd), "client-event", G_CALLBACK(PopupEvent), this); } if (Wnd && Pos.Valid()) { gtk_window_set_default_size(GTK_WINDOW(Wnd), Pos.X(), Pos.Y()); gtk_window_move(GTK_WINDOW(Wnd), Pos.x1, Pos.y1); } if (!_View) { _View = lgi_widget_new(this, Pos.X(), Pos.Y(), true); gtk_container_add(GTK_CONTAINER(Wnd), _View); } #endif if (!_Window) { if (Owner) _Window = Owner->GetWindow(); else _Window = p->GetWindow(); } return Handle() != 0; #endif } void GPopup::Visible(bool i) { #if defined __GTK_H__ if (i && !Wnd) { if (!Attach(0)) { printf("%s:%i - Attach failed.\n", _FL); return; } } GView::Visible(i); if (Wnd) { if (i) { gtk_widget_show_all(Wnd); gtk_window_move(GTK_WINDOW(Wnd), Pos.x1, Pos.y1); gtk_window_resize(GTK_WINDOW(Wnd), Pos.X(), Pos.Y()); // printf("%s:%i - Showing Wnd %s.\n", _FL, Pos.GetStr()); } else { gtk_widget_hide(Wnd); // printf("%s:%i - Hiding Wnd.\n", _FL); } } else printf("%s:%i - No Wnd.\n", _FL); #else #ifdef WINNATIVE bool HadFocus = false; #endif #ifdef LGI_SDL GWindow *TopWnd = LgiApp->AppWnd; if (i && TopWnd) { if (!TopWnd->HasView(this)) TopWnd->AddView(this); } #else if (!Handle() && i) { #if WINNATIVE SetStyle(WS_POPUP); #endif Attach(NULL); } #endif if (!_Window && Owner) { _Window = Owner->GetWindow(); } AttachChildren(); #ifdef WINNATIVE // See if we or a child window has the focus... for (HWND hWnd = GetFocus(); hWnd; hWnd = ::GetParent(hWnd)) { if (hWnd == Handle()) { HadFocus = true; break; } } if (d->TakeFocus || !i) GView::Visible(i); else ShowWindow(Handle(), SW_SHOWNA); #elif defined(CARBON) SetAlwaysOnTop(true); GWindow::Visible(i); #else //bool HadFocus = Focus(); GView::Visible(i); #endif #endif #if 1 if (i) { Start = LgiCurrentTime(); GMouseHook *Hook = LgiApp->GetMouseHook(); if (Hook) Hook->RegisterPopup(this); if (!_Window) { if (Owner) { _Window = Owner->GetWindow(); } else if (GetParent()) { _Window = GetParent()->GetWindow(); } } } else { GMouseHook *Hook = LgiApp->GetMouseHook(); if (Hook) Hook->UnregisterPopup(this); SendNotify(POPUP_HIDE); /* #if WINNATIVE // This is required to re-focus the owner. // If the popup or a child window gets focus at some point. The // owner doesn't get focus when we close... weird I know. if (Owner && HadFocus) { LgiTrace("%s Setting owner focus %s\n", GetClass(), Owner->GetClass()); Owner->Focus(true); } #endif */ } #endif #ifdef LGI_SDL if (TopWnd) TopWnd->Invalidate(); #endif } bool GPopup::Visible() { #if defined __GTK_H__ if (Wnd) { GView::Visible( #if GtkVer(2, 18) gtk_widget_get_visible(Wnd) #else (GTK_OBJECT_FLAGS (Wnd) & GTK_VISIBLE) != 0 #endif ); } #endif #if defined(CARBON) bool v = GWindow::Visible(); #else bool v = GView::Visible(); #endif return v; } ///////////////////////////////////////////////////////////////////////////////////// GDropDown::GDropDown(int Id, int x, int y, int cx, int cy, GPopup *popup) { SetId(Id); GRect r(x, y, x+cx, y+cy); SetPos(r); if ((Popup = popup)) Popup->SetNotify(this); SetTabStop(true); } GDropDown::~GDropDown() { DeleteObj(Popup); } void GDropDown::OnFocus(bool f) { Invalidate(); } GPopup *GDropDown::GetPopup() { return Popup; } void GDropDown::SetPopup(GPopup *popup) { DeleteObj(Popup); Popup = popup; Invalidate(); } bool GDropDown::IsOpen() { return Popup && Popup->Visible(); } void GDropDown::OnPaint(GSurface *pDC) { GRect r = GetClient(); r.Offset(-r.x1, -r.y1); if (!r.Valid()) return; #if defined(MAC) && !defined(COCOA) && !defined(LGI_SDL) GColour NoPaintColour(LC_MED, 24); if (GetCss()) { GCss::ColorDef NoPaint = GetCss()->NoPaintColor(); if (NoPaint.Type == GCss::ColorRgb) NoPaintColour.Set(NoPaint.Rgb32, 32); else if (NoPaint.Type == GCss::ColorTransparent) NoPaintColour.Empty(); } if (!NoPaintColour.IsTransparent()) { pDC->Colour(NoPaintColour); pDC->Rectangle(); } GRect rc = GetClient(); rc.x1 += 2; rc.y2 -= 1; rc.x2 -= 1; HIRect Bounds = rc; HIThemeButtonDrawInfo Info; HIRect LabelRect; Info.version = 0; Info.state = Enabled() ? kThemeStateActive : kThemeStateInactive; Info.kind = kThemePushButton; Info.value = kThemeButtonOff; Info.adornment = Focus() ? kThemeAdornmentFocus : kThemeAdornmentNone; OSStatus e = HIThemeDrawButton( &Bounds, &Info, pDC->Handle(), kHIThemeOrientationNormal, &LabelRect); if (e) printf("%s:%i - HIThemeDrawButton failed %li\n", _FL, e); #else if (GApp::SkinEngine && TestFlag(GApp::SkinEngine->GetFeatures(), GSKIN_BUTTON)) { GMemDC Mem(r.X(), r.Y(), System24BitColourSpace); GCss::ColorDef f; if (GetCss()) f = GetCss()->BackgroundColor(); if (f.Type == GCss::ColorRgb) Mem.Colour(f.Rgb32, 32); else Mem.Colour(LC_MED, 24); Mem.Rectangle(); GApp::SkinEngine->DrawBtn(&Mem, r, NULL, IsOpen(), Enabled()); pDC->Blt(0, 0, &Mem); r.Size(2, 2); r.x2 -= 2; } else { LgiWideBorder(pDC, r, IsOpen() ? DefaultSunkenEdge : DefaultRaisedEdge); pDC->Colour(LC_MED, 24); pDC->Rectangle(&r); if (Focus()) { #if WINNATIVE DrawFocusRect(pDC->Handle(), &((RECT)r)); #else pDC->Colour(LC_LOW, 24); pDC->Box(&r); #endif } } #endif int ArrowWidth = 5; double Aspect = (double)r.X() / r.Y(); int Cx = Aspect < 1.2 ? r.x1 + ((r.X() - ArrowWidth) >> 1) : r.x2 - (ArrowWidth << 1); int Cy = r.y1 + ((r.Y() - 3) >> 1); pDC->Colour(Enabled() && Popup ? LC_TEXT : LC_LOW, 24); if (IsOpen()) { Cx++; Cy++; } for (int i=0; i<3; i++) { pDC->Line(Cx+i, Cy+i, Cx+ArrowWidth-i, Cy+i); } char *Nm = Name(); if (Nm && X() >= 32) { GDisplayString Ds(SysFont, Nm); SysFont->Colour(LC_TEXT, LC_MED); SysFont->Transparent(true); int Offset = IsOpen() ? 1 : 0; Ds.Draw(pDC, (Cx-Ds.X())/2+Offset+r.x1, (Y()-Ds.Y())/2+Offset); } } void GDropDown::Activate() { if (IsOpen()) { // Hide Popup->Visible(false); } else // Show { // Locate under myself GdcPt2 p(X()-1, Y()); PointToScreen(p); GRect r(p.x-Popup->X()+1, p.y, p.x, p.y+Popup->Y()-1); // Show the popup if (!Popup->IsAttached()) { Popup->Attach(this); } Popup->Cancelled = false; Popup->SetPos(r); Popup->Visible(true); } Invalidate(); } bool GDropDown::OnKey(GKey &k) { if (k.IsChar && (k.c16 == ' ' || k.c16 == VK_RETURN)) { if (k.Down()) { Activate(); } return true; } else if (k.vkey == VK_ESCAPE) { if (k.Down() && IsOpen()) { Activate(); } return true; } return false; } void GDropDown::OnMouseClick(GMouse &m) { if (Popup && m.Down()) { Focus(true); Activate(); } } int GDropDown::OnNotify(GViewI *c, int f) { if (c == (GViewI*)Popup) { switch (f) { case POPUP_DELETE: { Popup = 0; break; } case POPUP_VISIBLE: { break; } case POPUP_HIDE: { Invalidate(); OnPopupClose(); break; } } } return false; } diff --git a/src/common/Widgets/GToolBar.cpp b/src/common/Widgets/GToolBar.cpp --- a/src/common/Widgets/GToolBar.cpp +++ b/src/common/Widgets/GToolBar.cpp @@ -1,1729 +1,1729 @@ /* ** FILE: GToolbar.cpp ** AUTHOR: Matthew Allen ** DATE: 18/10/2001 ** DESCRIPTION: Toolbar classes ** ** Copyright (C) 2001, Matthew Allen ** fret@memecode.com */ #include #include #include "Lgi.h" #include "GToken.h" #include "GVariant.h" #include "GDisplayString.h" #include "GPalette.h" #include "GNotifications.h" #include "LgiRes.h" #include "GCssTools.h" #define ToolBarHilightColour LC_HIGH #ifdef WIN32 HPALETTE GetSystemPalette(); bool BltBmpToBmp(HBITMAP hDest, int xDst, int yDst, int cx, int cy, HBITMAP hSrc, int xSrc, int ySrc, DWORD dwRop); bool BltBmpToDc(HDC DestDC, int xDst, int yDst, int cx, int cy, HBITMAP hSrc, int xSrc, int ySrc, DWORD dwRop); bool BltDcToBmp(HBITMAP hDest, int xDst, int yDst, int cx, int cy, HDC SrcDC, int xSrc, int ySrc, DWORD dwRop); #define AttachButton(b) Children.Insert(b); #else #define AttachButton(b) b->Attach(this); #endif #ifdef BEOS #include #endif enum IconCacheType { IconNormal, IconHilight, IconDisabled }; COLOUR Map(GSurface *pDC, COLOUR c); //////////////////////////////////////////////////////////////////////// GImageList *LgiLoadImageList(const char *File, int x, int y) { if (x < 0 || y < 0) { GString f = File; GString leaf = f(f.RFind(DIR_STR)+1, -1); GString width = leaf(leaf.RFind("-")+1, leaf.RFind(".")); ptrdiff_t sep = width.Find("x"); GString height = width(sep+1, -1); if (sep > 0) width.Length((int)sep); if (x < 0 && width.Get()) x = (int)width.Int(); if (y < 0 && (width.Get() || height.Get())) y = (int)(height.Get() ? height.Int() : width.Int()); } GImageList *ImgList = 0; char *Path = FileExists(File) ? NewStr(File) : LgiFindFile(File); if (Path) { GSurface *pDC = GdcD->Load(Path); if (pDC) { ImgList = new GImageList(x, y, pDC); DeleteObj(pDC); } else LgiTrace("%s:%i - Couldn't load '%s'\n", _FL, Path); DeleteArray(Path); } else LgiTrace("%s:%i - Couldn't find '%s'\n", _FL, File); return ImgList; } GToolBar *LgiLoadToolbar(GViewI *Parent, const char *File, int x, int y) { GToolBar *Toolbar = new GToolBar; if (Toolbar) { GAutoString FileName(LgiFindFile(File)); if (FileName) { bool Success = FileName && Toolbar->SetBitmap(FileName, x, y); if (!Success) { LgiMsg(Parent, "Can't load '%s' for the toolbar.\n" "This is probably because libpng/libjpeg is missing.", "LgiLoadToolbar", MB_OK, File); } } else { LgiMsg(Parent, "Can't find the graphic '%s' for the toolbar.\n" "You can find it in this program's archive.", "LgiLoadToolbar", MB_OK, File); } } return Toolbar; } ///////////////////////////////////////////////////////////////////////////////////////////////////////// #define ImgLst_Empty 0x40000000 #define IgmLst_Add 0x80000000 class GImageListPriv { public: GImageList *ImgLst; int Sx, Sy; struct CacheDC : public GMemDC { bool Disabled; GColour Back; }; GArray Cache; GArray Bounds; CacheDC *GetCache(GColour Back, bool Disabled) { for (int i=0; iBack == Back && dc->Disabled == Disabled) return dc; } CacheDC *dc = new CacheDC; if (dc) { dc->Disabled = Disabled; dc->Back = Back; bool Status = dc->Create(ImgLst->X(), ImgLst->Y(), GdcD->GetColourSpace()); if (Status) { dc->Colour(dc->Back); dc->Rectangle(); dc->Op(GDC_ALPHA); if (Disabled) { GMemDC tmp(ImgLst->X(), ImgLst->Y(), System32BitColourSpace, GSurface::SurfaceRequireExactCs); tmp.Colour(0, 32); tmp.Rectangle(); tmp.Op(GDC_ALPHA); tmp.Blt(0, 0, ImgLst); tmp.SetConstantAlpha(40); dc->Blt(0, 0, &tmp); } else { dc->Blt(0, 0, ImgLst); } Cache.Add(dc); } else { delete dc; LgiAssert(!"Create memdc failed."); } } return dc; } GImageListPriv(GImageList *imglst, int x, int y) { ImgLst = imglst; Sx = x; Sy = y; } ~GImageListPriv() { Cache.DeleteObjects(); } }; static bool HasPad(GColourSpace cs) { if (cs == CsRgbx32 || cs == CsBgrx32 || cs == CsXrgb32 || cs == CsXbgr32) return true; return false; } GImageList::GImageList(int x, int y, GSurface *pDC) { d = new GImageListPriv(this, x, y); // BeOS transparent pixels: // B_TRANSPARENT_MAGIC_CMAP8, B_TRANSPARENT_MAGIC_RGBA15, B_TRANSPARENT_MAGIC_RGBA32 - uint32 Transparent = + uint32_t Transparent = #ifdef BEOS B_TRANSPARENT_MAGIC_RGBA32; #else 0; #endif if (pDC && Create(pDC->X(), pDC->Y(), System32BitColourSpace, GSurface::SurfaceRequireExactCs)) { Colour(Transparent, 32); Rectangle(); int Old = Op(GDC_ALPHA); Blt(0, 0, pDC); Op(Old); #if 0 printf("Toolbar input image is %s, has_alpha=%i, has_pad=%i\n", GColourSpaceToString(pDC->GetColourSpace()), pDC->HasAlpha(), HasPad(pDC->GetColourSpace())); #endif if (pDC->GetBits() < 32 || HasPad(pDC->GetColourSpace())) { // auto InCs = pDC->GetColourSpace(); if (!pDC->HasAlpha()) { // No source alpha, do colour keying to create the alpha channel - REG uint32 *p = (uint32*)(*this)[0]; + REG uint32_t *p = (uint32_t*)(*this)[0]; if (p) { - uint32 key = *p; + uint32_t key = *p; for (int y=0; ya == 0) { p->r = 0; p->g = 0; p->b = 0; } p++; } } } #if 0 static int Idx = 0; char s[256]; sprintf_s(s, sizeof(s), "imglst_%i.bmp", Idx++); WriteDC(s, this); #endif } } GImageList::~GImageList() { DeleteObj(d); } void GImageList::Draw(GSurface *pDC, int Dx, int Dy, int Image, GColour Background, bool Disabled) { if (!pDC) return; GRect rSrc; rSrc.ZOff(d->Sx-1, d->Sy-1); rSrc.Offset(Image * d->Sx, 0); GImageListPriv::CacheDC *Cache = d->GetCache(Background, Disabled); if (!Cache) { GRect rDst; rDst.ZOff(d->Sx-1, d->Sy-1); rDst.Offset(Dx, Dy); pDC->Colour(Background); pDC->Rectangle(&rDst); pDC->Colour(GColour(255, 0, 0)); pDC->Line(rDst.x1, rDst.y1, rDst.x2, rDst.y2); pDC->Line(rDst.x2, rDst.y1, rDst.x1, rDst.y2); return; } if (pDC->SupportsAlphaCompositing()) { int Old = pDC->Op(GDC_ALPHA, Disabled ? 40 : -1); pDC->Blt(Dx, Dy, this, &rSrc); pDC->Op(Old); } else { pDC->Blt(Dx, Dy, Cache, &rSrc); } } int GImageList::TileX() { return d->Sx; } int GImageList::TileY() { return d->Sy; } int GImageList::GetItems() { return X() / d->Sx; } void GImageList::Update(int Flags) { } GRect *GImageList::GetBounds() { if (!d->Bounds.Length() && (*this)[0]) { int Items = GetItems(); if (d->Bounds.Length(Items)) { for (int i=0; iBounds[i].ZOff(d->Sx - 1, d->Sy - 1); d->Bounds[i].Offset(i * d->Sx, 0); LgiFindBounds(this, &d->Bounds[i]); d->Bounds[i].Offset(-i * d->Sx, 0); } } } return &d->Bounds[0]; } ///////////////////////////////////////////////////////////////////////////////////////////////////////// class GToolBarPrivate { public: int Bx, By; int Sx, Sy; bool Vertical; bool Text; int LastIndex; bool OwnImgList; GImageList *ImgList; GFont *Font; GToolTip *Tip; // Customization menu GDom *CustomDom; const char *CustomProp; // bitmap cache GAutoPtr IconCache; GToolBarPrivate() { Bx = By = 16; Sx = Sy = 10; Vertical = false; Text = false; Font = 0; Tip = 0; CustomProp = 0; CustomDom = 0; } bool ShowTextLabels() { return (Text || !ImgList) && Font; } void FixSeparators(GToolBar *Tb) { // Fix up separators so that no 2 separators are next to each other. I.e. // all the buttons between them are switched off. GToolButton *Last = 0; bool HasVis = false; GAutoPtr It(Tb->IterateViews()); for (GViewI *v = It->First(); v; v = It->Next()) { GToolButton *Btn = dynamic_cast(v); if (Btn) { if (Btn->Separator()) { Btn->Visible(HasVis); if (HasVis) { Last = Btn; } HasVis = false; } else { HasVis |= Btn->Visible(); } } } if (Last) { Last->Visible(HasVis); } } void Customizable(GToolBar *Tb) { GVariant v; if (CustomDom) { CustomDom->GetValue(CustomProp, v); } char *o; if ((o = v.Str())) { GToken t(o, ","); if (t.Length() >= 1) { Text = stricmp(t[0], "text") == 0; // Make all controls not visible. GViewI *v; GAutoPtr It(Tb->IterateViews()); for (v = It->First(); v; v = It->Next()) { GToolButton *Btn = dynamic_cast(v); if (Btn) v->Visible(false); } // Set sub-set of ctrls visible according to saved ID list for (int i=1; i 0) Tb->SetCtrlVisible(Id, true); } FixSeparators(Tb); } } } }; ///////////////////////////////////////////////////////////////////////////////////////////////////////// struct GToolButtonPriv { GArray Text; }; GToolButton::GToolButton(int Bx, int By) { d = new GToolButtonPriv; Type = TBT_PUSH; SetId(IDM_NONE); Down = false; Clicked = false; Over = false; ImgIndex = -1; NeedsRightClick = false; GRect r(0, 0, Bx+1, By+1); SetPos(r); SetParent(0); TipId = -1; _BorderSize = 0; LgiResources::StyleElement(this); } GToolButton::~GToolButton() { d->Text.DeleteObjects(); delete d; } bool GToolButton::Name(const char *n) { bool s = GView::Name(n); /* char *i = GView::Name(); char *o = i; while (*i) { if (*i != '&') *o++ = *i; *i++; } *o++ = 0; */ d->Text.DeleteObjects(); return s; } void GToolButton::Layout() { GToolBar *Par = dynamic_cast(GetParent()); // Text char *s = Name(); if (Par->d->ShowTextLabels() && s) { // Write each word centered on a different line char Buf[256]; strcpy_s(Buf, sizeof(Buf), s); GToken t(Buf, " "); if (t.Length() < 3) { if (t.Length() > 0) d->Text.Add(new GDisplayString(Par->d->Font, t[0])); if (t.Length() > 1) d->Text.Add(new GDisplayString(Par->d->Font, t[1])); } else if (t.Length() == 3) { sprintf_s(Buf, sizeof(Buf), "%s %s", t[0], t[1]); GDisplayString *d1 = new GDisplayString(Par->d->Font, Buf); sprintf_s(Buf, sizeof(Buf), "%s %s", t[1], t[2]); GDisplayString *d2 = new GDisplayString(Par->d->Font, Buf); if (d1 && d2) { if (d1->X() < d2->X()) { DeleteObj(d2); d->Text.Add(d1); d->Text.Add(new GDisplayString(Par->d->Font, t[2])); } else { DeleteObj(d1); d->Text.Add(new GDisplayString(Par->d->Font, t[0])); d->Text.Add(d2); } } } else { //GDisplayString *Cur = new GDisplayString(Par->d->Font, Buf); } } } void GToolButton::OnPaint(GSurface *pDC) { GToolBar *Par = dynamic_cast(GetParent()); bool e = Enabled(); if (Par) { GRect p = GetClient(); #if 0 // def _DEBUG pDC->Colour(GColour(255, 0, 255)); pDC->Rectangle(); #endif GColour cBack = StyleColour(GCss::PropBackgroundColor, GColour(LC_MED, 24)); if (e && Over) cBack = cBack.Mix(GColour::White); // Draw Background if (GetId() >= 0) { // Draw border GColour Background(cBack); if (Down) { // Sunken if the button is pressed LgiThinBorder(pDC, p, DefaultSunkenEdge); pDC->Colour(Background); pDC->Box(&p); } else { pDC->Colour(Background); pDC->Box(&p); p.Size(1, 1); } GRect IconPos; if (Par->d->ImgList) IconPos.Set(0, 0, Par->d->ImgList->TileX()-1, Par->d->ImgList->TileY()-1); else IconPos.ZOff(Par->d->Bx-1, Par->d->By-1); GRegion Unpainted(p); // Center the icon if (IconPos.X() < p.X() - 1) IconPos.Offset((p.X() - IconPos.X()) >> 1, 0); // Offset it if the button is pressed if (Down) IconPos.Offset(1, 1); // Draw any icon. if (ImgIndex >= 0) { if (Par->d->ImgList) { // Draw cached if (pDC->SupportsAlphaCompositing()) { pDC->Colour(Background); pDC->Rectangle(&IconPos); } Par->d->ImgList->Draw(pDC, IconPos.x1, IconPos.y1, ImgIndex, Background, !e); Unpainted.Subtract(&IconPos); // Fill in the rest of the area pDC->Colour(Background); for (GRect *r = Unpainted.First(); r; r = Unpainted.Next()) { pDC->Rectangle(r); } } else { // Draw a red cross indicating no icons. pDC->Colour(Background); pDC->Rectangle(&p); pDC->Colour(Rgb24(255, 0, 0), 24); pDC->Line(IconPos.x1, IconPos.y1, IconPos.x2, IconPos.y2); pDC->Line(IconPos.x2, IconPos.y1, IconPos.x1, IconPos.y2); } } else { pDC->Colour(Background); pDC->Rectangle(&p); } // Text if (Par->d->ShowTextLabels()) { if (Name() && !d->Text.Length()) { Layout(); } if (d->Text.Length()) { // Write each word centered on a different line int Ty = Down + Par->d->By + 2; COLOUR a = e ? LC_TEXT : LC_LOW; COLOUR b = LC_MED; Par->d->Font->Colour(a, b); for (int i=0; iText.Length(); i++) { GDisplayString *Ds = d->Text[i]; Ds->Draw(pDC, Down + ((X()-Ds->X())/2), Ty); Ty += Ds->Y(); } } } } else { // Separator int Px = X()-1; int Py = Y()-1; pDC->Colour(cBack); pDC->Rectangle(); GColour cLow = cBack.Mix(GColour::Black); GColour cHigh = cBack.Mix(GColour::White, 0.8f); if (X() > Y()) { int c = Y()/2-1; pDC->Colour(cLow); pDC->Line(2, c, Px-2, c); pDC->Colour(cHigh); pDC->Line(2, c+1, Px-2, c+1); } else { int c = X()/2-1; pDC->Colour(cLow); pDC->Line(c, 2, c, Py-2); pDC->Colour(cHigh); pDC->Line(c+1, 2, c+1, Py-2); } } } #if 0 // def _DEBUG pDC->Colour(GColour(255, 0, 255)); pDC->Box(); #endif } void GToolButton::Image(int i) { if (ImgIndex != i) { ImgIndex = i; Invalidate(); } } void GToolButton::Value(int64 b) { switch (Type) { case TBT_PUSH: { // do nothing... can't set value break; } case TBT_TOGGLE: { if (Value() != b) { Down = b != 0; Invalidate(); SendNotify(GNotifyValueChanged); } break; } case TBT_RADIO: { if (GetParent() && b) { // Clear any other radio buttons that are down GAutoPtr it(GetParent()->IterateViews()); if (it) { ssize_t CurIdx = it->IndexOf(this); if (CurIdx >= 0) { for (ssize_t i=CurIdx-1; i>=0; i--) { GToolButton *But = dynamic_cast((*it)[i]); if (But->Separator()) break; if (But->Type == TBT_RADIO && But->Down) But->Value(false); } for (size_t i=CurIdx+1; iLength(); i++) { GToolButton *But = dynamic_cast((*it)[i]); if (But->Separator()) break; if (But->Type == TBT_RADIO && But->Down) But->Value(false); } } } } Down = b != 0; GetParent()->Invalidate(); SendNotify(GNotifyValueChanged); break; } } } void GToolButton::SendCommand() { if (GetParent()) { GToolBar *t = dynamic_cast(GetParent()); if (t) t->OnButtonClick(this); } } void GToolButton::OnMouseClick(GMouse &m) { GToolBar *ToolBar = dynamic_cast(GetParent()); #if 0 printf("tool button click %i,%i down=%i, left=%i right=%i middle=%i, ctrl=%i alt=%i shift=%i Double=%i\n", m.x, m.y, m.Down(), m.Left(), m.Right(), m.Middle(), m.Ctrl(), m.Alt(), m.Shift(), m.Double()); #endif if (!NeedsRightClick && ToolBar && ToolBar->IsCustomizable() && m.IsContextMenu()) { m.ToScreen(); ToolBar->ContextMenu(m); } else { // left click action... if (GetId() >= 0 && Enabled()) { switch (Type) { case TBT_PUSH: { bool Old = Down; Clicked = m.Down(); Capture(m.Down()); if (Old && IsOver(m)) { // char *n = Name(); if (m.Left()) { SendCommand(); } SendNotify(m.Flags); } Down = m.Down(); if (Old != Down) { Invalidate(); } break; } case TBT_TOGGLE: { if (m.Down()) { if (m.Left()) { Value(!Down); SendCommand(); } SendNotify(m.Flags); } break; } case TBT_RADIO: { if (m.Down()) { if (!Down && m.Left()) { Value(true); SendCommand(); } SendNotify(m.Flags); } break; } } } } } void GToolButton::OnMouseEnter(GMouse &m) { if (!Separator() && Enabled()) { Over = true; Invalidate(); } if (Clicked) { Value(true); Invalidate(); } else { GToolBar *Bar = dynamic_cast(GetParent()); if (Bar) { Bar->OnMouseEnter(m); if (!Bar->TextLabels() && Bar->d->Tip && TipId < 0) { TipId = Bar->d->Tip->NewTip(Name(), GetPos()); } } if (GetParent()) { GToolBar *ToolBar = dynamic_cast(GetParent()); if (ToolBar) ToolBar->PostDescription(this, Name()); } } } void GToolButton::OnMouseMove(GMouse &m) { #ifdef BEOS if (GetParent()) { GToolBar *tb = dynamic_cast(GetParent()); if (tb) tb->PostDescription(this, Name()); } #endif } void GToolButton::OnMouseExit(GMouse &m) { if (Over) { Over = false; Invalidate(); } if (Clicked) { Value(false); Invalidate(); } else if (GetParent()) { GToolBar *ToolBar = dynamic_cast(GetParent()); if (ToolBar) ToolBar->PostDescription(this, ""); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////// GToolBar::GToolBar() { d = new GToolBarPrivate; Name("LGI_Toolbar"); _BorderSize = 1; _IsToolBar = 1; // Setup tool button font GFontType SysFontType; if (SysFontType.GetSystemFont("Small")) { d->Font = SysFontType.Create(); if (d->Font) { d->Font->PointSize(MIN(d->Font->PointSize(), SysFont->PointSize())); d->Font->Colour(0); d->Font->Bold(false); d->Font->Transparent(true); } } d->LastIndex = 0; d->OwnImgList = false; d->ImgList = 0; #if defined BEOS Handle()->SetViewColor(B_TRANSPARENT_COLOR); #endif GetCss(true)->BackgroundColor(GColour(LC_MED,24).Mix(GColour::Black, 0.05f)); LgiResources::StyleElement(this); } GToolBar::~GToolBar() { DeleteObj(d->Tip); if (d->OwnImgList) DeleteObj(d->ImgList); DeleteObj(d->Font); DeleteObj(d); } void GToolBar::OnCreate() { #ifndef WIN32 AttachChildren(); #endif } int GToolBar::GetBx() { return d->Bx; } int GToolBar::GetBy() { return d->By; } void GToolBar::ContextMenu(GMouse &m) { if (IsCustomizable()) { GSubMenu *Sub = new GSubMenu; if (Sub) { int n = 1; GViewI *v; for (v = Children.First(); v; v = Children.Next(), n++) { GToolButton *Btn = dynamic_cast(v); if (Btn && Btn->Separator()) { Sub->AppendSeparator(); } else { GMenuItem *Item = Sub->AppendItem(v->Name(), n, true); if (Item) { Item->Checked(v->Visible()); } } } Sub->AppendSeparator(); GMenuItem *Txt = Sub->AppendItem(LgiLoadString(L_TOOLBAR_SHOW_TEXT, "Show Text Labels"), 1000, true); Txt->Checked(d->Text); bool Save = false; int Pick = Sub->Float(this, m); switch (Pick) { case 1000: { d->Text = !d->Text; Save = true; SendNotify(GNotifyTableLayout_Refresh); break; } default: { GViewI *Ctrl = Children[Pick - 1]; if (Ctrl) { Ctrl->Visible(!Ctrl->Visible()); Save = true; } break; } } DeleteObj(Sub); if (Save) { GStringPipe p(256); p.Push((char*) (d->Text ? "text" : "no")); for (v = Children.First(); v; v = Children.Next()) { if (v->Visible()) { p.Print(",%i", v->GetId()); } } char *o = p.NewStr(); if (o) { if (d->CustomDom) { GVariant v(o); d->CustomDom->SetValue(d->CustomProp, v); } DeleteArray(o); } d->FixSeparators(this); for (GViewI *v = Children.First(); v; v = Children.Next()) { GToolButton *b = dynamic_cast(v); if (b && b->TipId >= 0) { d->Tip->DeleteTip(b->TipId); b->TipId = -1; } } GetWindow()->PourAll(); } } } } bool GToolBar::IsCustomizable() { return d->CustomDom != 0 && d->CustomProp; } void GToolBar::Customizable(GDom *Store, const char *Option) { d->CustomDom = Store; d->CustomProp = Option; d->Customizable(this); } bool GToolBar::IsVertical() { return d->Vertical; } void GToolBar::IsVertical(bool v) { d->Vertical = v; } bool GToolBar::TextLabels() { return d->Text; } void GToolBar::TextLabels(bool i) { d->Text = i; } GFont *GToolBar::GetFont() { return d->Font; } bool GToolBar::OnLayout(GViewLayoutInfo &Inf) { if (Inf.Width.Min == 0) { // Calc width GRegion r(0, 0, 10000, 10000); Pour(r); Inf.Width.Min = X(); Inf.Width.Max = X(); } else { // Calc height Inf.Height.Min = Y(); Inf.Height.Max = Y(); } return true; } #define GetBorderSpacing() GetCss() && GetCss()->BorderSpacing().IsValid() ? \ GetCss()->BorderSpacing().ToPx(X(), GetFont()) : \ 1 bool GToolBar::Pour(GRegion &r) { int BorderSpacing = GetBorderSpacing(); int EndX = 0; int EndY = 0; int MaxDim = 0; GCssTools Tools(this); GRect Border = Tools.GetBorder(r); GRect Padding = Tools.GetPadding(r); int PosX = BorderSpacing + Border.x1 + Padding.x1; int PosY = BorderSpacing + Border.y1 + Padding.y1; GRect ButPos; GViewI *But = Children.First(); while (But) { if (But->Visible()) { int Tx = 0, Ty = 0; GToolButton *Btn = dynamic_cast(But); if (d->ShowTextLabels()) { if (Btn) { if (Btn->d->Text.Length() == 0) { Btn->Layout(); } for (int i=0; id->Text.Length(); i++) { GDisplayString *Ds = Btn->d->Text[i]; Tx = MAX(Ds->X() + 4, Tx); Ty += Ds->Y(); } } } ButPos = But->GetPos(); if (Btn) { if (Btn->Separator()) { // This will be stretched out later by the code that makes // everything the same height. ButPos.ZOff(BORDER_SEPARATOR+1, BORDER_SEPARATOR+1); } else { if (Btn->Image() >= 0) { // Set initial size to the icon size ButPos.ZOff(d->Bx + 2, d->By + 2); } else { // Otherwise default to text size if (d->Vertical) ButPos.ZOff(0, 7); else ButPos.ZOff(7, 0); } Tx += 4; if (ButPos.X() < Tx) { // Make button wider for text label ButPos.x2 = Tx - 1; } ButPos.y2 += Ty; } } if (d->Vertical) MaxDim = MAX(MaxDim, ButPos.X()); else MaxDim = MAX(MaxDim, ButPos.Y()); ButPos.Offset(PosX - ButPos.x1, PosY - ButPos.y1); if (But->GetId() == IDM_BREAK) { ButPos.ZOff(0, 0); if (d->Vertical) { PosX = MaxDim; PosY = BORDER_SHADE + BorderSpacing; } else { PosX = BORDER_SHADE + BorderSpacing; PosY = MaxDim; } } else { if (d->Vertical) PosY = ButPos.y2 + BorderSpacing; else PosX = ButPos.x2 + BorderSpacing; } But->SetPos(ButPos); } else { GRect p(-100, -100, -90, -90); But->SetPos(p); } But = Children.Next(); } for (GViewI *w = Children.First(); w; w = Children.Next()) { GRect p = w->GetPos(); if (d->Vertical) { if (w->X() < MaxDim) { p.x2 = p.x1 + MaxDim - 1; w->SetPos(p); } } else { if (w->Y() < MaxDim) { p.y2 = p.y1 + MaxDim - 1; w->SetPos(p); } } EndX = MAX(EndX, p.x2); EndY = MAX(EndY, p.y2); } d->Sx = EndX + BorderSpacing; d->Sy = EndY + BorderSpacing; d->Sx += Border.x2 + Padding.x2; d->Sy += Border.y2 + Padding.y2; GRect n; n.ZOff(MAX(7, d->Sx), MAX(7, d->Sy)); GRect *Best = FindLargestEdge(r, GV_EDGE_TOP); if (Best) { n.Offset(Best->x1, Best->y1); n.Bound(Best); SetPos(n, true); // _Dump(); return true; } return false; } void GToolBar::OnButtonClick(GToolButton *Btn) { GViewI *w = (GetNotify()) ? GetNotify() : GetParent(); if (w && Btn) { int Id = Btn->GetId(); w->PostEvent(M_COMMAND, (GMessage::Param) Id, (GMessage::Param) Handle()); } } int GToolBar::PostDescription(GView *Ctrl, const char *Text) { if (GetParent()) { return GetParent()->PostEvent(M_DESCRIBE, (GMessage::Param) Ctrl, (GMessage::Param) Text); } return 0; } GMessage::Result GToolBar::OnEvent(GMessage *Msg) { switch (MsgCode(Msg)) { case M_CHANGE: { if (GetParent()) return GetParent()->OnEvent(Msg); break; } } return GView::OnEvent(Msg); } void GToolBar::OnPaint(GSurface *pDC) { GRect c = GetClient(); GCssTools Tools(this); Tools.PaintBorder(pDC, c); Tools.PaintPadding(pDC, c); pDC->Colour(Tools.GetBack()); pDC->Rectangle(&c); } void GToolBar::OnMouseClick(GMouse &m) { } void GToolBar::OnMouseEnter(GMouse &m) { if (!d->Tip) { d->Tip = new GToolTip; if (d->Tip) { d->Tip->Attach(this); } } } void GToolBar::OnMouseExit(GMouse &m) { } void GToolBar::OnMouseMove(GMouse &m) { } bool GToolBar::SetBitmap(char *File, int bx, int by) { bool Status = false; GSurface *pDC = GdcD->Load(File); if (pDC) { Status = SetDC(pDC, bx, by); DeleteObj(pDC); } return Status; } bool GToolBar::SetDC(GSurface *pNewDC, int bx, int by) { if (d->OwnImgList) { DeleteObj(d->ImgList); } d->Bx = bx; d->By = by; if (pNewDC) { d->ImgList = new GImageList(bx, by, pNewDC); if (d->ImgList) { d->OwnImgList = true; return true; } } return false; } GImageList *GToolBar::GetImageList() { return d->ImgList; } bool GToolBar::SetImageList(GImageList *l, int bx, int by, bool Own) { if (d->OwnImgList) DeleteObj(d->ImgList); d->OwnImgList = Own; d->Bx = bx; d->By = by; d->ImgList = l; return d->ImgList != 0; } GToolButton *GToolBar::AppendButton(const char *Tip, int Id, int Type, int Enabled, int IconId) { // bool HasIcon = IconId != TOOL_ICO_NONE; GToolButton *But = new GToolButton(d->Bx, d->By); if (But) { But->Name(Tip); But->SetId(Id); But->Type = Type; But->SetParent(this); But->Enabled(Enabled != 0); if (IconId >= 0) { But->ImgIndex = IconId; } else if (IconId == TOOL_ICO_NEXT) { But->ImgIndex = d->LastIndex++; } else if (IconId == TOOL_ICO_NONE) { But->ImgIndex = -1; } AttachButton(But); } return But; } bool GToolBar::AppendSeparator() { GToolButton *But = new GToolButton(d->Bx, d->By); if (But) { But->SetId(IDM_SEPARATOR); But->SetParent(this); AttachButton(But); return true; } return false; } bool GToolBar::AppendBreak() { GToolButton *But = new GToolButton(d->Bx, d->By); if (But) { But->SetId(IDM_BREAK); But->SetParent(this); AttachButton(But); return true; } return false; } bool GToolBar::AppendControl(GView *Ctrl) { bool Status = false; if (Ctrl) { Ctrl->SetParent(this); AttachButton(Ctrl); Status = true; } return Status; } void GToolBar::Empty() { for (GViewI *But = Children.First(); But; But = Children.Next()) { DeleteObj(But); } } #ifdef MAC bool GToolBar::Attach(GViewI *parent) { return GLayout::Attach(parent); } #endif /////////////////////////////////////////////////////////////////////// COLOUR Map(GSurface *pDC, COLOUR c) { if (pDC && pDC->GetBits() <= 8) { if (pDC->IsScreen()) { c = CBit(24, c); } #ifdef WIN32 else { HPALETTE hPal = GetSystemPalette(); if (hPal) { c = GetNearestPaletteIndex(hPal, c); DeleteObject(hPal); } } #endif } return c; } #ifdef WIN32 HPALETTE GetSystemPalette() { HPALETTE hPal = 0; LOGPALETTE *Log = (LOGPALETTE*) new uchar[sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * 255)]; if (Log) { Log->palVersion = 0x300; Log->palNumEntries = 256; HDC hDC = CreateCompatibleDC(0); GetSystemPaletteEntries(hDC, 0, 256, Log->palPalEntry); DeleteDC(hDC); hPal = CreatePalette(Log); } return hPal; } bool BltBmpToBmp(HBITMAP hDest, int xDst, int yDst, int cx, int cy, HBITMAP hSrc, int xSrc, int ySrc, DWORD dwRop) { bool Status = false; HDC DestDC = CreateCompatibleDC(0); HDC SrcDC = CreateCompatibleDC(0); if (DestDC && SrcDC) { hDest = (HBITMAP) SelectObject(DestDC, hDest); hSrc = (HBITMAP) SelectObject(SrcDC, hSrc); Status = BitBlt(DestDC, xDst, yDst, cx, cy, SrcDC, xSrc, ySrc, dwRop) != 0; hDest = (HBITMAP) SelectObject(DestDC, hDest); hSrc = (HBITMAP) SelectObject(SrcDC, hSrc); } if (DestDC) { DeleteDC(DestDC); } if (SrcDC) { DeleteDC(SrcDC); } return Status; } bool BltBmpToDc(HDC DestDC, int xDst, int yDst, int cx, int cy, HBITMAP hSrc, int xSrc, int ySrc, DWORD dwRop) { bool Status = false; HDC SrcDC = CreateCompatibleDC(0); if (DestDC && SrcDC) { hSrc = (HBITMAP) SelectObject(SrcDC, hSrc); Status = BitBlt(DestDC, xDst, yDst, cx, cy, SrcDC, xSrc, ySrc, dwRop) != 0; hSrc = (HBITMAP) SelectObject(SrcDC, hSrc); } if (SrcDC) { DeleteDC(SrcDC); } return Status; } bool BltDcToBmp(HBITMAP hDest, int xDst, int yDst, int cx, int cy, HDC SrcDC, int xSrc, int ySrc, DWORD dwRop) { bool Status = false; HDC DestDC = CreateCompatibleDC(0); if (DestDC && SrcDC) { hDest = (HBITMAP) SelectObject(DestDC, hDest); Status = BitBlt(DestDC, xDst, yDst, cx, cy, SrcDC, xSrc, ySrc, dwRop) != 0; hDest = (HBITMAP) SelectObject(DestDC, hDest); } if (DestDC) { DeleteDC(DestDC); } return Status; } #endif diff --git a/src/common/Widgets/GTree.cpp b/src/common/Widgets/GTree.cpp --- a/src/common/Widgets/GTree.cpp +++ b/src/common/Widgets/GTree.cpp @@ -1,2234 +1,2234 @@ #include #include "Lgi.h" #include "GTree.h" #include "GScrollBar.h" #include "GDisplayString.h" #include "GPalette.h" #include "LgiRes.h" #define TREE_BLOCK 16 #define DRAG_THRESHOLD 4 #define DRAG_SCROLL_EDGE 20 #define DRAG_SCROLL_X 8 #define DRAG_SCROLL_Y 1 /* #ifdef LINUX */ #define TreeUpdateNow false /* #else #define TreeUpdateNow true #endif */ #define TREELOCK LMutex::Auto Lck(d, _FL); #define ForAll(Items) for (auto c : Items) ////////////////////////////////////////////////////////////////////////////// // Private class definitions for binary compatibility class GTreePrivate : public LMutex { public: // Private data int LineFlags[4]; bool LayoutDirty; GdcPt2 Limit; GdcPt2 LastClick; GdcPt2 DragStart; int DragData; GMemDC *IconCache; bool InPour; int64 DropSelectTime; int8 IconTextGap; int LastLayoutPx; GMouse *CurrentClick; // Visual style GTree::ThumbStyle Btns; bool JoiningLines; // Pointers into items... be careful to clear when deleting items... GTreeItem *LastHit; List Selection; GTreeItem *DropTarget; GTreePrivate() : LMutex("GTreePrivate") { CurrentClick = NULL; LastLayoutPx = -1; DropSelectTime = 0; InPour = false; LastHit = 0; DropTarget = 0; IconCache = 0; LayoutDirty = true; IconTextGap = 0; Btns = GTree::TreeTriangle; JoiningLines = false; } ~GTreePrivate() { DeleteObj(IconCache); } }; class GTreeItemPrivate { GArray Ds; - GArray ColPx; + GArray ColPx; public: GTreeItem *Item; GRect Pos; GRect Thumb; GRect Text; GRect Icon; bool Open; bool Selected; bool Visible; bool Last; int Depth; GTreeItemPrivate(GTreeItem *it) { Item = it; Ds = NULL; Pos.ZOff(-1, -1); Open = false; Selected = false; Visible = false; Last = false; Depth = 0; Text.ZOff(-1, -1); Icon.ZOff(-1, -1); } ~GTreeItemPrivate() { Ds.DeleteObjects(); } GDisplayString *GetDs(int Col, int FixPx) { if (!Ds[Col]) { GFont *f = Item->GetTree() ? Item->GetTree()->GetFont() : SysFont; Ds[Col] = new GDisplayString(f, Item->GetText(Col)); if (Ds[Col]) { ColPx[Col] = Ds[Col]->X(); if (FixPx > 0) { Ds[Col]->TruncateWithDots(FixPx); } } } return Ds[Col]; } void ClearDs(int Col = -1) { if (Col >= 0) { delete Ds[Col]; Ds[Col] = NULL; } else { Ds.DeleteObjects(); } } int GetColumnPx(int Col) { int BasePx = 0; GetDs(Col, 0); if (Col == 0) { BasePx = (Depth + 1) * TREE_BLOCK; } return ColPx[Col] + BasePx; } }; ////////////////////////////////////////////////////////////////////////////// GTreeNode::GTreeNode() { Parent = NULL; Tree = NULL; } GTreeNode::~GTreeNode() { } void GTreeNode::SetLayoutDirty() { Tree->d->LayoutDirty = true; } void GTreeNode::_Visible(bool v) { for (GTreeItem *i=GetChild(); i; i=i->GetNext()) { LgiAssert(i != this); i->OnVisible(v); i->_Visible(v); } } void GTreeNode::_ClearDs(int Col) { List::I it = Items.begin(); for (GTreeItem *c = *it; c; c = *++it) { c->_ClearDs(Col); } } GItemContainer *GTreeItem::GetContainer() { return Tree; } GTreeItem *GTreeNode::Insert(GTreeItem *Obj, int Idx) { LgiAssert(Obj != this); if (Obj) { if (Obj->Tree) { Obj->Remove(); } } GTreeItem *NewObj = (Obj) ? Obj : new GTreeItem; if (NewObj) { NewObj->Parent = Item(); NewObj->_SetTreePtr(Tree); Items.Delete(NewObj); Items.Insert(NewObj, Idx); if (Tree) { Tree->d->LayoutDirty = true; if (Pos() && Pos()->Y() > 0) { Tree->_UpdateBelow(Pos()->y1); } else { Tree->Invalidate(); } } } return NewObj; } void GTreeNode::Detach() { if (Parent) { GTreeItem *It = Item(); if (It) { LgiAssert(Parent->Items.HasItem(It)); Parent->Items.Delete(It); } Parent = 0; } if (Tree) { Tree->d->LayoutDirty = true; Tree->Invalidate(); } if (Item()) Item()->_SetTreePtr(0); } void GTreeNode::Remove() { int y = 0; if (Parent) { GTreeItem *i = Item(); if (i && i->IsRoot()) { GRect *p = Pos(); GTreeItem *Prev = GetPrev(); if (Prev) { y = Prev->d->Pos.y1; } else { y = p->y1; } } else { y = Parent->d->Pos.y1; } } GTree *t = Tree; if (Item()) Item()->_Remove(); if (t) { t->_UpdateBelow(y); } } bool GTreeNode::IsRoot() { return Parent == 0 || (GTreeNode*)Parent == (GTreeNode*)Tree; } size_t GTreeNode::GetItems() { return Items.Length(); } int GTreeNode::ForEach(std::function Fn) { int Count = 0; for (auto t : Items) { Fn(t); Count += t->ForEach(Fn); } return Count + 1; } ssize_t GTreeNode::IndexOf() { if (Parent) { return Parent->Items.IndexOf(Item()); } else if (Tree) { return Tree->Items.IndexOf(Item()); } return -1; } GTreeItem *GTreeNode::GetChild() { return Items.First(); } GTreeItem *GTreeNode::GetPrev() { List *l = (Parent) ? &Parent->Items : (Tree) ? &Tree->Items : 0; if (l) { ssize_t Index = l->IndexOf(Item()); if (Index >= 0) { return l->ItemAt(Index-1); } } return 0; } GTreeItem *GTreeNode::GetNext() { List *l = (Parent) ? &Parent->Items : (Tree) ? &Tree->Items : 0; if (l) { ssize_t Index = l->IndexOf(Item()); if (Index >= 0) { return l->ItemAt(Index+1); } } return 0; } ////////////////////////////////////////////////////////////////////////////// GTreeItem::GTreeItem() { d = new GTreeItemPrivate(this); Str = 0; Sys_Image = -1; } GTreeItem::~GTreeItem() { if (Tree) { if (Tree->d->DropTarget == this) { Tree->d->DropTarget = 0; } if (Tree->d->LastHit == this) { Tree->d->LastHit = 0; } if (Tree->IsCapturing()) Tree->Capture(false); } int y = 0; GTree *t = 0; if (Parent && (GTreeNode*)Parent != (GTreeNode*)Tree) { t = Tree; y = Parent->d->Pos.y1; } else if ((GTreeNode*)this != (GTreeNode*)Tree) { t = Tree; GTreeItem *p = GetPrev(); if (p) { y = p->d->Pos.y1; } else { y = d->Pos.y1; } } _Remove(); GTreeItem *c; while ((c = Items.First())) delete c; DeleteObj(d); if (t) { t->_UpdateBelow(y); } } int GTreeItem::GetColumnSize(int Col) { int Px = d->GetColumnPx(Col); if (Expanded()) { ForAll(Items) { int ChildPx = c->GetColumnSize(Col); Px = MAX(ChildPx, Px); } } return Px; } GRect *GTreeItem::Pos() { return &d->Pos; } GdcPt2 GTreeItem::_ScrollPos() { GdcPt2 p; if (Tree) p = Tree->_ScrollPos(); return p; } GRect *GTreeItem::_GetRect(GTreeItemRect Which) { switch (Which) { case TreeItemPos: return &d->Pos; case TreeItemThumb: return &d->Thumb; case TreeItemText: return &d->Text; case TreeItemIcon: return &d->Icon; } return 0; } bool GTreeItem::SortChildren(int (*compare)(GTreeItem *a, GTreeItem *b, NativeInt data), NativeInt data) { Items.Sort(compare, data); if (Tree) { Tree->_Pour(); Tree->Invalidate(); } return true; } bool GTreeItem::IsDropTarget() { GTree *t = GetTree(); if (t && t->d && t->d->DropTarget == this) return true; return false; } GRect *GTreeItem::GetPos(int Col) { if (!d->Pos.Valid() && Tree) Tree->_Pour(); static GRect r; r = d->Pos; if (Col >= 0) { GItemColumn *Column = 0; int Cx = Tree->GetImageList() ? 16 : 0; for (int c=0; cColumnAt(c); if (Column) { Cx += Column->Width(); } } Column = Tree->ColumnAt(Col); if (Column) { r.x1 = Cx; r.x2 = Cx + Column->Width() - 1; } } return &r; } void GTreeItem::_RePour() { if (Tree) { Tree->_Pour(); } } void GTreeItem::ScrollTo() { if (Tree && Tree->VScroll) { GRect c = Tree->GetClient(); GRect p = d->Pos; int y = d->Pos.Y() ? d->Pos.Y() : 16; p.Offset(0, (int) (-Tree->VScroll->Value() * y)); if (p.y1 < c.y1) { int Lines = (c.y1 - p.y1 + y - 1) / y; Tree->VScroll->Value(Tree->VScroll->Value() - Lines); } else if (p.y2 > c.y2) { int Lines = (p.y2 - c.y2 + y - 1) / y; Tree->VScroll->Value(Tree->VScroll->Value() + Lines); } } } void GTreeItem::_SetTreePtr(GTree *t) { if (Tree && !t) { // Clearing tree pointer, must remove all references to this item that // the tree might still have. if (d->Selected) { Tree->d->Selection.Delete(this); d->Selected = false; } if (Tree->d->LastHit == this) { Tree->d->LastHit = 0; } if (Tree->d->DropTarget == this) { Tree->d->DropTarget = 0; } } Tree = t; List::I it = Items.begin(); for (GTreeItem *i=*it; i; i=*++it) { i->_SetTreePtr(t); } } void GTreeItem::_Remove() { if ((GTreeNode*)this != (GTreeNode*)Tree) { if (Parent) { LgiAssert(Parent->Items.HasItem(this)); Parent->Items.Delete(this); } else if (Tree) { LgiAssert(Tree->Items.HasItem(this)); Tree->Items.Delete(this); } if (Tree) { LgiAssert(Tree->d != NULL); Tree->d->LayoutDirty = true; if (Tree->IsCapturing()) Tree->Capture(false); } } Parent = 0; _SetTreePtr(0); } void GTreeItem::_PourText(GdcPt2 &Size) { GFont *f = Tree ? Tree->GetFont() : SysFont; auto *Txt = GetText(); #if defined(_WIN64) && defined(_DEBUG) if ((void*)Txt == (void*)0xfeeefeeefeeefeee || (void*)Txt == (void*)0xcdcdcdcdcdcdcdcd) { LgiAssert(!"Yeah nah..."); } #endif GDisplayString ds(f, Txt); Size.x = ds.X() + 4; Size.y = 0; } void GTreeItem::_PaintText(GItem::ItemPaintCtx &Ctx) { char *Text = GetText(); if (Text) { GDisplayString *Ds = d->GetDs(0, d->Text.X()); GFont *f = Tree ? Tree->GetFont() : SysFont; int Tab = f->TabSize(); f->TabSize(0); f->Transparent(false); f->Colour(Ctx.Fore, Ctx.Back); if (Ds) { Ds->Draw(Ctx.pDC, d->Text.x1 + 2, d->Text.y1 + 1, &d->Text); if (Ctx.x2 > d->Text.x2) { GRect r = Ctx; r.x1 = d->Text.x2 + 1; if (Ctx.Columns > 1) Ctx.pDC->Colour(Ctx.Back); else Ctx.pDC->Colour(LC_WORKSPACE, 24); Ctx.pDC->Rectangle(&r); } } else { Ctx.pDC->Colour(Ctx.Back); } f->TabSize(Tab); } else { Ctx.pDC->Colour(Ctx.Back); Ctx.pDC->Rectangle(&Ctx); } } void GTreeItem::_Pour(GdcPt2 *Limit, int ColumnPx, int Depth, bool Visible) { d->Visible = Visible; d->Depth = Depth; if (d->Visible) { GdcPt2 TextSize; _PourText(TextSize); GImageList *ImgLst = Tree->GetImageList(); // int IconX = (ImgLst && GetImage() >= 0) ? ImgLst->TileX() + Tree->d->IconTextGap : 0; int IconY = (ImgLst && GetImage() >= 0) ? ImgLst->TileY() : 0; int Height = MAX(TextSize.y, IconY); if (!Height) Height = 16; GDisplayString *Ds = d->GetDs(0, 0); d->Pos.ZOff(ColumnPx - 1, (Ds ? MAX(Height, Ds->Y()) : Height) - 1); d->Pos.Offset(0, Limit->y); if (!d->Pos.Valid()) { printf("Invalid pos: %s, ColumnPx=%i\n", d->Pos.GetStr(), ColumnPx); } Limit->x = MAX(Limit->x, d->Pos.x2 + 1); Limit->y = MAX(Limit->y, d->Pos.y2 + 1); } else { d->Pos.ZOff(-1, -1); } GTreeItem *n; List::I it = Items.begin(); for (GTreeItem *i=*it; i; i=n) { n = *++it; i->d->Last = n == 0; i->_Pour(Limit, ColumnPx, Depth+1, d->Open && d->Visible); } } void GTreeItem::_ClearDs(int Col) { d->ClearDs(Col); GTreeNode::_ClearDs(Col); } char *GTreeItem::GetText(int i) { return Str[i]; } bool GTreeItem::SetText(const char *s, int i) { if (!Str[i].Reset(NewStr(s))) return false; if (Tree) Update(); return true; } int GTreeItem::GetImage(int Flags) { return Sys_Image; } void GTreeItem::SetImage(int i) { Sys_Image = i; } void GTreeItem::Update() { if (Tree) { GRect p = d->Pos; p.x2 = 1000; d->ClearDs(); Tree->_Update(&p, TreeUpdateNow); } } bool GTreeItem::Select() { return d->Selected; } void GTreeItem::Select(bool b) { if (d->Selected != b) { d->Selected = b; if (b) { GTreeItem *p = this; while ((p = p->GetParent())) { p->Expanded(true); } } Update(); if (b && Tree) { Tree->_OnSelect(this); Tree->OnItemSelect(this); } } } bool GTreeItem::Expanded() { return d->Open; } void GTreeItem::Expanded(bool b) { if (d->Open != b) { d->Open = b; if (Items.Length() > 0) { if (Tree) { Tree->d->LayoutDirty = true; Tree->_UpdateBelow(d->Pos.y1); } OnExpand(b); } } } void GTreeItem::OnExpand(bool b) { _Visible(b); } GTreeItem *GTreeItem::_HitTest(int x, int y, bool Debug) { GTreeItem *Status = 0; if (d->Pos.Overlap(x, y) && x > (d->Depth*TREE_BLOCK)) { Status = this; } if (d->Open) { List::I it = Items.begin(); for (GTreeItem *i=*it; i && !Status; i=*++it) { Status = i->_HitTest(x, y, Debug); } } return Status; } void GTreeItem::_MouseClick(GMouse &m) { if (m.Down()) { if ((Items.Length() > 0 && d->Thumb.Overlap(m.x, m.y)) || m.Double()) { Expanded(!Expanded()); } GRect rText = d->Text; if (Tree && Tree->Columns.Length() > 0) rText.x2 = Tree->X(); if (rText.Overlap(m.x, m.y) || d->Icon.Overlap(m.x, m.y)) { Select(true); if (Tree) { Tree->OnItemClick(this, m); } } } } void GTreeItem::OnPaint(ItemPaintCtx &Ctx) { LgiAssert(Tree != NULL); // background up to text GSurface *&pDC = Ctx.pDC; pDC->Colour(LC_WORKSPACE, 24); pDC->Rectangle(0, d->Pos.y1, (d->Depth*TREE_BLOCK)+TREE_BLOCK, d->Pos.y2); // draw trunk GRect Pos = d->Pos; Pos.x2 = Pos.x1 + Ctx.ColPx[0] - 1; int x = 0; COLOUR Lines = LC_MED; pDC->Colour(Lines, 24); if (Tree->d->JoiningLines) { for (int i=0; iDepth; i++) { if (Tree->d->LineFlags[0] & (1 << i)) { pDC->Line(x + 8, Pos.y1, x + 8, Pos.y2); } x += TREE_BLOCK; } } else { x += TREE_BLOCK * d->Depth; } // draw node int cy = Pos.y1 + (Pos.Y() >> 1); if (Items.Length() > 0) { d->Thumb.ZOff(8, 8); d->Thumb.Offset(x + 4, cy - 4); switch (Tree->d->Btns) { case GTree::TreePlus: { // plus/minus symbol pDC->Colour(LC_LOW, 24); pDC->Box(&d->Thumb); pDC->Colour(LC_WHITE, 24); pDC->Rectangle(d->Thumb.x1+1, d->Thumb.y1+1, d->Thumb.x2-1, d->Thumb.y2-1); pDC->Colour(LC_SHADOW, 24); pDC->Line( d->Thumb.x1+2, d->Thumb.y1+4, d->Thumb.x1+6, d->Thumb.y1+4); if (!d->Open) { // not open, so draw the cross bar making the '-' into a '+' pDC->Colour(LC_SHADOW, 24); pDC->Line( d->Thumb.x1+4, d->Thumb.y1+2, d->Thumb.x1+4, d->Thumb.y1+6); } break; } case GTree::TreeTriangle: { // Triangle style expander pDC->Colour(LC_LOW, 24); int Off = 2; if (d->Open) { for (int y=0; yThumb.Y(); y++) { int x1 = d->Thumb.x1 + y; int x2 = d->Thumb.x2 - y; if (x2 < x1) break; pDC->HLine(x1, x2, d->Thumb.y1 + y + Off); } } else { for (int x=0; xThumb.X(); x++) { int y1 = d->Thumb.y1 + x; int y2 = d->Thumb.y2 - x; if (y2 < y1) break; pDC->VLine(d->Thumb.x1 + x + Off, y1, y2); } } break; } } pDC->Colour(Lines, 24); if (Tree->d->JoiningLines) { if (Parent || IndexOf() > 0) { // draw line to item above pDC->Line(x + 8, Pos.y1, x + 8, d->Thumb.y1-1); } // draw line to leaf beside pDC->Line(d->Thumb.x2+1, cy, x + (TREE_BLOCK-1), cy); if (!d->Last) { // draw line to item below pDC->Line(x + 8, d->Thumb.y2+1, x + 8, Pos.y2); } } } else if (Tree->d->JoiningLines) { // leaf node pDC->Colour(LC_MED, 24); if (d->Last) { pDC->Rectangle(x + 8, Pos.y1, x + 8, cy); } else { pDC->Rectangle(x + 8, Pos.y1, x + 8, Pos.y2); } pDC->Rectangle(x + 8, cy, x + (TREE_BLOCK-1), cy); } x += TREE_BLOCK; // draw icon int Image = GetImage(Select()); GImageList *Lst = Tree->GetImageList(); if (Image >= 0 && Lst) { d->Icon.ZOff(Lst->TileX() + Tree->d->IconTextGap - 1, Pos.Y() - 1); d->Icon.Offset(x, Pos.y1); GColour Background(LC_WORKSPACE, 24); pDC->Colour(Background); if (Tree->d->IconCache) { // no flicker GRect From; From.ZOff(Lst->TileX()-1, Tree->d->IconCache->Y()-1); From.Offset(Lst->TileX()*Image, 0); pDC->Blt(d->Icon.x1, d->Icon.y1, Tree->d->IconCache, &From); pDC->Rectangle(d->Icon.x1 + Lst->TileX(), d->Icon.y1, d->Icon.x2, d->Icon.y2); } else { // flickers... int Px = d->Icon.y1 + ((Lst->TileY()-Pos.Y()) >> 1); pDC->Rectangle(&d->Icon); Tree->GetImageList()->Draw(pDC, d->Icon.x1, Px, Image, Background); } x += d->Icon.X(); } // text: first column GdcPt2 TextSize; _PourText(TextSize); d->Text.ZOff(TextSize.x-1, Pos.Y()-1); d->Text.Offset(x, Pos.y1); (GRect&)Ctx = d->Text; Ctx.x2 = Ctx.ColPx[0] - 1; _PaintText(Ctx); x = Pos.x2 + 1; // text: other columns for (int i=1; iColumns[i]); x = Ctx.x2 + 1; } // background after text pDC->Colour(LC_WORKSPACE, 24); pDC->Rectangle(x, Pos.y1, MAX(Tree->X(), Tree->d->Limit.x), Pos.y2); // children if (d->Open) { if (!d->Last) { Tree->d->LineFlags[0] |= 1 << d->Depth; } COLOUR SelFore = Tree->Focus() ? LC_FOCUS_SEL_FORE : LC_NON_FOCUS_SEL_FORE; COLOUR SelBack = Tree->Focus() ? LC_FOCUS_SEL_BACK : LC_NON_FOCUS_SEL_BACK; List::I it = Items.begin(); for (GTreeItem *i=*it; i; i=*++it) { bool IsSelected = (Tree->d->DropTarget == i) || (Tree->d->DropTarget == 0 && i->Select()); // Foreground GCss::ColorDef Fill = i->GetCss(true)->Color(); Ctx.Fore.Set(Fill.Type == GCss::ColorRgb ? Rgb32To24(Fill.Rgb32) : (IsSelected ? SelFore : LC_TEXT), 24); // Background Fill = i->GetCss()->BackgroundColor(); Ctx.Back.Set(Fill.Type == GCss::ColorRgb ? Rgb32To24(Fill.Rgb32) : (IsSelected ? SelBack : LC_WORKSPACE), 24); i->OnPaint(Ctx); } Tree->d->LineFlags[0] &= ~(1 << d->Depth); } } void GTreeItem::OnPaintColumn(GItem::ItemPaintCtx &Ctx, int i, GItemColumn *c) { GDisplayString *ds = d->GetDs(i, Ctx.ColPx[i]); if (ds) { GFont *f = ds->GetFont(); f->Colour(Ctx.Fore, Ctx.Back); ds->Draw(Ctx.pDC, Ctx.x1 + 2, Ctx.y1 + 1, &Ctx); } } ////////////////////////////////////////////////////////////////////////////// GTree::GTree(int id, int x, int y, int cx, int cy, const char *name) : ResObject(Res_TreeView) { d = new GTreePrivate; SetId(id); GRect e(x, y, x+cx, y+cy); SetPos(e); if (name) Name(name); else Name("LGI.GTree"); Sunken(true); Tree = this; Lines = true; Buttons = true; LinesAtRoot = true; EditLabels = false; ColumnHeaders = false; rItems.ZOff(-1, -1); #if WINNATIVE SetStyle(GetStyle() | WS_CHILD | WS_VISIBLE | WS_TABSTOP); #endif SetTabStop(true); LgiResources::StyleElement(this); } GTree::~GTree() { Empty(); DeleteObj(d); } bool GTree::Lock(const char *file, int line, int TimeOut) { if (TimeOut > 0) return d->LockWithTimeout(TimeOut, file, line); return d->Lock(file, line); } void GTree::Unlock() { return d->Unlock(); } // Internal tree methods List *GTree::GetSelLst() { return &d->Selection; } void GTree::_Update(GRect *r, bool Now) { TREELOCK if (r) { GRect u = *r; GdcPt2 s = _ScrollPos(); GRect c = GetClient(); u.Offset(c.x1-s.x, c.y1-s.y); Invalidate(&u, Now && !d->InPour); } else { Invalidate((GRect*)0, Now && !d->InPour); } } void GTree::_UpdateBelow(int y, bool Now) { TREELOCK GdcPt2 s = _ScrollPos(); GRect c = GetClient(); GRect u(c.x1, y - s.y + c.y1, X()-1, Y()-1); Invalidate(&u, Now); } void GTree::ClearDs(int Col) { TREELOCK List::I it = Items.begin(); for (GTreeItem *i=*it; i; i=*++it) i->_ClearDs(Col); } GdcPt2 GTree::_ScrollPos() { TREELOCK GdcPt2 Status; Status.x = (HScroll) ? (int)HScroll->Value() : 0; Status.y = (VScroll) ? (int)VScroll->Value() * TREE_BLOCK : 0; return Status; } void GTree::_UpdateScrollBars() { static bool Processing = false; if (!Processing) { Processing = true; { TREELOCK GdcPt2 Old = _ScrollPos(); GRect Client = GetClient(); bool x = d->Limit.x > Client.X(); bool y = d->Limit.y > Client.Y(); SetScrollBars(x, y); Client = GetClient(); // x scroll... in pixels if (HScroll) { HScroll->SetLimits(0, d->Limit.x-1); HScroll->SetPage(Client.X()); int Max = d->Limit.x - Client.X(); if (HScroll->Value() > Max) { HScroll->Value(Max+1); } } // y scroll... in items if (VScroll) { int All = d->Limit.y / TREE_BLOCK; int Visible = Client.Y() / TREE_BLOCK; VScroll->SetLimits(0, All - 1); VScroll->SetPage(Visible); /* Why is this commented out? -fret Dec2018 int Max = All - Visible + 1; if (VScroll->Value() > Max) { VScroll->Value(Max); } */ } GdcPt2 New = _ScrollPos(); if (Old.x != New.x || Old.y != New.y) { Invalidate(); } } Processing = false; } } void GTree::_OnSelect(GTreeItem *Item) { TREELOCK if ( !MultiSelect() || !d->CurrentClick || ( d->CurrentClick && !d->CurrentClick->Ctrl() ) ) { for (GTreeItem *i=d->Selection.First(); i; i=d->Selection.Next()) { if (i != Item) i->Select(false); } d->Selection.Empty(); } else { d->Selection.Delete(Item); } d->Selection.Insert(Item); } void GTree::_Pour() { TREELOCK d->InPour = true; d->Limit.x = rItems.x1; d->Limit.y = rItems.y1; int ColumnPx = 0; if (Columns.Length()) { for (int i=0; iWidth(); } } else { ColumnPx = d->LastLayoutPx = GetClient().X(); if (ColumnPx < 16) ColumnPx = 16; } GTreeItem *n; List::I it = Items.begin(); for (GTreeItem *i=*it; i; i=n) { n = *++it; i->d->Last = n == 0; i->_Pour(&d->Limit, ColumnPx, 0, true); } _UpdateScrollBars(); d->LayoutDirty = false; d->InPour = false; } // External methods and events void GTree::OnItemSelect(GTreeItem *Item) { if (!Item) return; TREELOCK Item->OnSelect(); SendNotify(GNotifyItem_Select); } void GTree::OnItemExpand(GTreeItem *Item, bool Expand) { TREELOCK if (Item) Item->OnExpand(Expand); } GTreeItem *GTree::GetAdjacent(GTreeItem *i, bool Down) { TREELOCK GTreeItem *Ret = NULL; if (i) { if (Down) { GTreeItem *n = i->GetChild(); if (!n || !n->d->Visible) { for (n = i; n; ) { GTreeItem *p = n->GetParent(); if (p) { ssize_t Index = n->IndexOf(); if (Index < (ssize_t)p->Items.Length()-1) { n = n->GetNext(); break; } else { n = p; } } else { n = n->GetNext(); break; } } } Ret = n; } else { GTreeItem *p = i->GetParent() ? i->GetParent() : 0; ssize_t Index = i->IndexOf(); if (p) { GTreeItem *n = p; if (Index > 0) { n = i->GetPrev(); while ( n->GetChild() && n->GetChild()->d->Visible) { n = n->Items.ItemAt(n->Items.Length()-1); } } Ret = n; } else if (Index > 0) { p = i->GetTree()->ItemAt(Index - 1); while (p->GetChild() && p->GetChild()->d->Visible) { if (p->Items.First()) { p = p->Items.ItemAt(p->Items.Length()-1); } else break; } Ret = p; } } } return Ret; } bool GTree::OnKey(GKey &k) { if (!Lock(_FL)) return false; bool Status = false; GTreeItem *i = d->Selection.First(); if (!i) { i = Items.First(); if (i) i->Select(); } if (k.Down()) { switch (k.vkey) { case VK_PAGEUP: case VK_PAGEDOWN: { if (i && i->d->Pos.Y() > 0) { int Page = GetClient().Y() / i->d->Pos.Y(); for (int j=0; jSelect(true); i->ScrollTo(); } } Status = true; break; } case VK_HOME: { GTreeItem *i; if ((i = Items.First())) { i->Select(true); i->ScrollTo(); } Status = true; break; } case VK_END: { GTreeItem *n = i, *p = 0; while ((n = GetAdjacent(n, true))) { p = n; } if (p) { p->Select(true); p->ScrollTo(); } Status = true; break; } case VK_LEFT: { if (i) { if (i->Items.First() && i->Expanded()) { i->Expanded(false); break; } else { GTreeItem *p = i->GetParent(); if (p) { p->Select(true); p->Expanded(false); _Pour(); break; } } } // fall thru } case VK_UP: { GTreeItem *n = GetAdjacent(i, false); if (n) { n->Select(true); n->ScrollTo(); } Status = true; break; } case VK_RIGHT: { if (i) { i->Expanded(true); if (d->LayoutDirty) { _Pour(); break; } } // fall thru } case VK_DOWN: { GTreeItem *n = GetAdjacent(i, true); if (n) { n->Select(true); n->ScrollTo(); } Status = true; break; } case VK_DELETE: { if (k.Down()) { Unlock(); // before potentially being deleted...? SendNotify(GNotify_DeleteKey); // This might delete the item... so just return here. return true; } break; } case 'F': case 'f': { if (k.Ctrl()) SendNotify(GNotifyContainer_Find); break; } #ifdef VK_APPS case VK_APPS: { GTreeItem *s = Selection(); if (s) { GRect *r = &s->d->Text; if (r) { GMouse m; m.x = r->x1 + (r->X() >> 1); m.y = r->y1 + (r->Y() >> 1); m.Target = this; m.ViewCoords = true; m.Down(true); m.Right(true); s->OnMouseClick(m); } } break; } #endif default: { // LgiTrace("Key c16=%i, down=%i\n", k.c16, k.Down()); break; } } } if (i && i != (GTreeItem*)this) { i->OnKey(k); } Unlock(); return Status; } GTreeItem *GTree::ItemAtPoint(int x, int y, bool Debug) { TREELOCK GdcPt2 s = _ScrollPos(); List::I it = Items.begin(); GTreeItem *Hit = NULL; for (GTreeItem *i = *it; i; i=*++it) { Hit = i->_HitTest(s.x + x, s.y + y, Debug); if (Hit) break; } return Hit; } bool GTree::OnMouseWheel(double Lines) { TREELOCK if (VScroll) VScroll->Value(VScroll->Value() + (int)Lines); return true; } void GTree::OnMouseClick(GMouse &m) { TREELOCK d->CurrentClick = &m; if (m.Down()) { DragMode = DRAG_NONE; if (ColumnHeaders && ColumnHeader.Overlap(m.x, m.y)) { d->DragStart.x = m.x; d->DragStart.y = m.y; // Clicked on a column heading GItemColumn *Resize; GItemColumn *Over = NULL; HitColumn(m.x, m.y, Resize, Over); if (Resize) { if (m.Double()) { Resize->Width(Resize->GetContentSize() + DEFAULT_COLUMN_SPACING); Invalidate(); } else { DragMode = RESIZE_COLUMN; d->DragData = (int)Columns.IndexOf(Resize); Capture(true); } } /* else { DragMode = CLICK_COLUMN; d->DragData = Columns.IndexOf(Over); if (Over) { Over->Value(true); GRect r = Over->GetPos(); Invalidate(&r); Capture(true); } } */ } else if (rItems.Overlap(m.x, m.y)) { Focus(true); Capture(true); d->LastClick.x = m.x; d->LastClick.y = m.y; d->LastHit = ItemAtPoint(m.x, m.y, true); if (d->LastHit) { GdcPt2 c = _ScrollPos(); m.x += c.x; m.y += c.y; d->LastHit->_MouseClick(m); } else { SendNotify(GNotifyContainer_Click); } } } else if (IsCapturing()) { Capture(false); if (rItems.Overlap(m.x, m.y)) { d->LastClick.x = m.x; d->LastClick.y = m.y; d->LastHit = ItemAtPoint(m.x, m.y); if (d->LastHit) { GdcPt2 c = _ScrollPos(); m.x += c.x; m.y += c.y; d->LastHit->_MouseClick(m); } } } d->CurrentClick = NULL; } void GTree::OnMouseMove(GMouse &m) { if (!IsCapturing()) return; TREELOCK switch (DragMode) { /* case DRAG_COLUMN: { if (DragCol) { GdcPt2 p; PointToScreen(p); GRect r = DragCol->GetPos(); r.Offset(-p.x, -p.y); // to view co-ord r.Offset(m.x - DragCol->GetOffset() - r.x1, 0); if (r.x1 < 0) r.Offset(-r.x1, 0); if (r.x2 > X()-1) r.Offset((X()-1)-r.x2, 0); r.Offset(p.x, p.y); // back to screen co-ord DragCol->SetPos(r, true); r = DragCol->GetPos(); } break; } */ case RESIZE_COLUMN: { GItemColumn *c = Columns[d->DragData]; if (c) { // int OldWidth = c->Width(); int NewWidth = m.x - c->GetPos().x1; c->Width(MAX(NewWidth, 4)); _ClearDs(d->DragData); Invalidate(); } break; } default: { if (rItems.Overlap(m.x, m.y)) { if (abs(d->LastClick.x - m.x) > DRAG_THRESHOLD || abs(d->LastClick.y - m.y) > DRAG_THRESHOLD) { OnItemBeginDrag(d->LastHit, m.Flags); Capture(false); } } break; } } } void GTree::OnPosChange() { TREELOCK if (Columns.Length() == 0 && d->LastLayoutPx != GetClient().X()) d->LayoutDirty = true; GLayout::OnPosChange(); _UpdateScrollBars(); } void GTree::OnPaint(GSurface *pDC) { TREELOCK #if 0 // coverage testing... pDC->Colour(GColour(255, 0, 255)); pDC->Rectangle(); #endif rItems = GetClient(); GFont *f = GetFont(); if (ShowColumnHeader()) { ColumnHeader.ZOff(rItems.X()-1, f->GetHeight() + 4); PaintColumnHeadings(pDC); rItems.y1 = ColumnHeader.y2 + 1; } else { ColumnHeader.ZOff(-1, -1); } d->IconTextGap = GetFont()->GetHeight() / 6; // icon cache if (GetImageList() && !d->IconCache) { int CacheHeight = MAX(SysFont->GetHeight(), GetImageList()->Y()); d->IconCache = new GMemDC; if (d->IconCache && d->IconCache->Create(GetImageList()->X(), CacheHeight, GdcD->GetColourSpace())) { if (d->IconCache->GetColourSpace() == CsIndex8) { d->IconCache->Palette(new GPalette(GdcD->GetGlobalColour()->GetPalette())); } GColour Background(LC_WORKSPACE, 24); d->IconCache->Colour(Background); d->IconCache->Rectangle(); d->IconCache->Op(GDC_ALPHA); GetImageList()->Lock(); int DrawY = (CacheHeight - GetImageList()->TileY()) >> 1; LgiAssert(DrawY >= 0); for (int i=0; iGetItems(); i++) { GetImageList()->Draw(d->IconCache, i * GetImageList()->TileX(), DrawY, i, Background); } GetImageList()->Unlock(); d->IconCache->Unlock(); } } // scroll GdcPt2 s = _ScrollPos(); int Ox, Oy; pDC->GetOrigin(Ox, Oy); pDC->SetOrigin(Ox + s.x, Oy + s.y); // selection colour GArray ColPx; GItem::ItemPaintCtx Ctx; Ctx.pDC = pDC; if (Columns.Length() > 0) { Ctx.Columns = (int)Columns.Length(); for (int i=0; iWidth(); } else { Ctx.Columns = 1; ColPx[0] = rItems.X(); } Ctx.ColPx = &ColPx[0]; COLOUR SelFore = Focus() ? LC_FOCUS_SEL_FORE : LC_NON_FOCUS_SEL_FORE; COLOUR SelBack = Focus() ? LC_FOCUS_SEL_BACK : LC_NON_FOCUS_SEL_BACK; // layout items if (d->LayoutDirty) { _Pour(); } // paint items ZeroObj(d->LineFlags); List::I it = Items.begin(); for (GTreeItem *i = *it; i; i=*++it) { bool IsSelected = (d->DropTarget == i) || (d->DropTarget == 0 && i->Select()); // Foreground GCss::ColorDef Fill = i->GetCss(true)->Color(); Ctx.Fore.Set(Fill.Type == GCss::ColorRgb ? Rgb32To24(Fill.Rgb32) : (IsSelected ? SelFore : LC_TEXT), 24); // Background Fill = i->GetCss()->BackgroundColor(); Ctx.Back.Set(Fill.Type == GCss::ColorRgb ? Rgb32To24(Fill.Rgb32) : (IsSelected ? SelBack : LC_WORKSPACE), 24); i->OnPaint(Ctx); } pDC->SetOrigin(Ox, Oy); if (d->Limit.y-s.y < rItems.Y()) { // paint after items pDC->Colour(LC_WORKSPACE, 24); pDC->Rectangle(rItems.x1, d->Limit.y - s.y, rItems.x2, rItems.y2); } } int GTree::OnNotify(GViewI *Ctrl, int Flags) { switch (Ctrl->GetId()) { case IDC_HSCROLL: case IDC_VSCROLL: { TREELOCK if (Flags == GNotifyScrollBar_Create) _UpdateScrollBars(); Invalidate(); break; } } return GLayout::OnNotify(Ctrl, Flags); } GMessage::Result GTree::OnEvent(GMessage *Msg) { return GLayout::OnEvent(Msg); } GTreeItem *GTree::Insert(GTreeItem *Obj, int Pos) { TREELOCK GTreeItem *NewObj = GTreeNode::Insert(Obj, Pos); if (NewObj) NewObj->_SetTreePtr(this); return NewObj; } bool GTree::Remove(GTreeItem *Obj) { TREELOCK bool Status = false; if (Obj && Obj->Tree == this) { Obj->Remove(); Status = true; } return Status; } void GTree::RemoveAll() { TREELOCK List::I it = Items.begin(); for (GTreeItem *i=*it; i; i=*++it) i->_Remove(); Invalidate(); } void GTree::Empty() { TREELOCK GTreeItem *i; while ((i = Items.First())) Delete(i); } bool GTree::Delete(GTreeItem *Obj) { bool Status = false; TREELOCK if (Obj) { GTreeItem *i; while ((i = Obj->Items.First())) { Delete(i); } Obj->Remove(); DeleteObj(Obj); Status = true; } return Status; } void GTree::OnPulse() { TREELOCK if (d->DropTarget) { int64 p = LgiCurrentTime() - d->DropSelectTime; if (p >= 1000) { SetPulse(); if (!d->DropTarget->Expanded() && d->DropTarget->GetChild()) { d->DropTarget->Expanded(true); } } } if (InsideDragOp()) { GMouse m; if (GetMouse(m)) { GRect c = GetClient(); if (VScroll) { if (m.y < DRAG_SCROLL_EDGE) { // Scroll up... VScroll->Value(VScroll->Value() - DRAG_SCROLL_Y); } else if (m.y > c.Y() - DRAG_SCROLL_EDGE) { // Scroll down... VScroll->Value(VScroll->Value() + DRAG_SCROLL_Y); } } if (HScroll) { if (m.x < DRAG_SCROLL_EDGE) { // Scroll left... HScroll->Value(HScroll->Value() - DRAG_SCROLL_X); } else if (m.x > c.X() - DRAG_SCROLL_EDGE) { // Scroll right... HScroll->Value(HScroll->Value() + DRAG_SCROLL_X); } } } } } int GTree::GetContentSize(int ColumnIdx) { TREELOCK int MaxPx = 0; List::I it = Items.begin(); for (GTreeItem *i = *it; i; i=*++it) { int ItemPx = i->GetColumnSize(ColumnIdx); MaxPx = MAX(ItemPx, MaxPx); } return MaxPx; } LgiCursor GTree::GetCursor(int x, int y) { TREELOCK GItemColumn *Resize = NULL, *Over = NULL; HitColumn(x, y, Resize, Over); return (Resize) ? LCUR_SizeHor : LCUR_Normal; } void GTree::OnDragEnter() { TREELOCK InsideDragOp(true); SetPulse(120); } void GTree::OnDragExit() { TREELOCK InsideDragOp(false); SetPulse(); SelectDropTarget(0); } void GTree::SelectDropTarget(GTreeItem *Item) { TREELOCK if (Item != d->DropTarget) { bool Update = (d->DropTarget != 0) ^ (Item != 0); GTreeItem *Old = d->DropTarget; d->DropTarget = Item; if (Old) { Old->Update(); } if (d->DropTarget) { d->DropTarget->Update(); d->DropSelectTime = LgiCurrentTime(); } if (Update) { OnFocus(true); } } } bool GTree::Select(GTreeItem *Obj) { TREELOCK bool Status = false; if (Obj && IsAttached()) { Obj->Select(true); Status = true; } else if (d->Selection.Length()) { d->Selection.Empty(); OnItemSelect(0); Status = true; } return Status; } GTreeItem *GTree::Selection() { TREELOCK return d->Selection.First(); } bool GTree::ForAllItems(std::function Callback) { TREELOCK return ForEach(Callback) > 0; } void GTree::OnItemClick(GTreeItem *Item, GMouse &m) { if (!Item) return; TREELOCK Item->OnMouseClick(m); if (!m.Ctrl() && !m.Shift()) SendNotify(GNotifyItem_Click); } void GTree::OnItemBeginDrag(GTreeItem *Item, int Flags) { if (!Item) return; TREELOCK GMouse m; m.x = m.y = 0; m.Target = NULL; m.ViewCoords = false; m.Flags = Flags; Item->OnBeginDrag(m); } void GTree::OnFocus(bool b) { TREELOCK // errors during deletion of the control can cause // this to be called after the destructor if (d) { List::I it = d->Selection.begin(); for (GTreeItem *i=*it; i; i=*++it) i->Update(); } } static void GTreeItemUpdateAll(GTreeNode *n) { for (GTreeItem *i=n->GetChild(); i; i=i->GetNext()) { i->Update(); GTreeItemUpdateAll(i); } } void GTree::UpdateAllItems() { TREELOCK d->LayoutDirty = true; GTreeItemUpdateAll(this); } void GTree::SetVisualStyle(ThumbStyle Btns, bool JoiningLines) { TREELOCK d->Btns = Btns; d->JoiningLines = JoiningLines; Invalidate(); } diff --git a/src/win32/Gdc2/GMemDC.cpp b/src/win32/Gdc2/GMemDC.cpp --- a/src/win32/Gdc2/GMemDC.cpp +++ b/src/win32/Gdc2/GMemDC.cpp @@ -1,790 +1,790 @@ /*hdr ** FILE: GMemDC.h ** AUTHOR: Matthew Allen ** DATE: 27/11/2001 ** DESCRIPTION: GDC v2.xx header ** ** Copyright (C) 2001, Matthew Allen ** fret@memecode.com */ #include #include #include "Lgi.h" #include "GdiLeak.h" #include "GPalette.h" ///////////////////////////////////////////////////////////////////////////////////////////////////// class GMemDCPrivate { public: void *pBits; PBITMAPINFO Info; HPALETTE OldPal; bool UpsideDown; GRect Client; int ConstAlpha; GMemDCPrivate() { pBits = 0; Info = 0; OldPal = 0; Client.ZOff(-1, -1); UpsideDown = false; ConstAlpha = 255; } ~GMemDCPrivate() { DeleteArray(((char*&)Info)); } }; GMemDC::GMemDC(int x, int y, GColourSpace cs, int Flags) { d = new GMemDCPrivate; ColourSpace = CsNone; hBmp = 0; hDC = 0; pMem = 0; if (x > 0 && y > 0) { Create(x, y, cs, Flags); } } GMemDC::GMemDC(GSurface *pDC) { d = new GMemDCPrivate; ColourSpace = CsNone; hBmp = 0; hDC = 0; pMem = 0; if (pDC && Create(pDC->X(), pDC->Y(), pDC->GetColourSpace()) ) { if (pDC->Palette()) { Palette(new GPalette(pDC->Palette())); } Blt(0, 0, pDC); if (pDC->AlphaDC() && HasAlpha(true)) { pAlphaDC->Blt(0, 0, pDC->AlphaDC()); } } } GMemDC::~GMemDC() { Empty(); DeleteObj(d); } void GMemDC::SetClient(GRect *c) { if (c) { if (hDC) { SetWindowOrgEx(hDC, 0, 0, NULL); SelectClipRgn(hDC, 0); HRGN hRgn = CreateRectRgn(c->x1, c->y1, c->x2+1, c->y2+1); if (hRgn) { SelectClipRgn(hDC, hRgn); DeleteObject(hRgn); } SetWindowOrgEx(hDC, -c->x1, -c->y1, NULL); } GRect Doc(0, 0, pMem->x-1, pMem->y-1); Clip = d->Client = *c; Clip.Bound(&Doc); OriginX = -c->x1; OriginY = -c->y1; } else { d->Client.ZOff(-1, -1); if (hDC) { SetWindowOrgEx(hDC, 0, 0, NULL); SelectClipRgn(hDC, 0); } OriginX = 0; OriginY = 0; Clip.ZOff(pMem->x-1, pMem->y-1); } } void GMemDC::UpsideDown(bool upsidedown) { d->UpsideDown = upsidedown; } bool GMemDC::Lock() { return true; } bool GMemDC::Unlock() { return true; } PBITMAPINFO GMemDC::GetInfo() { return d->Info; } void GMemDC::Update(int Flags) { if (d->Info && pPalette && (Flags & GDC_PAL_CHANGE)) { GdcRGB *p = (*pPalette)[0]; if (p) { for (int i=0; iGetSize(); i++, p++) { d->Info->bmiColors[i].rgbRed = p->r; d->Info->bmiColors[i].rgbGreen = p->g; d->Info->bmiColors[i].rgbBlue = p->b; } if (hDC) { SetDIBColorTable(hDC, 0, 256, d->Info->bmiColors); } } } } HDC GMemDC::StartDC() { if (!hBmp) return NULL; hDC = CreateCompatibleDC(NULL); if (hDC) { hBmp = (HBITMAP) SelectObject(hDC, hBmp); SetWindowOrgEx(hDC, OriginX, OriginY, NULL); } return hDC; } void GMemDC::EndDC() { if (hDC) { hBmp = (HBITMAP) SelectObject(hDC, hBmp); DeleteDC(hDC); hDC = 0; } } GRect GMemDC::ClipRgn(GRect *Rgn) { GRect Prev = Clip; if (Rgn) { POINT Origin = {0, 0}; if (hDC) GetWindowOrgEx(hDC, &Origin); else { Origin.x = OriginX; Origin.y = OriginY; } Clip.x1 = max(Rgn->x1 - Origin.x, 0); Clip.y1 = max(Rgn->y1 - Origin.y, 0); Clip.x2 = min(Rgn->x2 - Origin.x, pMem->x-1); Clip.y2 = min(Rgn->y2 - Origin.y, pMem->y-1); HRGN hRgn = CreateRectRgn(Clip.x1, Clip.y1, Clip.x2+1, Clip.y2+1); if (hRgn) { SelectClipRgn(hDC, hRgn); DeleteObject(hRgn); } } else { Clip.x1 = 0; Clip.y1 = 0; Clip.x2 = X()-1; Clip.y2 = Y()-1; SelectClipRgn(hDC, NULL); } return Prev; } bool GMemDC::SupportsAlphaCompositing() { return true; } enum BmpComp { BmpRed, BmpGrn, BmpBlu, BmpAlp }; bool GMemDC::Create(int x, int y, GColourSpace Cs, int Flags) { bool Status = FALSE; HBITMAP hOldBmp = hBmp; BITMAPINFO *OldInfo = d->Info; GBmpMem *pOldMem = pMem; DrawOnAlpha(FALSE); DeleteObj(pAlphaDC); hBmp = NULL; pMem = NULL; d->Info = NULL; int Bits = GColourSpaceToBits(Cs); int LineLen = (((x * Bits) + 31) / 32) * 4; if (x > 0 && y > 0) { int Colours = Bits <= 8 ? 1 << Bits : 0; - int SizeOf = sizeof(BITMAPINFO)+(3*sizeof(uint32))+(sizeof(RGBQUAD)*Colours); + int SizeOf = sizeof(BITMAPINFO)+(3*sizeof(uint32_t))+(sizeof(RGBQUAD)*Colours); d->Info = (PBITMAPINFO) new char[SizeOf]; if (d->Info) { d->Info->bmiHeader.biSize = sizeof(d->Info->bmiHeader); d->Info->bmiHeader.biWidth = x; d->Info->bmiHeader.biHeight = d->UpsideDown ? y : -y; d->Info->bmiHeader.biPlanes = 1; d->Info->bmiHeader.biSizeImage = LineLen * y; d->Info->bmiHeader.biXPelsPerMeter = 3000; d->Info->bmiHeader.biYPelsPerMeter = 3000; d->Info->bmiHeader.biClrUsed = 0; d->Info->bmiHeader.biClrImportant = 0; - uint32 *BitFeilds = (uint32*) d->Info->bmiColors; + uint32_t *BitFeilds = (uint32_t*) d->Info->bmiColors; switch (Cs) { case CsIndex1: case CsIndex4: case CsIndex8: { ColourSpace = Cs; d->Info->bmiHeader.biBitCount = Bits; d->Info->bmiHeader.biCompression = BI_RGB; break; } case System15BitColourSpace: { ColourSpace = Cs; #if 1 union { - uint32 u16; + uint32_t u16; System15BitPixel p; }; u16 = 0; p.r = -1; BitFeilds[BmpRed] = u16; u16 = 0; p.g = -1; BitFeilds[BmpGrn] = u16; u16 = 0; p.b = -1; BitFeilds[BmpBlu] = u16; #else BitFeilds[BmpRed] = 0x001F; BitFeilds[BmpGrn] = 0x03E0; BitFeilds[BmpBlu] = 0x7C00; #endif BitFeilds[BmpAlp] = 0; d->Info->bmiHeader.biBitCount = 16; d->Info->bmiHeader.biCompression = BI_BITFIELDS; d->Info->bmiHeader.biSize += sizeof(*BitFeilds) * 4; break; } case System16BitColourSpace: { ColourSpace = Cs; #if 1 union { - uint32 u16; + uint32_t u16; System16BitPixel p; }; u16 = 0; p.r = -1; BitFeilds[BmpRed] = u16; u16 = 0; p.g = -1; BitFeilds[BmpGrn] = u16; u16 = 0; p.b = -1; BitFeilds[BmpBlu] = u16; #else BitFeilds[BmpRed] = 0x001F; BitFeilds[BmpGrn] = 0x07E0; BitFeilds[BmpBlu] = 0xF800; #endif BitFeilds[BmpAlp] = 0; d->Info->bmiHeader.biBitCount = 16; d->Info->bmiHeader.biCompression = BI_BITFIELDS; d->Info->bmiHeader.biSize += sizeof(*BitFeilds) * 4; break; } case System24BitColourSpace: { ColourSpace = Cs; d->Info->bmiHeader.biBitCount = 24; d->Info->bmiHeader.biCompression = BI_RGB; break; } case System32BitColourSpace: { ColourSpace = Cs; union { - uint32 u32; + uint32_t u32; System32BitPixel p; }; u32 = 0; p.r = -1; BitFeilds[BmpRed] = u32; u32 = 0; p.g = -1; BitFeilds[BmpGrn] = u32; u32 = 0; p.b = -1; BitFeilds[BmpBlu] = u32; u32 = 0; p.a = -1; BitFeilds[BmpAlp] = u32; d->Info->bmiHeader.biBitCount = 32; d->Info->bmiHeader.biCompression = BI_BITFIELDS; d->Info->bmiHeader.biSize += sizeof(*BitFeilds) * 4; break; } default: { // Non-native colour space support... break; } } if (ColourSpace) { // Native colour space support... HDC hDC = GetDC(NULL); if (hDC) { if (Colours > 0 && GdcD->GetBits()) { PALETTEENTRY Pal[256]; GetSystemPaletteEntries(hDC, 0, Colours, Pal); for (int i=0; iInfo->bmiColors[i].rgbReserved = 0; d->Info->bmiColors[i].rgbRed = Pal[i].peRed; d->Info->bmiColors[i].rgbGreen = Pal[i].peGreen; d->Info->bmiColors[i].rgbBlue = Pal[i].peBlue; } } hBmp = CreateDIBSection(hDC, d->Info, DIB_RGB_COLORS, &d->pBits, NULL, 0); if (hBmp) { pMem = new GBmpMem; if (pMem) { if (d->UpsideDown) { pMem->Base = ((uchar*) d->pBits) + (LineLen * (y - 1)); } else { pMem->Base = (uchar*) d->pBits; } pMem->x = x; pMem->y = y; pMem->Cs = ColourSpace; pMem->Line = d->UpsideDown ? -LineLen : LineLen; pMem->Flags = 0; Status = TRUE; } } else { DWORD err = GetLastError(); #if 1 LgiAssert(!"Create bmp failed."); #endif } } ReleaseDC(NULL, hDC); } else { // Non-native colour space support... ColourSpace = Cs; if (ColourSpace) { // Non-native image data pMem = new GBmpMem; if (pMem) { pMem->x = x; pMem->y = y; pMem->Line = ((x * Bits + 31) / 32) << 2; pMem->Cs = ColourSpace; pMem->Flags = GBmpMem::BmpOwnMemory; pMem->Base = new uchar[pMem->y * pMem->Line]; Status = pMem->Base != NULL; } } } } if (Status) { int NewOp = (pApp) ? Op() : GDC_SET; if ( (Flags & GDC_OWN_APPLICATOR) && !(Flags & GDC_CACHED_APPLICATOR)) { DeleteObj(pApp); } for (int i=0; iInfo); DeleteObj(pMem) } } if (hOldBmp) { DeleteObject(hOldBmp); hOldBmp = 0; } DeleteObj(OldInfo); DeleteObj(pOldMem); return Status; } void GMemDC::Empty() { if (hBmp) { DeleteObject(hBmp); hBmp = 0; } DeleteObj(pMem); } void GMemDC::Blt(int x, int y, GSurface *Src, GRect *a) { LgiAssert(Src != 0); if (!Src) return; if (Src->IsScreen()) { GRect b; if (a) { b = *a; } else { GArray Displays; LgiGetDisplays(Displays, &b); } int RowOp; switch (Op()) { case GDC_SET: { RowOp = SRCCOPY; break; } case GDC_AND: { RowOp = SRCAND; break; } case GDC_OR: { RowOp = SRCPAINT; break; } case GDC_XOR: { RowOp = SRCINVERT; break; } default: { return; } } HDC hSrcDC = GetWindowDC(GetDesktopWindow()); if (hSrcDC) { // Get the screen DC and blt from there to our own memory HDC hDstDC = StartDC(); BitBlt(hDstDC, x, y, b.X(), b.Y(), hSrcDC, b.x1, b.y1, RowOp); // Overlay any effects between the screen and cursor layers... OnCaptureScreen(); // Do we need to capture the cursor as well? if (TestFlag(Flags, GDC_CAPTURE_CURSOR)) { // Capture the cursor as well.. CURSORINFO ci; ZeroObj(ci); ci.cbSize = sizeof(ci); GetCursorInfo(&ci); if (ci.flags == CURSOR_SHOWING) { HICON hicon = CopyIcon(ci.hCursor); ICONINFO icInfo; ZeroObj(icInfo); if (GetIconInfo(hicon, &icInfo)) { int cx = ci.ptScreenPos.x - ((int)icInfo.xHotspot); int cy = ci.ptScreenPos.y - ((int)icInfo.yHotspot); DrawIcon(hDstDC, cx - b.x1, cy - b.y1, hicon); } DestroyIcon(hicon); } } EndDC(); ReleaseDC(0, hSrcDC); } } else { GSurface::Blt(x, y, Src, a); } } void GMemDC::StretchBlt(GRect *Dest, GSurface *Src, GRect *s) { if (Src) { GRect DestR; if (Dest) { DestR = *Dest; } else { DestR.ZOff(X()-1, Y()-1); } GRect SrcR; if (s) { SrcR = *s; } else { SrcR.ZOff(Src->X()-1, Src->Y()-1); } int RowOp = SRCCOPY; switch (Op()) { case GDC_AND: { RowOp = SRCAND; break; } case GDC_OR: { RowOp = SRCPAINT; break; } case GDC_XOR: { RowOp = SRCINVERT; break; } case GDC_ALPHA: { if (GdcD->AlphaBlend && Src->GetBits() == 32) { HDC hDestDC = StartDC(); HDC hSrcDC = Src->StartDC(); BLENDFUNCTION Blend; Blend.BlendOp = AC_SRC_OVER; Blend.BlendFlags = 0; Blend.SourceConstantAlpha = d->ConstAlpha >= 0 && d->ConstAlpha <= 255 ? d->ConstAlpha : 255; Blend.AlphaFormat = AC_SRC_ALPHA; if (!GdcD->AlphaBlend( hDestDC, DestR.x1, DestR.y1, DestR.X(), DestR.Y(), hSrcDC, SrcR.x1, SrcR.y1, SrcR.X(), SrcR.Y(), Blend)) { static bool First = true; if (First) { First = false; LgiTrace("%s:%i - AlphaBlend(%p, %s, %p, %s) failed.\n", _FL, hDestDC, DestR.GetStr(), hSrcDC, SrcR.GetStr()); } } Src->EndDC(); EndDC(); return; } break; } default: { break; } } HDC hDestDC = StartDC(); HDC hSrcDC = Src->StartDC(); ::StretchBlt(hDestDC, DestR.x1, DestR.y1, DestR.X(), DestR.Y(), hSrcDC, SrcR.x1, SrcR.y1, SrcR.X(), SrcR.Y(), RowOp); Src->EndDC(); EndDC(); } } void GMemDC::HorzLine(int x1, int x2, int y, COLOUR a, COLOUR b) { if (x1 > x2) LgiSwap(x1, x2); if (x1 < Clip.x1) x1 = Clip.x1; if (x2 > Clip.x2) x2 = Clip.x2; if ( x1 <= x2 && y >= Clip.y1 && y <= Clip.y2) { COLOUR Prev = pApp->c; pApp->SetPtr(x1, y); for (; x1 <= x2; x1++) { if (x1 & 1) { pApp->c = a; } else { pApp->c = b; } pApp->Set(); pApp->IncX(); } pApp->c = Prev; } } void GMemDC::VertLine(int x, int y1, int y2, COLOUR a, COLOUR b) { if (y1 > y2) LgiSwap(y1, y2); if (y1 < Clip.y1) y1 = Clip.y1; if (y2 > Clip.y2) y2 = Clip.y2; if ( y1 <= y2 && x >= Clip.x1 && x <= Clip.x2) { COLOUR Prev = pApp->c; pApp->SetPtr(x, y1); for (; y1 <= y2; y1++) { if (y1 & 1) { pApp->c = a; } else { pApp->c = b; } pApp->Set(); pApp->IncY(); } pApp->c = Prev; } } void GMemDC::SetOrigin(int x, int y) { GSurface::SetOrigin(x, y); if (hDC) { SetWindowOrgEx(hDC, OriginX, OriginY, NULL); } } \ No newline at end of file diff --git a/src/win32/Gdc2/GScreenDC.cpp b/src/win32/Gdc2/GScreenDC.cpp --- a/src/win32/Gdc2/GScreenDC.cpp +++ b/src/win32/Gdc2/GScreenDC.cpp @@ -1,904 +1,904 @@ /*hdr ** FILE: Gdc2.h ** AUTHOR: Matthew Allen ** DATE: 20/2/97 ** DESCRIPTION: GDC v2.xx header ** ** Copyright (C) 1997, Matthew Allen ** fret@memecode.com */ #include #include #include "Gdc2.h" #include "GdiLeak.h" #include "GPalette.h" class GScreenPrivate { public: #if defined WIN32 bool Release; bool End; HWND hWnd; PAINTSTRUCT Ps; HPEN hPen; HBRUSH hBrush; HBITMAP hOldBitmap; GRect Client; int Mode; COLOUR Col; int Sx, Sy; GGlobalColour *Gc; NativeInt ConstAlpha; class NullObjects { friend class GScreenDC; HPEN Pen; HBRUSH Brush; public: NullObjects() { LOGBRUSH LogBrush; LogBrush.lbStyle = BS_NULL; LogBrush.lbColor = 0; LogBrush.lbHatch = 0; Brush = CreateBrushIndirect(&LogBrush); Pen = CreatePen(PS_NULL, 1, 0); } ~NullObjects() { DeleteObject(Pen); DeleteObject(Brush); } }; static GPalette *LastRealized; static NullObjects Null; GScreenPrivate() { Gc = 0; hWnd = 0; hPen = 0; hBrush = 0; hOldBitmap = 0; Mode = GDC_SET; Col = 0; Sx = Sy = 0; Release = End = false; Client.ZOff(-1, -1); ConstAlpha = -1; } #else OsView View; int BitDepth; bool _ClientClip; GRect _Client; void _SetClient(GRect *c); #ifdef LINUX friend class GFont; friend class GView; COLOUR Col; QPainter p; #endif #endif }; GScreenPrivate::NullObjects GScreenPrivate::Null; GPalette *GScreenPrivate::LastRealized = 0; ///////////////////////////////////////////////////////////////////////////////////////////////////// GScreenDC::GScreenDC() { d = new GScreenPrivate; ColourSpace = CsNone; } GScreenDC::GScreenDC(GViewI *view) { d = new GScreenPrivate; ColourSpace = GdcD->GetColourSpace(); d->hWnd = view->Handle(); d->End = true; CreateFromHandle(BeginPaint(d->hWnd, &d->Ps)); RECT rc; GetClientRect(d->hWnd, &rc); SetSize(rc.right-rc.left, rc.bottom-rc.top); } GScreenDC::GScreenDC(HWND hWindow) { d = new GScreenPrivate; ColourSpace = GdcD->GetColourSpace(); d->hWnd = hWindow; d->End = true; CreateFromHandle(BeginPaint(d->hWnd, &d->Ps)); RECT rc; GetClientRect(d->hWnd, &rc); SetSize(rc.right-rc.left, rc.bottom-rc.top); } GScreenDC::GScreenDC(HDC hdc, HWND hwnd, bool Release) { d = new GScreenPrivate; ColourSpace = GdcD->GetColourSpace(); LgiAssert(hdc != 0); d->hWnd = hwnd; CreateFromHandle(hdc); d->Release = Release; RECT rc; if (GetWindowRect(d->hWnd, &rc)) { SetSize(rc.right-rc.left, rc.bottom-rc.top); } } GScreenDC::GScreenDC(HBITMAP hbmp, int Sx, int Sy) { d = new GScreenPrivate; ColourSpace = GdcD->GetColourSpace(); CreateFromHandle(CreateCompatibleDC(0)); hBmp = hbmp; d->hOldBitmap = (HBITMAP) SelectObject(hDC, hBmp); SetSize(Sx, Sy); } GScreenDC::~GScreenDC() { if (hDC) { if (d->hPen) { d->hPen = (HPEN) SelectObject(hDC, d->hPen); if (d->hPen) { DeleteObject(d->hPen); } } if (d->hBrush) { d->hBrush = (HBRUSH) SelectObject(hDC, d->hBrush); if (d->hBrush) { DeleteObject(d->hBrush); } } if (d->hOldBitmap) { SelectObject(hDC, d->hOldBitmap); DeleteDC(hDC); hDC = 0; } } if (d->End) { EndPaint(d->hWnd, &d->Ps); } else if (d->Release) { ReleaseDC(d->hWnd, hDC); } DeleteObj(d); } bool GScreenDC::GetClient(GRect *c) { if (!c) return false; *c = d->Client; return true; } void GScreenDC::SetClient(GRect *c) { if (c) { SetWindowOrgEx(hDC, 0, 0, NULL); SelectClipRgn(hDC, 0); HRGN hRgn = CreateRectRgn(c->x1, c->y1, c->x2+1, c->y2+1); if (hRgn) { SelectClipRgn(hDC, hRgn); DeleteObject(hRgn); } SetWindowOrgEx(hDC, -c->x1, -c->y1, NULL); d->Client = *c; } else { d->Client.ZOff(-1, -1); SetWindowOrgEx(hDC, 0, 0, NULL); SelectClipRgn(hDC, 0); } } void GScreenDC::SetSize(int x, int y) { d->Sx = x; d->Sy = y; Clip.ZOff(d->Sx-1, d->Sy-1); } bool GScreenDC::CreateFromHandle(HDC hdc) { bool Status = FALSE; hDC = hdc; if (hdc) { LOGBRUSH LogBrush; LogBrush.lbStyle = BS_SOLID; LogBrush.lbColor = 0; LogBrush.lbHatch = 0; d->hBrush = (HBRUSH) SelectObject(hDC, CreateBrushIndirect(&LogBrush)); d->hPen = (HPEN) SelectObject(hDC, CreatePen(PS_SOLID, 1, 0)); Status = TRUE; } if (GetBits() == 8 && d->hWnd) { d->Gc = GdcD->GetGlobalColour(); if (d->Gc) { GPalette *Pal = d->Gc->GetPalette(); if (Pal) { HPALETTE hpal = SelectPalette(hdc, Pal->Handle(), false); RealizePalette(hdc); } } } return Status; } void GScreenDC::GetOrigin(int &x, int &y) { POINT pt; if (GetWindowOrgEx(hDC, &pt)) { x = pt.x; y = pt.y; } else { x = y = 0; } } void GScreenDC::SetOrigin(int x, int y) { GSurface::SetOrigin(x, y); if (hDC) { SetWindowOrgEx(hDC, x, y, NULL); } } GPalette *GScreenDC::Palette() { return GSurface::Palette(); } void GScreenDC::Palette(GPalette *pPal, bool bOwnIt) { GSurface::Palette(pPal, bOwnIt); } GRect GScreenDC::ClipRgn(GRect *Rgn) { GRect Prev = Clip; if (Rgn) { POINT Origin; GetWindowOrgEx(hDC, &Origin); Clip.x1 = max(Rgn->x1 - Origin.x, 0); Clip.y1 = max(Rgn->y1 - Origin.y, 0); Clip.x2 = min(Rgn->x2 - Origin.x, d->Sx-1); Clip.y2 = min(Rgn->y2 - Origin.y, d->Sy-1); LgiAssert(d->Sx > 0 && d->Sy > 0); HRGN hRgn = CreateRectRgn(Clip.x1, Clip.y1, Clip.x2+1, Clip.y2+1); if (hRgn) { SelectClipRgn(hDC, hRgn); DeleteObject(hRgn); } } else { Clip.x1 = 0; Clip.y1 = 0; Clip.x2 = X()-1; Clip.y2 = Y()-1; SelectClipRgn(hDC, NULL); } return Prev; } GRect GScreenDC::ClipRgn() { return Clip; } COLOUR GScreenDC::Colour(COLOUR c, int Bits) { COLOUR Prev = d->Col; if (hDC) { d->hPen = (HPEN) SelectObject(hDC, d->hPen); d->hBrush = (HBRUSH) SelectObject(hDC, d->hBrush); DeleteObject(d->hPen); DeleteObject(d->hBrush); if (Bits) { d->Col = CBit(24, c, Bits, pPalette); } else { d->Col = CBit(24, c, GetBits(), pPalette); } if (d->Gc) { // Give the global colour manager a chance to map the // colour to a palette index (8 bit only) d->Col = d->Gc->GetColour(d->Col); } - uint32 WinCol = RGB( R24(d->Col), G24(d->Col), B24(d->Col) ); + uint32_t WinCol = RGB( R24(d->Col), G24(d->Col), B24(d->Col) ); LOGBRUSH LogBrush; LogBrush.lbStyle = BS_SOLID; LogBrush.lbColor = WinCol; LogBrush.lbHatch = 0; d->hBrush = (HBRUSH) SelectObject(hDC, CreateBrushIndirect(&LogBrush)); if (LineBits == 0xffffffff) { d->hPen = (HPEN) SelectObject(hDC, CreatePen(PS_SOLID, 1, WinCol)); } else { int Type = PS_SOLID; switch (LineBits) { case LineNone: Type = PS_NULL; break; case LineSolid: Type = PS_SOLID; break; case LineAlternate: Type = PS_ALTERNATE; break; case LineDash: Type = PS_DASH; break; case LineDot: Type = PS_DOT; break; case LineDashDot: Type = PS_DASHDOT; break; case LineDashDotDot: Type = PS_DASHDOTDOT; break; } LOGBRUSH br = { BS_SOLID, WinCol, HS_VERTICAL }; d->hPen = (HPEN) SelectObject(hDC, ExtCreatePen(PS_COSMETIC | Type, 1, &br, 0, NULL)); } } return Prev; } GColour GScreenDC::Colour(GColour c) { GColour cPrev(d->Col, GetBits()); Colour(c.c32(), 32); return cPrev; } int GScreenDC::Op(int Op, NativeInt Param) { int Prev = d->Mode; int Rop; switch (Op) { case GDC_ALPHA: case GDC_SET: { Rop = R2_COPYPEN; break; } case GDC_AND: { Rop = R2_MASKPEN; break; } case GDC_OR: { Rop = R2_MERGEPEN; break; } case GDC_XOR: { Rop = R2_XORPEN; break; } default: { return Prev; } } SetROP2(hDC, Rop); d->Mode = Op; d->ConstAlpha = Param; return Prev; } COLOUR GScreenDC::Colour() { return d->Col; } int GScreenDC::Op() { return d->Mode; } int GScreenDC::X() { if (d->Client.Valid()) return d->Client.X(); return d->Sx; } int GScreenDC::Y() { if (d->Client.Valid()) return d->Client.Y(); return d->Sy; } int GScreenDC::GetBits() { return GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES); } bool GScreenDC::SupportsAlphaCompositing() { // Windows does support blending screen content with bitmaps that have alpha return true; } void GScreenDC::Set(int x, int y) { - uint32 WinCol = RGB( R24(d->Col), G24(d->Col), B24(d->Col) ); + uint32_t WinCol = RGB( R24(d->Col), G24(d->Col), B24(d->Col) ); SetPixel(hDC, x, y, WinCol); } COLOUR GScreenDC::Get(int x, int y) { return GetPixel(hDC, x, y); } -uint GScreenDC::LineStyle(uint32 Bits, uint32 Reset) +uint GScreenDC::LineStyle(uint32_t Bits, uint32_t Reset) { uint Old = LineBits; LineBits = Bits; return Old; } uint GScreenDC::LineStyle() { return LineBits; } void GScreenDC::HLine(int x1, int x2, int y) { if (x1 > x2) { int t = x1; x1 = x2; x2 = t; } MoveToEx(hDC, x1, y, NULL); LineTo(hDC, x2 + 1, y); } void GScreenDC::VLine(int x, int y1, int y2) { if (y1 > y2) { int t = y1; y1 = y2; y2 = t; } MoveToEx(hDC, x, y1, NULL); LineTo(hDC, x, y2 + 1); } void GScreenDC::Line(int x1, int y1, int x2, int y2) { MoveToEx(hDC, x1, y1, NULL); LineTo(hDC, x2, y2); - uint32 WinCol = RGB( R24(d->Col), G24(d->Col), B24(d->Col) ); + uint32_t WinCol = RGB( R24(d->Col), G24(d->Col), B24(d->Col) ); SetPixel(hDC, x2, y2, WinCol); } void GScreenDC::Circle(double cx, double cy, double radius) { HBRUSH hTemp = (HBRUSH) SelectObject(hDC, d->Null.Brush); ::Ellipse( hDC, (int)floor(cx - radius), (int)floor(cy - radius), (int)ceil(cx + radius), (int)ceil(cy + radius)); SelectObject(hDC, hTemp); } void GScreenDC::FilledCircle(double cx, double cy, double radius) { ::Ellipse( hDC, (int)floor(cx - radius), (int)floor(cy - radius), (int)ceil(cx + radius), (int)ceil(cy + radius)); } void GScreenDC::Arc(double cx, double cy, double radius, double start, double end) { int StartX = (int)(cx + (cos(start) * radius)); int StartY = (int)(cy + (int)(sin(start) * radius)); int EndX = (int)(cx + (int)(cos(end) * radius)); int EndY = (int)(cy + (int)(sin(end) * radius)); ::Arc( hDC, (int)floor(cx - radius), (int)floor(cy - radius), (int)ceil(cx + radius), (int)ceil(cy + radius), StartX, StartY, EndX, EndY); } void GScreenDC::FilledArc(double cx, double cy, double radius, double start, double end) { int StartX = (int)(cx + (cos(start) * radius)); int StartY = (int)(cy + (int)(sin(start) * radius)); int EndX = (int)(cx + (int)(cos(end) * radius)); int EndY = (int)(cy + (int)(sin(end) * radius)); ::Pie( hDC, (int)floor(cx - radius), (int)floor(cy - radius), (int)ceil(cx + radius), (int)ceil(cy + radius), StartX, StartY, EndX, EndY); } void GScreenDC::Ellipse(double cx, double cy, double x, double y) { HBRUSH hTemp = (HBRUSH) SelectObject(hDC, d->Null.Brush); ::Ellipse( hDC, (int)floor(cx - x), (int)floor(cy - y), (int)ceil(cx + x), (int)ceil(cy + y) ); SelectObject(hDC, hTemp); } void GScreenDC::FilledEllipse(double cx, double cy, double x, double y) { ::Ellipse( hDC, (int)floor(cx - x), (int)floor(cy - y), (int)ceil(cx + x), (int)ceil(cy + y) ); } void GScreenDC::Box(int x1, int y1, int x2, int y2) { HBRUSH hTemp = (HBRUSH) SelectObject(hDC, d->Null.Brush); ::Rectangle(hDC, x1, y1, x2+1, y2+1); SelectObject(hDC, hTemp); } void GScreenDC::Box(GRect *a) { HBRUSH hTemp = (HBRUSH) SelectObject(hDC, d->Null.Brush); if (a) { ::Rectangle(hDC, a->x1, a->y1, a->x2+1, a->y2+1); } else { ::Rectangle(hDC, 0, 0, X(), Y()); } SelectObject(hDC, hTemp); } void GScreenDC::Rectangle(int x1, int y1, int x2, int y2) { ::Rectangle(hDC, x1, y1, x2+1, y2+1); } void GScreenDC::Rectangle(GRect *a) { GRect b; if (a) { b = *a; } else { b.ZOff(X()-1, Y()-1); } ::Rectangle(hDC, b.x1, b.y1, b.x2+1, b.y2+1); } void GScreenDC::Blt(int x, int y, GSurface *Src, GRect *a) { if (Src) { GRect b; if (a) { b = *a; } else { b.ZOff(Src->X()-1, Src->Y()-1); } int RowOp; switch (d->Mode) { case GDC_SET: { RowOp = SRCCOPY; break; } case GDC_AND: { RowOp = SRCAND; break; } case GDC_OR: { RowOp = SRCPAINT; break; } case GDC_XOR: { RowOp = SRCINVERT; break; } case GDC_ALPHA: { if (GdcD->AlphaBlend && Src->GetBits() == 32) { HDC hDestDC = StartDC(); HDC hSrcDC = Src->StartDC(); BLENDFUNCTION Blend; Blend.BlendOp = AC_SRC_OVER; Blend.BlendFlags = 0; Blend.SourceConstantAlpha = (BYTE) (d->ConstAlpha >= 0 && d->ConstAlpha <= 255 ? d->ConstAlpha : 255); Blend.AlphaFormat = AC_SRC_ALPHA; if (!GdcD->AlphaBlend( hDestDC, x, y, b.X(), b.Y(), hSrcDC, b.x1, b.y1, b.X(), b.Y(), Blend)) { printf("%s:%i - AlphaBlend failed.\n", _FL); } Src->EndDC(); EndDC(); return; } else { // Lame '95 RowOp = SRCCOPY; } break; } default: { return; } } HDC hDestDC = StartDC(); HDC hSrcDC = Src->StartDC(); GMemDC Tmp; if (!hSrcDC) { GColourSpace Cs = GdcD->GetColourSpace(); if (!Tmp.Create(b.X(), b.Y(), Cs)) { return; } Tmp.Blt(0, 0, Src, &b); LgiAssert(Tmp.GetBitmap() != 0); Src = &Tmp; hSrcDC = Src->StartDC(); } GPalette *Pal = Src->DrawOnAlpha() ? NULL : Src->Palette(); HPALETTE sPal = 0, dPal = 0; if (Pal) { Src->Update(-1); dPal = SelectPalette(hDestDC, Pal->Handle(), false); } POINT Old; GetWindowOrgEx(hDestDC, &Old); int RealX = x - Old.x; int RealY = y - Old.y; BOOL Ret = BitBlt(hDestDC, x, y, b.X(), b.Y(), hSrcDC, b.x1, b.y1, RowOp); if (!Ret) { int asd=0; } if (Pal) { dPal = SelectPalette(hDestDC, dPal, false); } Src->EndDC(); EndDC(); } } void GScreenDC::StretchBlt(GRect *dst, GSurface *Src, GRect *s) { if (Src) { GRect DestR; if (dst) { DestR = *dst; } else { DestR.ZOff(X()-1, Y()-1); } GRect SrcR; if (s) { SrcR = *s; } else { SrcR.ZOff(Src->X()-1, Src->Y()-1); } int RowOp; switch (d->Mode) { case GDC_SET: { RowOp = SRCCOPY; break; } case GDC_AND: { RowOp = SRCAND; break; } case GDC_OR: { RowOp = SRCPAINT; break; } case GDC_XOR: { RowOp = SRCINVERT; break; } default: { return; } } HDC hDestDC = StartDC(); HDC hSrcDC = Src->StartDC(); ::StretchBlt(hDestDC, DestR.x1, DestR.y1, DestR.X(), DestR.Y(), hSrcDC, SrcR.x1, SrcR.y1, SrcR.X(), SrcR.Y(), RowOp); Src->EndDC(); EndDC(); } } void GScreenDC::Polygon(int Points, GdcPt2 *Data) { if (Points > 0 && Data) { PPOINT pt = new POINT[Points]; if (pt) { int *d = (int*)Data; for (int i=0; iCol); } diff --git a/src/win32/Gdc2/Gdc2.cpp b/src/win32/Gdc2/Gdc2.cpp --- a/src/win32/Gdc2/Gdc2.cpp +++ b/src/win32/Gdc2/Gdc2.cpp @@ -1,1236 +1,1236 @@ /* ** FILE: Gdc2.cpp ** AUTHOR: Matthew Allen ** DATE: 24/2/97 ** DESCRIPTION: GDC v2.xx ** ** Copyright (C) 1997, Matthew Allen ** fret@memecode.com */ /****************************** Includes ************************************************************************************/ #include #include #include #include #include "Lgi.h" #include "GdiLeak.h" #include "GPalette.h" /****************************** Defines *************************************************************************************/ #define LGI_RAD (360/(2*LGI_PI)) /****************************** Helper Functions ****************************************************************************/ void LgiDrawIcon(GSurface *pDC, int Dx, int Dy, HICON ico) { ICONINFO iconinfo; GetIconInfo(ico, &iconinfo); BITMAP bm, msk; GetObject(iconinfo.hbmColor, sizeof(bm), &bm); GetObject(iconinfo.hbmMask, sizeof(msk), &msk); - GArray bits, mask; + GArray bits, mask; int bmp_bpp = bm.bmPlanes * bm.bmBitsPixel; int msk_bpp = msk.bmPlanes * msk.bmBitsPixel; bits.Length(bm.bmWidthBytes * bm.bmHeight); mask.Length(msk.bmWidthBytes * msk.bmHeight); GetBitmapBits(iconinfo.hbmColor, (LONG)bits.Length(), &bits[0]); GetBitmapBits(iconinfo.hbmMask, (LONG)mask.Length(), &mask[0]); bool HasAlpha = false; int y; for (y=0; !HasAlpha && y 0) { HasAlpha = true; break; } } } for (y=0; y>3] & bit)) { if (HasAlpha) pDC->Colour(c[x], 32); else pDC->Colour(c[x] | Rgba32(0, 0, 0, 255), 32); pDC->Set(Dx + x, Dy + y); } } } } /****************************** Classes *************************************************************************************/ GPalette::GPalette() { Data = 0; hPal = NULL; Lut = 0; } GPalette::GPalette(GPalette *pPal) { Data = 0; hPal = NULL; Lut = 0; Set(pPal); } GPalette::GPalette(uchar *pPal, int s) { Data = 0; hPal = NULL; Lut = 0; if (pPal || s > 0) Set(pPal, s); } GPalette::~GPalette() { DeleteArray(Lut); DeleteArray(Data); if (hPal) DeleteObject(hPal); } void GPalette::Set(GPalette *pPal) { if (pPal == this) return; DeleteArray(Data); if (hPal) DeleteObject(hPal); if (pPal && pPal->Data) { int Len = sizeof(LOGPALETTE) + (pPal->Data->palNumEntries * sizeof(GdcRGB)); Data = (LOGPALETTE*) new char[Len]; if (Data) { memcpy(Data, pPal->Data, Len); hPal = CreatePalette(Data); } } } void GPalette::Set(uchar *pPal, int s) { DeleteArray(Data); if (hPal) DeleteObject(hPal); int Len = sizeof(LOGPALETTE) + (s * sizeof(GdcRGB)); Data = (LOGPALETTE*) new char[Len]; if (Data) { Data->palVersion = 0x300; Data->palNumEntries = s; if (pPal) { for (int i=0; ipalPalEntry[i].peRed = *pPal++; Data->palPalEntry[i].peGreen = *pPal++; Data->palPalEntry[i].peBlue = *pPal++; Data->palPalEntry[i].peFlags = 0; } } else { memset(Data->palPalEntry, 0, s * sizeof(GdcRGB)); } hPal = CreatePalette(Data); } } void GPalette::Set(int Index, int r, int g, int b) { GdcRGB *rgb = (*this)[Index]; if (rgb) { rgb->r = r; rgb->g = g; rgb->b = b; } } bool GPalette::Update() { if (Data && hPal) { return SetPaletteEntries(hPal, 0, GetSize(), Data->palPalEntry) != 0; } return FALSE; } bool GPalette::SetSize(int s) { int Len = sizeof(LOGPALETTE) + (s * sizeof(GdcRGB)); LOGPALETTE *NewData = (LOGPALETTE*) new char[Len]; if (NewData) { NewData->palVersion = 0x300; NewData->palNumEntries = s; int Common = min(s, (Data) ? Data->palNumEntries : 0); memset(NewData->palPalEntry, 0, s * sizeof(NewData->palPalEntry[0])); if (Common > 0) { memcpy(NewData->palPalEntry, Data->palPalEntry, Common * sizeof(NewData->palPalEntry[0])); } DeleteArray(Data); if (hPal) DeleteObject(hPal); Data = NewData; hPal = CreatePalette(Data); } return (Data != 0) && (hPal != 0); } void GPalette::SwapRAndB() { if (Data) { for (int i=0; ir; (*this)[i]->r = (*this)[i]->b; (*this)[i]->b = n; } } Update(); } uchar *GPalette::MakeLut(int Bits) { if (Bits) { if (!Lut) { GdcRGB *p = (*this)[0]; int Size = 1 << Bits; switch (Bits) { case 15: { Lut = new uchar[Size]; if (Lut) { for (int i=0; i> 2); int g = (G15(i) << 3) | (G15(i) >> 2); int b = (B15(i) << 3) | (B15(i) >> 2); Lut[i] = MatchRgb(Rgb24(r, g, b)); } } break; } case 16: { Lut = new uchar[Size]; if (Lut) { for (int i=0; i> 2); int g = (G16(i) << 2) | (G16(i) >> 3); int b = (B16(i) << 3) | (B16(i) >> 2); Lut[i] = MatchRgb(Rgb24(r, g, b)); } } break; } } } } else { DeleteArray(Lut); } return Lut; } int GPalette::MatchRgb(COLOUR Rgb) { if (Data) { GdcRGB *Entry = (*this)[0]; /* int r = (R24(Rgb) & 0xF8) + 4; int g = (G24(Rgb) & 0xF8) + 4; int b = (B24(Rgb) & 0xF8) + 4; */ int r = R24(Rgb); int g = G24(Rgb); int b = B24(Rgb); ulong *squares = GdcD->GetCharSquares(); ulong mindist = 200000; ulong bestcolor; ulong curdist; long rdist; long gdist; long bdist; for (int i = 0; i < Data->palNumEntries; i++) { rdist = Entry[i].r - r; gdist = Entry[i].g - g; bdist = Entry[i].b - b; curdist = squares[rdist] + squares[gdist] + squares[bdist]; if (curdist < mindist) { mindist = curdist; bestcolor = i; } } return bestcolor; } return 0; } void GPalette::CreateGreyScale() { SetSize(256); GdcRGB *p = (*this)[0]; for (int i=0; i<256; i++) { p->r = i; p->g = i; p->b = i; p->a = 0; p++; } } void GPalette::CreateCube() { SetSize(216); GdcRGB *p = (*this)[0]; for (int r=0; r<6; r++) { for (int g=0; g<6; g++) { for (int b=0; b<6; b++) { p->r = r * 51; p->g = g * 51; p->b = b * 51; p->a = 0; p++; } } } bool b = Update(); LgiAssert(b); } void TrimWhite(char *s) { char *White = " \r\n\t"; char *c = s; while (*c && strchr(White, *c)) c++; auto Len = strlen(c); if (c != s) memmove(s, c, Len+1); c = s + Len - 1; while (c > s && strchr(White, *c)) { *c = 0; c--; } } bool GPalette::Load(GFile &F) { #if 1 GString::Array Lines = F.Read().SplitDelimit("\r\n"); if (Lines.Length() < 2) { LgiAssert(0); return false; } if (!Lines[0].Equals("JASC-PAL")) { LgiAssert(0); return false; } auto Sz = Lines[1].Int(); if (Sz < 0 || Sz > 256) { LgiAssert(0); return false; } if (!SetSize((int)Sz)) { LgiAssert(0); return false; } for (int i=2; ir = (uint8) p[0].Int(); - e->g = (uint8) p[1].Int(); - e->b = (uint8) p[2].Int(); + e->r = (uint8_t) p[0].Int(); + e->g = (uint8_t) p[1].Int(); + e->b = (uint8_t) p[2].Int(); } else { LgiAssert(0); return false; } } return true; #else bool Status = FALSE; char Buf[256]; F.ReadStr(Buf, sizeof(Buf)); TrimWhite(Buf); if (strcmp(Buf, "JASC-PAL") == 0) { // is JASC palette // skip hex length F.ReadStr(Buf, sizeof(Buf)); // read decimal length F.ReadStr(Buf, sizeof(Buf)); SetSize(atoi(Buf)); for (int i=0; ir = atoi(strtok(Buf, " ")); p->g = atoi(strtok(NULL, " ")); p->b = atoi(strtok(NULL, " ")); } Status = TRUE; } } else { // check for microsoft format } return Status; #endif } bool GPalette::Save(GFile &F, int Format) { bool Status = FALSE; switch (Format) { case GDCPAL_JASC: { char Buf[256]; sprintf_s(Buf, sizeof(Buf), "JASC-PAL\r\n%04.4X\r\n%i\r\n", GetSize(), GetSize()); F.Write(Buf, strlen(Buf)); for (int i=0; ir, p->g, p->b); F.Write(Buf, strlen(Buf)); } } Status = true; break; } } return Status; } bool GPalette::operator ==(GPalette &p) { if (GetSize() == p.GetSize()) { GdcRGB *a = (*this)[0]; GdcRGB *b = p[0]; for (int i=0; ir != b->r || a->g != b->g || a->b != b->b) { return FALSE; } a++; b++; } return TRUE; } return FALSE; } bool GPalette::operator !=(GPalette &p) { return !((*this) == p); } //////////////////////////////////////////////////////////////////////////////////////////////////// GBmpMem::GBmpMem() { Base = 0; Flags = 0; Cs = CsNone; } GBmpMem::~GBmpMem() { if (Base && (Flags & GBmpMem::BmpOwnMemory)) { delete [] Base; } } /////////////////////////////////////////////////////////////////////////////////////////////// class GlobalColourEntry { public: COLOUR c24; bool Fixed; bool Used; GlobalColourEntry() { c24 = 0; Fixed = false; Used = false; } }; class GGlobalColourPrivate { public: GlobalColourEntry c[256]; GPalette *Global; List Cache; int FirstUnused; int FreeColours() { int f = 0; for (int i=0; i<256; i++) { if (!c[i].Used) { f++; } } return f; } GGlobalColourPrivate() { FirstUnused = 0; Global = 0; #ifdef WIN32 if (GdcD->GetBits() <= 8) { PALETTEENTRY e[256]; HDC hdc = CreateCompatibleDC(0); int Entries = GetSystemPaletteEntries(hdc, 0, 1 << GdcD->GetBits(), e); for (int i=0; i<10; i++) { c[i].c24 = Rgb24(e[i].peRed, e[i].peGreen, e[i].peBlue); c[i].Fixed = true; c[i].Used = true; c[255-i].c24 = Rgb24(e[255-i].peRed, e[255-i].peGreen, e[255-i].peBlue); c[255-i].Fixed = true; c[255-i].Used = true; } DeleteDC(hdc); FirstUnused = 10; } #endif } ~GGlobalColourPrivate() { Cache.DeleteObjects(); } }; GGlobalColour::GGlobalColour() { d = new GGlobalColourPrivate; } GGlobalColour::~GGlobalColour() { DeleteObj(d); } COLOUR GGlobalColour::AddColour(COLOUR c24) { for (int i=0; i<256; i++) { if (d->c[i].c24 == c24) { #ifdef WIN32 return PALETTEINDEX(i); #else return i; #endif } } if (d->FirstUnused >= 0) { d->c[d->FirstUnused].c24 = c24; d->c[d->FirstUnused].Used = true; d->c[d->FirstUnused].Fixed = true; #ifdef WIN32 return PALETTEINDEX(d->FirstUnused++); #else return d->FirstUnused++; #endif } return c24; } bool GGlobalColour::AddBitmap(GSurface *pDC) { if (pDC) { GSurface *s = new GMemDC(pDC); if (s) { d->Cache.Insert(s); return true; } } return false; } void KeyBlt(GSurface *To, GSurface *From, COLOUR Key) { int Bits = From->GetBits(); GPalette *Pal = From->Palette(); for (int y=0; yY(); y++) { for (int x=0; xX(); x++) { COLOUR c = From->Get(x, y); if (c != Key) { To->Colour(CBit(To->GetBits(), c, Bits, Pal)); To->Set(x, y); } } } } bool GGlobalColour::AddBitmap(GImageList *il) { if (il) { GSurface *s = new GMemDC(il); if (s) { // Cache the full colour bitmap d->Cache.Insert(s); // Cache the disabled alpha blending bitmap s = new GMemDC(il->X(), il->Y(), System24BitColourSpace); if (s) { s->Op(GDC_ALPHA); GApplicator *pApp = s->Applicator(); if (pApp) pApp->SetVar(GAPP_ALPHA_A, 40); s->Blt(0, 0, il); d->Cache.Insert(s); } return true; } } return false; } bool GGlobalColour::MakeGlobalPalette() { if (!d->Global) { d->Global = new GPalette(0, 256); if (d->Global) { for (GSurface *pDC = d->Cache.First(); pDC; pDC = d->Cache.Next()) { for (int y=0; yY(); y++) { for (int x=0; xX(); x++) { COLOUR c = CBit(24, pDC->Get(x, y), pDC->GetBits(), pDC->Palette()); AddColour(c); } } } for (int i=0; i<256; i++) { GdcRGB *r = (*d->Global)[i]; if (r) { /* if (i < 10 || i > 246) { r->R = i; r->G = 0; r->B = 0; r->Flags = PC_EXPLICIT; } else { */ r->r = R24(d->c[i].c24); r->g = G24(d->c[i].c24); r->b = B24(d->c[i].c24); r->a = 0; // PC_RESERVED; // } } } d->Global->Update(); d->Cache.DeleteObjects(); } } return d->Global != 0; } GPalette *GGlobalColour::GetPalette() { if (!d->Global) { MakeGlobalPalette(); } return d->Global; } COLOUR GGlobalColour::GetColour(COLOUR c24) { for (int i=0; i<256; i++) { if (d->c[i].Used && d->c[i].c24 == c24) { return PALETTEINDEX(i); } } return c24; } bool GGlobalColour::RemapBitmap(GSurface *pDC) { return false; } /////////////////////////////////////////////////////////////////////////////////////////////// class GdcDevicePrivate { public: // class GLibrary *Iconv; // Iconv startup // extern GLibrary *_LgiStartIconv(); // Iconv = _LgiStartIconv(); // DeleteObj(Iconv); GdcDevice *Device; // Current mode info int ScrX; int ScrY; int ScrBits; GColourSpace ColourSpace; // Palette double GammaCorrection; uchar GammaTable[256]; GPalette *pSysPal; GGlobalColour *GlobalColour; // Data ulong *CharSquareData; uchar *Div255; // Options int OptVal[GDC_MAX_OPTION]; // Alpha #if defined WIN32 GLibrary MsImg; #elif defined LINUX int Pixel24Size; #endif GdcDevicePrivate(GdcDevice *d) #ifdef WIN32 : MsImg("msimg32") #endif { Device = d; GlobalColour = 0; ZeroObj(OptVal); // Palette information GammaCorrection = 1.0; // Get mode stuff HWND hDesktop = GetDesktopWindow(); HDC hScreenDC = GetDC(hDesktop); ScrX = GetSystemMetrics(SM_CXSCREEN); ScrY = GetSystemMetrics(SM_CYSCREEN); ScrBits = GetDeviceCaps(hScreenDC, BITSPIXEL); switch (ScrBits) { case 8: ColourSpace = CsIndex8; break; case 15: ColourSpace = System15BitColourSpace; break; case 16: ColourSpace = System16BitColourSpace; break; case 24: ColourSpace = System24BitColourSpace; break; case 32: ColourSpace = System32BitColourSpace; break; default: LgiAssert(!"Unknown colour space."); ColourSpace = CsNone; break; } int Colours = 1 << ScrBits; pSysPal = (ScrBits <= 8) ? new GPalette(0, Colours) : 0; if (pSysPal) { GdcRGB *p = (*pSysPal)[0]; if (p) { PALETTEENTRY pal[256]; GetSystemPaletteEntries(hScreenDC, 0, Colours, pal); for (int i=0; iMsImg.IsLoaded()) { AlphaBlend = (MsImg32_AlphaBlend) d->MsImg.GetAddress("AlphaBlend"); } SetGamma(LGI_DEFAULT_GAMMA); d->GlobalColour = new GGlobalColour; } GdcDevice::~GdcDevice() { DeleteObj(d); pInstance = NULL; } int GdcDevice::GetOption(int Opt) { return (Opt >= 0 && Opt < GDC_MAX_OPTION) ? d->OptVal[Opt] : 0; } int GdcDevice::SetOption(int Opt, int Value) { int Prev = d->OptVal[Opt]; if (Opt >= 0 && Opt < GDC_MAX_OPTION) d->OptVal[Opt] = Value; return Prev; } ulong *GdcDevice::GetCharSquares() { return (d->CharSquareData) ? d->CharSquareData + 255 : 0; } uchar *GdcDevice::GetDiv255() { return d->Div255; } GGlobalColour *GdcDevice::GetGlobalColour() { return d->GlobalColour; } GColourSpace GdcDevice::GetColourSpace() { return d->ColourSpace; } int GdcDevice::GetBits() { return d->ScrBits; } int GdcDevice::X() { return d->ScrX; } int GdcDevice::Y() { return d->ScrY; } void GdcDevice::SetGamma(double Gamma) { d->GammaCorrection = Gamma; for (int i=0; i<256; i++) { d->GammaTable[i] = (uchar) (pow(((double)i)/256, Gamma) * 256); } } double GdcDevice::GetGamma() { return d->GammaCorrection; } void GdcDevice::SetSystemPalette(int Start, int Size, GPalette *pPal) { /* if (pPal) { uchar Pal[768]; uchar *Temp = Pal; uchar *System = Palette + (Start * 3); GdcRGB *P = (*pPal)[Start]; for (int i=0; iR] >> PalShift; *Temp++ = GammaTable[*System++ = P->G] >> PalShift; *Temp++ = GammaTable[*System++ = P->B] >> PalShift; } SetPaletteBlockDirect(Pal, Start, Size * 3); } */ } GPalette *GdcDevice::GetSystemPalette() { return d->pSysPal; } void GdcDevice::SetColourPaletteType(int Type) { bool SetOpt = TRUE; /* switch (Type) { case PALTYPE_ALLOC: { SetPalIndex(0, 0, 0, 0); SetPalIndex(255, 255, 255, 255); break; } case PALTYPE_RGB_CUBE: { uchar Pal[648]; uchar *p = Pal; for (int r=0; r<6; r++) { for (int g=0; g<6; g++) { for (int b=0; b<6; b++) { *p++ = r * 51; *p++ = g * 51; *p++ = b * 51; } } } SetPalBlock(0, 216, Pal); SetPalIndex(255, 0xFF, 0xFF, 0xFF); break; } case PALTYPE_HSL: { for (int h = 0; h<16; h++) { } break; } default: { SetOpt = FALSE; } } if (SetOpt) { SetOption(GDC_PALETTE_TYPE, Type); } */ } COLOUR GdcDevice::GetColour(COLOUR Rgb24, GSurface *pDC) { int Bits = (pDC) ? pDC->GetBits() : GetBits(); COLOUR C; switch (Bits) { case 8: { switch (GetOption(GDC_PALETTE_TYPE)) { case PALTYPE_ALLOC: { static uchar Current = 1; Rgb24 &= 0xFFFFFF; if (Rgb24 == 0xFFFFFF) { C = 0xFF; } else if (Rgb24 == 0) { C = 0; } else if (d->pSysPal) { GdcRGB *n = (*d->pSysPal)[Current]; n->r = R24(Rgb24); n->g = G24(Rgb24); n->b = B24(Rgb24); C = Current++; if (Current == 255) Current = 1; } else { C = 0; } break; } case PALTYPE_RGB_CUBE: { uchar r = (R24(Rgb24) + 25) / 51; uchar g = (G24(Rgb24) + 25) / 51; uchar b = (B24(Rgb24) + 25) / 51; C = (r*36) + (g*6) + b; break; } case PALTYPE_HSL: { C = 0; break; } } break; } case 16: { C = Rgb24To16(Rgb24); break; } case 24: { C = Rgb24; break; } case 32: { C = Rgb24To32(Rgb24); break; } } return C; } ////////////////////////////////////////////////////////////////////////////////////////////// static int _Factories; static GApplicatorFactory *_Factory[16]; GApp8 Factory8; GApp15 Factory15; GApp16 Factory16; GApp24 Factory24; GApp32 Factory32; GAlphaFactory FactoryAlpha; GApplicatorFactory::GApplicatorFactory() { LgiAssert(_Factories >= 0 && _Factories < CountOf(_Factory)); if (_Factories < CountOf(_Factory) - 1) { _Factory[_Factories++] = this; } } GApplicatorFactory::~GApplicatorFactory() { LgiAssert(_Factories >= 0 && _Factories < CountOf(_Factory)); for (int i=0; i<_Factories; i++) { if (_Factory[i] == this) { _Factory[i] = _Factory[_Factories-1]; _Factories--; break; } } } class NullApplicator : public GApplicator { public: const char *GetClass() { return "NullApplicator"; } bool SetSurface(GBmpMem *d, GPalette *p = 0, GBmpMem *a = 0) { return false; } void SetPtr(int x, int y) {} void IncX() {} void IncY() {} void IncPtr(int X, int Y) {} void Set() {} COLOUR Get() { return 0; } void VLine(int height) {} void Rectangle(int x, int y) {} bool Blt(GBmpMem *Src, GPalette *SPal, GBmpMem *SrcAlpha = 0) { return false; } }; GApplicator *GApplicatorFactory::NewApp(GColourSpace Cs, int Op) { LgiAssert(_Factories >= 0 && _Factories < CountOf(_Factory)); for (int i=0; i<_Factories; i++) { GApplicator *a = _Factory[i]->Create(Cs, Op); if (a) return a; } return new NullApplicator; // At least it won't crash. } diff --git a/src/win32/General/GFile.cpp b/src/win32/General/GFile.cpp --- a/src/win32/General/GFile.cpp +++ b/src/win32/General/GFile.cpp @@ -1,1828 +1,1828 @@ /*hdr ** FILE: File.cpp ** AUTHOR: Matthew Allen ** DATE: 11/7/95 ** DESCRIPTION: The new file subsystem ** ** Copyright (C) 1995, Matthew Allen ** fret@memecode.com */ /****************************** Includes ************************************************************************************/ #include #include #include #include #include #include "LgiInc.h" #include "LgiOsDefs.h" #include "GVariant.h" #include #include #include #include #ifdef _MSC_VER #include #include #endif #include "GFile.h" #include "GContainers.h" #include "GLibrary.h" #include "GToken.h" #include "LgiCommon.h" #include "LMutex.h" #include "GString.h" #include "GLibrary.h" /****************************** Defines *************************************************************************************/ #define FILEDEBUG #define CHUNK 0xFFF0 #define FLOPPY_360K 0x0001 #define FLOPPY_720K 0x0002 #define FLOPPY_1_2M 0x0004 #define FLOPPY_1_4M 0x0008 #define FLOPPY_5_25 (FLOPPY_360K | FLOPPY_1_2M) #define FLOPPY_3_5 (FLOPPY_720K | FLOPPY_1_4M) /****************************** Helper Functions ****************************************************************************/ char *ReadTextFile(const char *File) { char *s = 0; GFile f; if (File && f.Open(File, O_READ)) { auto Len = f.GetSize(); s = new char[Len+1]; if (s) { auto Read = f.Read(s, Len); s[Read] = 0; } } return s; } int64 LgiFileSize(const char *FileName) { int64 Size = -1; GDirectory Dir; if (Dir.First(FileName, 0)) { Size = Dir.GetSize(); } return Size; } bool FileExists(const char *Name, char *CorrectCase) { bool Status = false; if (Name) { HANDLE hFind = INVALID_HANDLE_VALUE; GAutoWString n(Utf8ToWide(Name)); if (n) { WIN32_FIND_DATAW Info; hFind = FindFirstFileW(n, &Info); if (hFind != INVALID_HANDLE_VALUE) Status = StrcmpW(Info.cFileName, L".") != 0; } if (hFind != INVALID_HANDLE_VALUE) { FindClose(hFind); return true; } } return false; } bool DirExists(const char *Dir, char *CorrectCase) { GAutoWString n(Utf8ToWide(Dir)); DWORD e = GetFileAttributesW(n); return e != 0xFFFFFFFF && TestFlag(e, FILE_ATTRIBUTE_DIRECTORY); } const char *GetErrorName(int e) { static char Buf[256]; char *s = Buf; Buf[0] = 0; ::FormatMessageA( // FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, NULL, e, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), (LPSTR)Buf, sizeof(Buf), NULL); s = Buf + strlen(Buf); while (s > Buf && strchr(" \t\r\n", s[-1])) { s--; *s = 0; } return Buf[0] ? Buf : 0; } template int _GetLongPathName ( T *Short, T *Long, int LongLen ) { bool Status = false; int Len = 0; if (Short && Long) { HMODULE hDll = 0; hDll = LoadLibrary(_T("kernel32.dll")); if (hDll) { // Win98/ME/2K... short->long conversion supported #ifdef UNICODE typedef DWORD (__stdcall *Proc_GetLongPathName)(LPCWSTR, LPWSTR, DWORD cchBuffer); Proc_GetLongPathName Proc = (Proc_GetLongPathName)GetProcAddress(hDll, "GetLongPathNameW"); #else typedef DWORD (__stdcall *Proc_GetLongPathName)(LPCSTR, LPSTR, DWORD); Proc_GetLongPathName Proc = (Proc_GetLongPathName)GetProcAddress(hDll, "GetLongPathNameA"); #endif if (Proc) { Len = Proc(Short, Long, LongLen); Status = true; } FreeLibrary(hDll); } if (!Status) { // Win95/NT4 doesn't support this function... so we do the conversion ourselves TCHAR *In = Short; TCHAR *Out = Long; *Out = 0; while (*In) { // find end of segment TCHAR *e = Strchr(In, DIR_CHAR); if (!e) e = In + Strlen(In); // process segment TCHAR Old = *e; *e = 0; if (Strchr(In, ':')) { // drive specification Strcat(Long, LongLen, In); // just copy over } else { TCHAR Sep[] = {DIR_CHAR, 0}; Strcat(Out, LongLen, Sep); if (Strlen(In) > 0) { // has segment to work on WIN32_FIND_DATA Info; HANDLE Search = FindFirstFile(Short, &Info); if (Search != INVALID_HANDLE_VALUE) { // have long name segment, copy over Strcat(Long, LongLen, Info.cFileName); FindClose(Search); } else { // no long name segment... copy over short segment :( Strcat(Long, LongLen, In); } } // else is a double path char... i.e. as in a M$ network path } In = (Old) ? e + 1 : e; Out += Strlen(Out); *e = Old; } } } return Len; } bool ResolveShortcut(const char *LinkFile, char *Path, ssize_t Len) { bool Status = false; HWND hwnd = NULL; HRESULT hres; IShellLink* psl; TCHAR szGotPath[DIR_PATH_SIZE] = {0}; WIN32_FIND_DATA wfd; CoInitialize(0); hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void**) &psl); if (SUCCEEDED(hres)) { IPersistFile* ppf; hres = psl->QueryInterface(IID_IPersistFile, (void**) &ppf); if (SUCCEEDED(hres)) { char16 wsz[DIR_PATH_SIZE]; MultiByteToWideChar(CP_ACP, 0, LinkFile, -1, wsz, DIR_PATH_SIZE); char16 *l = StrrchrW(wsz, '.'); if (l && StricmpW(l, L".lnk")) { StrcatW(wsz, L".lnk"); } hres = ppf->Load(wsz, STGM_READ); if (SUCCEEDED(hres)) { hres = psl->Resolve(hwnd, SLR_ANY_MATCH); if (SUCCEEDED(hres)) { hres = psl->GetPath(szGotPath, DIR_PATH_SIZE, (WIN32_FIND_DATA *)&wfd, SLGP_SHORTPATH ); if (SUCCEEDED(hres) && Strlen(szGotPath) > 0) { #ifdef UNICODE TCHAR TmpPath[MAX_PATH]; ssize_t TpLen = _GetLongPathName(szGotPath, TmpPath, CountOf(TmpPath)) * sizeof(TmpPath[0]); const void *Tp = TmpPath; ssize_t OutLen = LgiBufConvertCp(Path, "utf-8", Len-1, Tp, LGI_WideCharset, TpLen); Path[OutLen] = 0; #else _GetLongPathName(szGotPath, Path, Len); #endif Status = true; } } } ppf->Release(); } psl->Release(); } CoUninitialize(); return Status; } void WriteStr(GFile &f, const char *s) { auto Len = (s) ? strlen(s) : 0; f << Len; if (Len > 0) { f.Write(s, Len); } } char *ReadStr(GFile &f DeclDebugArgs) { char *s = 0; // read the strings length... - uint32 Len; + uint32_t Len; f >> Len; if (Len > 0) { // 16mb sanity check.... anything over this // is _probably_ an error if (Len >= (16 << 20)) { // LgiAssert(0); return 0; } // allocate the memory buffer #if defined MEMORY_DEBUG s = new(_file, _line) char[Len+1]; #else s = new char[Len+1]; #endif if (s) { // read the bytes from disk f.Read(s, Len); s[Len] = 0; } else { // memory allocation error, skip the data // on disk so the caller is where they think // they are in the file. f.Seek(Len, SEEK_CUR); } } return s; } ssize_t SizeofStr(const char *s) { - return sizeof(uint32) + ((s) ? strlen(s) : 0); + return sizeof(uint32_t) + ((s) ? strlen(s) : 0); } bool LgiGetDriveInfo ( char *Path, uint64 *Free, uint64 *Size, uint64 *Available ) { bool Status = false; ULARGE_INTEGER available = {0, 0}; ULARGE_INTEGER total = {0, 0}; ULARGE_INTEGER free = {0, 0}; if (Path) { GAutoWString w(Utf8ToWide(Path)); if (w) { char16 *d = StrchrW(w, DIR_CHAR); if (d) *d = 0; if (GetDiskFreeSpaceExW(w, &available, &total, &free)) { if (Free) *Free = free.QuadPart; if (Size) *Size = total.QuadPart; if (Available) *Available = available.QuadPart; Status = true; } } } return Status; } /****************************** Classes ***************************************/ GVolume::GVolume() { _Type = VT_NONE; _Flags = 0; _Size = 0; _Free = 0; } class GWin32Volume : public GVolume { bool IsRoot; List _Sub; public: GWin32Volume(LgiSystemPath Type, char *Name) { IsRoot = Type == LSP_ROOT; _Name = Name; _Type = VT_FOLDER; int Id = 0; switch (Type) { case LSP_HOME: { Id = CSIDL_PROFILE; break; } case LSP_DESKTOP: { _Type = VT_DESKTOP; Id = CSIDL_DESKTOPDIRECTORY; break; } } if (Id) _Path = WinGetSpecialFolderPath(Id); else _Path = LGetSystemPath(Type); } GWin32Volume(const char *Drive) { IsRoot = false; int type = GetDriveTypeA(Drive); if (type != DRIVE_UNKNOWN && type != DRIVE_NO_ROOT_DIR) { char Buf[DIR_PATH_SIZE]; const char *Desc = 0; switch (type) { case DRIVE_REMOVABLE: { _Type = VT_REMOVABLE; if (GetVolumeInformationA(Drive, Buf, sizeof(Buf), 0, 0, 0, 0, 0) && ValidStr(Buf)) { Desc = Buf; } break; } case DRIVE_REMOTE: Desc = "Network"; _Type = VT_NETWORK_SHARE; break; case DRIVE_CDROM: Desc = "Cdrom"; _Type = VT_CDROM; break; case DRIVE_RAMDISK: Desc = "Ramdisk"; _Type = VT_RAMDISK; break; case DRIVE_FIXED: default: { if (GetVolumeInformationA(Drive, Buf, sizeof(Buf), 0, 0, 0, 0, 0) && ValidStr(Buf)) { Desc = Buf; } else { Desc = "Hard Disk"; } _Type = VT_HARDDISK; break; } } if (Desc) { char s[DIR_PATH_SIZE]; sprintf_s(s, sizeof(s), "%s (%.2s)", Desc, Drive); _Name = s; } _Path = Drive; } } ~GWin32Volume() { _Sub.DeleteObjects(); } bool IsMounted() { return false; } bool SetMounted(bool Mount) { return Mount; } void Insert(GAutoPtr v) { _Sub.Insert(v.Release()); } GVolume *First() { if (IsRoot) { // Get drive list IsRoot = false; char Str[512]; if (GetLogicalDriveStringsA(sizeof(Str), Str) > 0) { for (char *d = Str; *d; d += strlen(d) + 1) { GWin32Volume *v = new GWin32Volume(d); if (v) { if (v->Name()) _Sub.Insert(v); else DeleteObj(v); } } } } return _Sub.First(); } GVolume *Next() { return _Sub.Next(); } GDirectory *GetContents() { GDirectory *Dir = 0; if (_Path.Get()) { if (Dir = new GDirectory) { if (!Dir->First(_Path)) { DeleteObj(Dir); } } } return Dir; } }; //////////////////////////////////////////////////////////////////////////////// GFileSystem *GFileSystem::Instance = 0; class GFileSystemPrivate { public: GFileSystemPrivate() { } ~GFileSystemPrivate() { } }; GFileSystem::GFileSystem() { Instance = this; d = new GFileSystemPrivate; Root = 0; } GFileSystem::~GFileSystem() { DeleteObj(Root); DeleteObj(d); } void GFileSystem::OnDeviceChange(char *Reserved) { DeleteObj(Root); } GVolume *GFileSystem::GetRootVolume() { if (!Root) { Root = new GWin32Volume(LSP_ROOT, "Computer"); GAutoPtr v(new GWin32Volume(LSP_DESKTOP, "Desktop")); Root->Insert(v); v.Reset(new GWin32Volume(LSP_USER_DOWNLOADS, "Downloads")); Root->Insert(v); } return Root; } bool GFileSystem::Copy(const char *From, const char *To, LError *ErrorCode, CopyFileCallback Callback, void *Token) { if (!From || !To) { if (ErrorCode) *ErrorCode = ERROR_INVALID_PARAMETER; return false; } GFile In, Out; if (!In.Open(From, O_READ)) { if (ErrorCode) *ErrorCode = In.GetError(); return false; } if (!Out.Open(To, O_WRITE)) { if (ErrorCode) *ErrorCode = Out.GetError(); return false; } if (Out.SetSize(0)) { if (ErrorCode) *ErrorCode = ERROR_WRITE_FAULT; return false; } int64 Size = In.GetSize(); if (!Size) { return true; } int64 Block = min((1 << 20), Size); char *Buf = new char[Block]; if (!Buf) { if (ErrorCode) *ErrorCode = ERROR_NOT_ENOUGH_MEMORY; return false; } int64 i = 0; while (i < Size) { auto r = In.Read(Buf, Block); if (r > 0) { ssize_t Written = 0; while (Written < r) { auto w = Out.Write(Buf + Written, r - Written); if (w > 0) { Written += w; } else { if (ErrorCode) *ErrorCode = ERROR_WRITE_FAULT; goto ExitCopyLoop; } } i += Written; if (Callback) { if (!Callback(Token, i, Size)) { break; } } } else break; } ExitCopyLoop: DeleteArray(Buf); if (i == Size) { if (ErrorCode) *ErrorCode = ERROR_SUCCESS; } else { Out.Close(); Delete(To, false); } return i == Size; } bool GFileSystem::Delete(GArray &Files, GArray *Status, bool ToTrash) { bool Ret = true; if (ToTrash) { GArray Name; for (int i=0; i w(Utf8ToWide(Files[i])); auto Chars = StrlenW(w); auto Len = Name.Length(); Name.Length(Len + Chars + 1); StrcpyW(Name.AddressOf(Len), w.Get()); } Name.Add(0); SHFILEOPSTRUCTW s; ZeroObj(s); s.hwnd = 0; s.wFunc = FO_DELETE; s.pFrom = Name.AddressOf(); s.fFlags = FOF_NOCONFIRMATION; // FOF_ALLOWUNDO; int e = SHFileOperationW(&s); Ret = e == 0; if (Status && e) { for (int i=0; i Files; Files.Add(FileName); return Delete(Files, 0, ToTrash); } bool GFileSystem::CreateFolder(const char *PathName, bool CreateParentFoldersIfNeeded, LError *Err) { GAutoWString w(Utf8ToWide(PathName)); bool Status = ::CreateDirectoryW(w, NULL) != 0; if (!Status) { int Code = GetLastError(); if (Err) Err->Set(Code); if (CreateParentFoldersIfNeeded && Code == ERROR_PATH_NOT_FOUND) { char Base[DIR_PATH_SIZE]; strcpy_s(Base, sizeof(Base), PathName); do { char *Leaf = strrchr(Base, DIR_CHAR); if (!Leaf) return false; *Leaf = 0; } while (!DirExists(Base)); GToken Parts(PathName + strlen(Base), DIR_STR); for (int i=0; iSet(GetLastError()); break; } } } } return Status; } bool GFileSystem::RemoveFolder(const char *PathName, bool Recurse) { if (Recurse) { GDirectory Dir; if (Dir.First(PathName)) { do { char Str[DIR_PATH_SIZE]; if (Dir.Path(Str, sizeof(Str))) { if (Dir.IsDir()) { RemoveFolder(Str, Recurse); } else { Delete(Str, false); } } } while (Dir.Next()); } } #ifdef UNICODE GAutoWString w(Utf8ToWide(PathName)); if (!::RemoveDirectory(w)) #else if (!::RemoveDirectory(PathName)) #endif { #ifdef _DEBUG DWORD e = GetLastError(); #endif return false; } return true; } bool GFileSystem::Move(const char *OldName, const char *NewName, LError *Err) { if (!OldName || !NewName) { if (Err) Err->Set(LErrorInvalidParam); return false; } GAutoWString New(Utf8ToWide(NewName)); GAutoWString Old(Utf8ToWide(OldName)); if (!New || !Old) { if (Err) Err->Set(LErrorNoMem); return false; } if (!::MoveFileW(Old, New)) { if (Err) Err->Set(GetLastError()); return false; } return true; } /* bool GFileSystem::GetVolumeInfomation(char Drive, VolumeInfo *pInfo) { bool Status = false; if (pInfo) { char Name[] = "A:\\"; uint32 Serial; Name[0] = Drive + 'A'; if (GetVolumeInformation( Name, pInfo->Name, sizeof(pInfo->Name), &Serial, &pInfo->MaxPath, &pInfo->Flags, pInfo->System, sizeof(pInfo->System))) { pInfo->Drive = Drive; Status = true; } } return Status; } */ void GetFlagsStr(char *flagstr, short flags) { if (flagstr) { flagstr[0] = 0; if (flags & O_WRONLY) { strcat(flagstr,"w"); } else if (flags & O_APPEND) { strcat(flagstr,"a"); } else { strcat(flagstr,"r"); } if ((flags & O_RDWR) == O_RDWR) { strcat(flagstr,"+"); } strcat(flagstr,"b"); } } bool Match(char *Name, char *Mask) { while (*Name && *Mask) { if (*Mask == '*') { if (toupper(*Name) == toupper(Mask[1])) { Mask++; } else { Name++; } } else if (*Mask == '?' || toupper(*Mask) == toupper(*Name)) { Mask++; Name++; } else { return false; } } while (*Mask && ((*Mask == '*') || (*Mask == '.'))) Mask++; return (*Name == 0 && *Mask == 0); } short DaysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int LeapYear(int year) { if (year & 3) { return 0; } if ((year % 100 == 0) && !(year % 400 == 0)) { return 0; } return 1; } // Disk Infomation typedef struct { uchar bsJump[3]; uchar bsOemName[8]; ushort bsBytePerSec; uchar bsSecPerCluster; ushort bsResSectores; uchar bsFAT; ushort bsRootDirEnts; ushort bsSectors; uchar bsMedia; ushort bsFATsecs; ushort bsSecPerTrack; ushort bsHeads; ulong bsHiddenSecs; ulong bsHugeSectoes; uchar bsDriveNumber; uchar bsReserved; uchar bsBootSig; ulong bsVolumeID; // serial number uchar bsVolumeLabel[11]; uchar bsFileSysType[8]; } BOOTSECTOR; typedef struct { ulong diStartSector; ushort diSectors; char *diBuffer; } DISKIO; ///////////////////////////////////////////////////////////////////////////////// bool GDirectory::ConvertToTime(char *Str, int SLen, uint64 Time) const { if (Str) { FILETIME t; SYSTEMTIME s; t.dwHighDateTime = Time >> 32; t.dwLowDateTime = Time & 0xFFFFFFFF; if (FileTimeToSystemTime(&t, &s)) { int Hour = (s.wHour < 1) ? s.wHour + 23 : s.wHour - 1; char AP = (Hour >= 12) ? 'a' : 'p'; Hour %= 12; if (Hour == 0) Hour = 12; sprintf_s(Str, SLen, "%i:%02.2i:%02.2i %c", Hour-1, s.wMinute, s.wSecond, AP); } } return false; } bool GDirectory::ConvertToDate(char *Str, int SLen, uint64 Time) const { if (Str) { FILETIME t; SYSTEMTIME s; t.dwHighDateTime = Time >> 32; t.dwLowDateTime = Time & 0xFFFFFFFF; if (FileTimeToSystemTime(&t, &s)) { sprintf_s(Str, SLen, "%i/%i/%i", s.wDay, s.wMonth, s.wYear); } } return false; } ///////////////////////////////////////////////////////////////////////////////// //////////////////////////// Directory ////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////// struct GDirectoryPriv { char BasePath[DIR_PATH_SIZE]; char *BaseEnd; int BaseRemaining; GString Utf; HANDLE Handle; WIN32_FIND_DATAW Data; GDirectoryPriv() { Handle = INVALID_HANDLE_VALUE; BasePath[0] = 0; BaseRemaining = 0; } ~GDirectoryPriv() { } }; GDirectory::GDirectory() { d = new GDirectoryPriv; } GDirectory::~GDirectory() { Close(); DeleteObj(d); } GDirectory *GDirectory::Clone() { return new GDirectory; } const char *GDirectory::FullPath() { auto n = GetName(); if (!n) return NULL; strcpy_s(d->BaseEnd, d->BaseRemaining, n); return d->BasePath; } bool GDirectory::Path(char *s, int BufLen) const { char *Name = GetName(); auto Len = strlen(d->BasePath) + 2; if (Name) Len += strlen(Name); bool Status = false; if (Name && (BufLen < 0 || Len <= BufLen)) { LgiMakePath(s, BufLen, d->BasePath, Name); Status = true; } else LgiAssert(!"Not enough output buffer to write path."); return Status; } -size_t Utf8To16Cpy(uint16 *out, ssize_t outChar, uint8 *in, ssize_t inChar = -1) +size_t Utf8To16Cpy(uint16_t *out, ssize_t outChar, uint8_t *in, ssize_t inChar = -1) { auto start = out; int32 u32; outChar <<= 1; // words to bytes if (inChar < 0) { while (*in && outChar >= 4) { ssize_t len = 6; u32 = LgiUtf8To32(in, len); LgiUtf32To16(u32, out, outChar); } } else { while (outChar >= 4 && (u32 = LgiUtf8To32(in, inChar))) { LgiUtf32To16(u32, out, outChar); } } outChar >>= 1; // bytes to words if (outChar > 0) *out = 0; return out - start; } -size_t Utf16To8Cpy(uint8 *out, ssize_t outChar, const uint16 *in, ssize_t inChar = -1) +size_t Utf16To8Cpy(uint8_t *out, ssize_t outChar, const uint16_t *in, ssize_t inChar = -1) { auto start = out; int32 u32; if (inChar < 0) { while (*in && outChar > 0) { ssize_t len = 4; u32 = LgiUtf16To32(in, len); LgiUtf32To8(u32, out, outChar); } } else { inChar <<= 1; // words to bytes while (outChar > 0 && (u32 = LgiUtf16To32(in, inChar))) { LgiUtf32To8(u32, out, outChar); } inChar >>= 1; // bytes to words } if (outChar > 0) *out = 0; return out - start; } template size_t UnicodeCpy(O *out, ssize_t outChar, I *in, ssize_t inChar = -1) { if (out == NULL || in == NULL) return 0; if (sizeof(O) == 2 && sizeof(I) == 1) - return Utf8To16Cpy((uint16*)out, outChar, (uint8*)in, inChar); + return Utf8To16Cpy((uint16_t*)out, outChar, (uint8_t*)in, inChar); else if (sizeof(O) == 1 && sizeof(I) == 2) - return Utf16To8Cpy((uint8*)out, outChar, (uint16*)in, inChar); + return Utf16To8Cpy((uint8_t*)out, outChar, (uint16_t*)in, inChar); else LgiAssert(0); return 0; } int GDirectory::First(const char *InName, const char *Pattern) { Close(); d->Utf.Empty(); if (!InName) return false; wchar_t wTmp[DIR_PATH_SIZE], wIn[DIR_PATH_SIZE]; UnicodeCpy(wTmp, CountOf(wTmp), InName); auto Chars = GetFullPathNameW(wTmp, CountOf(wTmp), wIn, NULL); UnicodeCpy(d->BasePath, sizeof(d->BasePath), wIn, Chars); d->BaseEnd = d->BasePath + strlen(d->BasePath); if (d->BaseEnd > d->BasePath && d->BaseEnd[-1] != DIR_CHAR) { *d->BaseEnd++ = DIR_CHAR; *d->BaseEnd = 0; } ssize_t Used = d->BaseEnd - d->BasePath; d->BaseRemaining = (int) (sizeof(d->BasePath) - Used); char Str[DIR_PATH_SIZE]; wchar_t *FindArg; if (Pattern) { if (!LgiMakePath(Str, sizeof(Str), d->BasePath, Pattern)) return false; UnicodeCpy(FindArg = wTmp, CountOf(wTmp), Str); } else { FindArg = wIn; } d->Handle = FindFirstFileW(FindArg, &d->Data); if (d->Handle != INVALID_HANDLE_VALUE) { while ( stricmp(GetName(), ".") == 0 || stricmp(GetName(), "..") == 0) { if (!Next()) return false; } } return d->Handle != INVALID_HANDLE_VALUE; } int GDirectory::Next() { int Status = false; d->Utf.Empty(); if (d->Handle != INVALID_HANDLE_VALUE) { Status = FindNextFileW(d->Handle, &d->Data); } return Status; } int GDirectory::Close() { if (d->Handle != INVALID_HANDLE_VALUE) { FindClose(d->Handle); d->Handle = INVALID_HANDLE_VALUE; } d->Utf.Empty(); return true; } int GDirectory::GetUser(bool Group) const { return 0; } bool GDirectory::IsReadOnly() const { return (d->Data.dwFileAttributes & FA_READONLY) != 0; } bool GDirectory::IsDir() const { return (d->Data.dwFileAttributes & FA_DIRECTORY) != 0; } bool GDirectory::IsSymLink() const { return (d->Data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0; } bool GDirectory::IsHidden() const { return (d->Data.dwFileAttributes & FA_HIDDEN) != 0; } long GDirectory::GetAttributes() const { return d->Data.dwFileAttributes; } int GDirectory::GetType() const { return IsDir() ? VT_FOLDER : VT_FILE; } char *GDirectory::GetName() const { if (!d->Utf) d->Utf = d->Data.cFileName; return d->Utf; } GString GDirectory::FileName() const { if (!d->Utf) d->Utf = d->Data.cFileName; return d->Utf; } uint64 GDirectory::GetCreationTime() const { return ((uint64) d->Data.ftCreationTime.dwHighDateTime) << 32 | d->Data.ftCreationTime.dwLowDateTime; } uint64 GDirectory::GetLastAccessTime() const { return ((uint64) d->Data.ftLastAccessTime.dwHighDateTime) << 32 | d->Data.ftLastAccessTime.dwLowDateTime; } uint64 GDirectory::GetLastWriteTime() const { return ((uint64) d->Data.ftLastWriteTime.dwHighDateTime) << 32 | d->Data.ftLastWriteTime.dwLowDateTime; } uint64 GDirectory::GetSize() const { return ((uint64) d->Data.nFileSizeHigh) << 32 | d->Data.nFileSizeLow; } struct ClusterSizeMap { ClusterSizeMap() { ZeroObj(Sizes); } uint64 GetDriveCluserSize(char Letter) { uint64 Cs = 4096; auto letter = ToLower(Letter) - 'a'; Cs = Sizes[letter]; if (!Cs) { DWORD SectorsPerCluster, BytesPerSector; const char Drive[] = { Letter , ':', '\\', 0 }; BOOL b = GetDiskFreeSpaceA(Drive, &SectorsPerCluster, &BytesPerSector, NULL, NULL); if (b) Sizes[letter] = Cs = SectorsPerCluster * BytesPerSector; else LgiAssert(0); } return Cs; } private: uint64 Sizes[26]; } ClusterSizes; int64 GDirectory::GetSizeOnDisk() { auto Fp = FullPath(); if (!Fp || !IsAlpha(Fp[0])) return -1; auto ClusterSize = ClusterSizes.GetDriveCluserSize(Fp[0]); DWORD HighSize = 0; DWORD LoSize = GetCompressedFileSizeA(FullPath(), &HighSize); *d->BaseEnd = 0; auto Size = ((uint64)HighSize << 32) | LoSize; return ((Size + ClusterSize - 1) / ClusterSize) * ClusterSize; } ///////////////////////////////////////////////////////////////////////////////// //////////////////////////// File /////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////// class GFilePrivate { public: OsFile hFile; char *Name; int Attributes; bool Swap; bool Status; OsThread CreateThread; DWORD LastError; GFilePrivate() { hFile = INVALID_HANDLE; Name = 0; Attributes = 0; Swap = false; Status = false; LastError = 0; } ~GFilePrivate() { DeleteArray(Name); } }; GFile::GFile(const char *Path, int Mode) { d = new GFilePrivate; if (Path) Open(Path, Mode); } GFile::~GFile() { if (ValidHandle(d->hFile)) { Close(); } DeleteObj(d); } OsFile GFile::Handle() { return d->hFile; } char *GFile::GetName() { return d->Name; } void GFile::SetSwap(bool s) { d->Swap = s; } bool GFile::GetSwap() { return d->Swap; } int GFile::GetOpenMode() { return d->Attributes; } int GFile::GetBlockSize() { DWORD SectorsPerCluster; DWORD BytesPerSector = 0; DWORD NumberOfFreeClusters; DWORD TotalNumberOfClusters; #ifdef UNICODE GAutoWString n(Utf8ToWide(GetName())); #else GAutoString n(NewStr(GetName())); #endif if (n) { TCHAR *dir = n ? Strchr(n.Get(), '\\') : 0; if (dir) dir[1] = 0; GetDiskFreeSpace( n, &SectorsPerCluster, &BytesPerSector, &NumberOfFreeClusters, &TotalNumberOfClusters); } return BytesPerSector; } int GFile::Open(const char *File, int Mode) { int Status = false; bool NoCache = (Mode & O_NO_CACHE) != 0; Mode &= ~O_NO_CACHE; Close(); if (File) { bool SharedAccess = (Mode & O_SHARE) != 0; Mode &= ~O_SHARE; if (File[0] == '/' && File[1] == '/') { // This hangs the process, so just bail safely. return false; } GAutoWString n(Utf8ToWide(File)); if (n) { d->hFile = CreateFileW( n, Mode, FILE_SHARE_READ | (SharedAccess ? FILE_SHARE_WRITE : 0), 0, (Mode & O_WRITE) ? OPEN_ALWAYS : OPEN_EXISTING, NoCache ? FILE_FLAG_NO_BUFFERING : 0, NULL); } if (!ValidHandle(d->hFile)) { switch (d->LastError = GetLastError()) { case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: { // Path/File not found; int i=0; break; } case ERROR_ACCESS_DENIED: { // Access is denied: // Read-Only or another process has the file open int i=0; break; } } } else { d->Attributes = Mode; d->Name = NewStr(File); Status = true; d->Status = true; d->CreateThread = LgiGetCurrentThread(); } } return Status; } bool GFile::IsOpen() { return ValidHandle(d->hFile); } int GFile::GetError() { return d->LastError; } int GFile::Close() { int Status = false; if (ValidHandle(d->hFile)) { ::CloseHandle(d->hFile); d->hFile = INVALID_HANDLE; } DeleteArray(d->Name); return Status; } int64 GFile::GetSize() { LgiAssert(IsOpen()); DWORD High = -1; DWORD Low = GetFileSize(d->hFile, &High); return Low | ((int64)High<<32); } int64 GFile::SetSize(int64 Size) { LgiAssert(IsOpen()); LONG OldPosHigh = 0; DWORD OldPosLow = SetFilePointer(d->hFile, 0, &OldPosHigh, SEEK_CUR); LONG SizeHigh = Size >> 32; DWORD r = SetFilePointer(d->hFile, (LONG)Size, &SizeHigh, FILE_BEGIN); BOOL b = SetEndOfFile(d->hFile); if (!b) { DWORD err = GetLastError(); LgiTrace("%s:%i - SetSize(" LPrintfInt64 ") failed: 0x%x\n", _FL, Size, err); } SetFilePointer(d->hFile, OldPosLow, &OldPosHigh, FILE_BEGIN); return GetSize(); } int64 GFile::GetPos() { LgiAssert(IsOpen()); LONG PosHigh = 0; DWORD PosLow = SetFilePointer(d->hFile, 0, &PosHigh, FILE_CURRENT); return PosLow | ((int64)PosHigh<<32); } int64 GFile::SetPos(int64 Pos) { LgiAssert(IsOpen()); LONG PosHigh = Pos >> 32; DWORD PosLow = SetFilePointer(d->hFile, (LONG)Pos, &PosHigh, FILE_BEGIN); return PosLow | ((int64)PosHigh<<32); } ssize_t GFile::Read(void *Buffer, ssize_t Size, int Flags) { ssize_t Rd = 0; // This loop allows ReadFile (32bit) to read more than 4GB in one go. If need be. for (ssize_t Pos = 0; Pos < Size; ) { DWORD Bytes = 0; int BlockSz = (int) MIN( Size - Pos, 1 << 30 ); // 1 GiB blocks if (ReadFile(d->hFile, (char*)Buffer + Pos, BlockSz, &Bytes, NULL) && Bytes == BlockSz) { Rd += Bytes; Pos += Bytes; d->Status &= Bytes > 0; } else { Rd += Bytes; d->Status &= Bytes > 0; d->LastError = GetLastError(); break; } } return Rd; } ssize_t GFile::Write(const void *Buffer, ssize_t Size, int Flags) { ssize_t Wr = 0; // This loop allows WriteFile (32bit) to read more than 4GB in one go. If need be. for (ssize_t Pos = 0; Pos < Size; ) { DWORD Bytes = 0; int BlockSz = (int) MIN( Size - Pos, 1 << 30 ); // 1 GiB blocks if (WriteFile(d->hFile, (const char*)Buffer + Pos, BlockSz, &Bytes, NULL) && Bytes == BlockSz) { Wr += Bytes; Pos += Bytes; d->Status &= Bytes > 0; } else { Wr += Bytes; d->Status &= Bytes > 0; d->LastError = GetLastError(); break; } } return Wr; } int64 GFile::Seek(int64 To, int Whence) { LgiAssert(IsOpen()); int Mode; switch (Whence) { default: case SEEK_SET: Mode = FILE_BEGIN; break; case SEEK_CUR: Mode = FILE_CURRENT; break; case SEEK_END: Mode = FILE_END; break; } LONG ToHigh = To >> 32; DWORD ToLow = SetFilePointer(d->hFile, (LONG)To, &ToHigh, Mode); return ToLow | ((int64)ToHigh<<32); } bool GFile::Eof() { LgiAssert(IsOpen()); return GetPos() >= GetSize(); } ssize_t GFile::SwapRead(uchar *Buf, ssize_t Size) { DWORD r = 0; if (!ReadFile(d->hFile, Buf, (DWORD)Size, &r, NULL) || r != Size) { d->LastError = GetLastError(); return 0; } // Swap the bytes uchar *s = Buf, *e = Buf + Size - 1; while (s < e) { uchar t = *s; *s++ = *e; *e-- = t; } return r; } ssize_t GFile::SwapWrite(uchar *Buf, ssize_t Size) { int i = 0; DWORD w; Buf = &Buf[Size-1]; while (Size--) { if (!(d->Status &= WriteFile(d->hFile, Buf--, 1, &w, NULL) != 0 && w == 1)) break; i += w; } return i; } ssize_t GFile::ReadStr(char *Buf, ssize_t Size) { int i = 0; DWORD r; if (Buf && Size > 0) { char c; Size--; do { ReadFile(d->hFile, &c, 1, &r, NULL); if (Eof()) { break; } *Buf++ = c; i++; } while (i < Size - 1 && c != '\n'); *Buf = 0; } return i; } ssize_t GFile::WriteStr(char *Buf, ssize_t Size) { int i = 0; DWORD w; while (i <= Size) { WriteFile(d->hFile, Buf, 1, &w, NULL); Buf++; i++; if (*Buf == '\n') break; } return i; } void GFile::SetStatus(bool s) { d->Status = s; } bool GFile::GetStatus() { return d->Status; } #define RdIO \ { \ if (d->Swap) \ SwapRead((uchar*) &i, sizeof(i)); \ else \ Read(&i, sizeof(i)); \ return *this; \ } #define WrIO \ { \ if (d->Swap) \ SwapWrite((uchar*) &i, sizeof(i)); \ else \ Write(&i, sizeof(i)); \ return *this; \ } #define GFilePre GFile &GFile::operator >> ( #define GFilePost &i) RdIO GFileOps(); #undef GFilePre #undef GFilePost #define GFilePre GFile &GFile::operator << ( #define GFilePost i) WrIO GFileOps(); #undef GFilePre #undef GFilePost diff --git a/src/win32/Lgi/GApp.cpp b/src/win32/Lgi/GApp.cpp --- a/src/win32/Lgi/GApp.cpp +++ b/src/win32/Lgi/GApp.cpp @@ -1,1094 +1,1094 @@ #define _WIN32_WINNT 0x600 #ifndef _MSC_VER #include #endif #include "Lgi.h" #include #include #include #include "GSkinEngine.h" #include "GSymLookup.h" #include "GDocView.h" #include "GToken.h" #include "GCss.h" #include "GFontCache.h" #include #include "LgiSpellCheck.h" // Don't have a better place to put this... const char GSpellCheck::Delimiters[] = { ' ', '\t', '\r', '\n', ',', ',', '.', ':', ';', '{', '}', '[', ']', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '-', '+', '=', '|', '\\', '/', '?', '\"', 0 }; HINSTANCE _lgi_app_instance = 0; extern LPTOP_LEVEL_EXCEPTION_FILTER _PrevExceptionHandler; OsAppArguments::OsAppArguments() { _Default(); } OsAppArguments::OsAppArguments(int Args, char **Arg) { _Default(); Set(Args, Arg); } OsAppArguments &OsAppArguments::operator =(OsAppArguments &p) { hInstance = p.hInstance; Pid = p.Pid; nCmdShow = p.nCmdShow; lpCmdLine = 0; CmdLine.Reset(NewStrW(p.lpCmdLine)); lpCmdLine = CmdLine; return *this; } void OsAppArguments::_Default() { hInstance = 0; lpCmdLine = 0; Pid = GetCurrentProcessId(); nCmdShow = SW_RESTORE; } void OsAppArguments::Set(int Args, char **Arg) { GStringPipe p; for (int i=0; i FontCache; // Win32 bool QuitReceived; GApp::ClassContainer Classes; GSymLookup *SymLookup; GAppPrivate() { LinuxWine = -1; SymLookup = 0; QuitReceived = false; SkinLib = 0; GuiThread = NULL; auto b = DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &GuiThread, 0, false, DUPLICATE_SAME_ACCESS); ThemeAware = true; } ~GAppPrivate() { DeleteObj(SkinLib); DeleteObj(SymLookup); } }; ///////////////////////////////////////////////////////////////////////////// LONG __stdcall _ExceptionFilter_Redir(LPEXCEPTION_POINTERS e) { if (LgiApp) return LgiApp->_ExceptionFilter(e, LgiApp->d->ProductId); else LgiTrace("_ExceptionFilter_Redir error: No application ptr.\n"); return 0; } ///////////////////////////////////////////////////////////////////////////// GSkinEngine *GApp::SkinEngine = 0; GMouseHook *GApp::MouseHook = 0; GMouseHook *GApp::GetMouseHook() { return MouseHook; } static GAutoString ParseStr(GPointer &p, bool Pad = true) { char16 *Key = p.w; // Skip 'key' string while (*p.w) p.w++; // Skip NULL p.w++; if (Pad) { // Align to 32-bit boundry while ((NativeInt)p.u8 & 3) p.u8++; } return GAutoString(WideToUtf8(Key)); } static GAutoString ParseVer(void *Resource, char *Part) { GToken Parts(Part, "."); if (Parts.Length() == 3) { GPointer p; - p.u8 = (uint8*)Resource; + p.u8 = (uint8_t*)Resource; uint16 Len = *p.u16++; uint16 ValueLen = *p.u16++; uint16 Type = *p.u16++; GAutoString Key = ParseStr(p); // Read VS_FIXEDFILEINFO structure DWORD dwSig = *p.u32++; DWORD dwStrucVersion = *p.u32++; DWORD dwFileVersionMS = *p.u32++; DWORD dwFileVersionLS = *p.u32++; DWORD dwProductVersionMS = *p.u32++; DWORD dwProductVersionLS = *p.u32++; DWORD dwFileFlagsMask = *p.u32++; DWORD dwFileFlags = *p.u32++; DWORD dwFileOS = *p.u32++; DWORD dwFileType = *p.u32++; DWORD dwFileSubtype = *p.u32++; DWORD dwFileDateMS = *p.u32++; DWORD dwFileDateLS = *p.u32++; // Align to 32-bit boundry while ((NativeInt)p.u8 & 3) p.u8++; // Children - while (p.u8 < (uint8*)Resource + Len) + while (p.u8 < (uint8_t*)Resource + Len) { // Read StringFileInfo structures... - uint8 *fStart = p.u8; + uint8_t *fStart = p.u8; uint16 fLength = *p.u16++; uint16 fValueLength = *p.u16++; uint16 fType = *p.u16++; GAutoString fKey = ParseStr(p); if (strcmp(fKey, "StringFileInfo")) break; while (p.u8 < fStart + fLength) { // Read StringTable entries - uint8 *tStart = p.u8; + uint8_t *tStart = p.u8; uint16 tLength = *p.u16++; uint16 tValueLength = *p.u16++; uint16 tType = *p.u16++; GAutoString tKey = ParseStr(p); while (p.u8 < tStart + tLength) { // Read String entries - uint8 *sStart = p.u8; + uint8_t *sStart = p.u8; uint16 sLength = *p.u16++; uint16 sValueLength = *p.u16++; uint16 sType = *p.u16++; GAutoString sKey = ParseStr(p); GAutoString sValue; if (p.u8 < sStart + sLength) sValue = ParseStr(p); if (!stricmp(Parts[0], fKey) && !stricmp(Parts[1], tKey) && !stricmp(Parts[2], sKey)) { return sValue; } } } } } return GAutoString(); } void LgiInvalidParam(const wchar_t * expression, const wchar_t * function, const wchar_t * file, unsigned int line, uintptr_t pReserved) { LgiTrace("Invalid Parameter: %S (%S @ %S:%i)\n", expression, function, file, line); } ///////////////////////////////////////////////////////////////////////////// #include #include extern int MouseRollMsg; typedef HRESULT (CALLBACK *fDllGetVersion)(DLLVERSIONINFO *); GApp::GApp(OsAppArguments &AppArgs, const char *AppName, GAppArguments *ObjArgs) { // GApp instance SystemNormal = 0; SystemBold = 0; LgiAssert(TheApp == 0); TheApp = this; LgiAssert(AppName != NULL); Name(AppName); int64 Time = LgiCurrentTime(); #define DumpTime(str) /* \ { int64 n = LgiCurrentTime(); \ LgiTrace("%s=%ims\n", str, (int)(n-Time)); \ Time = n; } */ // Sanity Checks LgiAssert(sizeof(int8) == 1); - LgiAssert(sizeof(uint8) == 1); + LgiAssert(sizeof(uint8_t) == 1); LgiAssert(sizeof(int16) == 2); LgiAssert(sizeof(uint16) == 2); LgiAssert(sizeof(int32) == 4); - LgiAssert(sizeof(uint32) == 4); + LgiAssert(sizeof(uint32_t) == 4); LgiAssert(sizeof(int64) == 8); LgiAssert(sizeof(uint64) == 8); LgiAssert(sizeof(char16) == 2); LgiAssert(GDisplayString::FScale == (1 << GDisplayString::FShift)); DumpTime("start"); // Private data d = new GAppPrivate; char Mime[256]; sprintf_s(Mime, sizeof(Mime), "application/x-%s", AppName); d->Mime.Reset(NewStr(Mime)); DumpTime("priv"); // Setup exception handler HRSRC hRsrc = ::FindResource(NULL, MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); HGLOBAL hGlobal = ::LoadResource(NULL, hRsrc); LPVOID pVersionResource = ::LockResource(hGlobal); GAutoString ProductName, ProductVer; // replace "040904e4" with the language ID of your resources if (pVersionResource) { ProductName = ParseVer(pVersionResource, "StringFileInfo.0c0904b0.ProductName"); ProductVer = ParseVer(pVersionResource, "StringFileInfo.0c0904b0.ProductVersion"); } if (ProductName && ProductVer) { char s[256]; sprintf_s(s, sizeof(s), "%s-%s", ProductName.Get(), ProductVer.Get()); d->ProductId.Reset(NewStr(s)); } InitializeCriticalSection(&StackTraceSync); #if !defined(_DEBUG) _PrevExceptionHandler = SetUnhandledExceptionFilter(_ExceptionFilter_Redir); #endif _set_invalid_parameter_handler(LgiInvalidParam); DumpTime("exception handler"); // Initialize windows dll's OleInitialize(NULL); CoInitialize(NULL); InitCommonControls(); { /* GLibrary ComCtl32("ComCtl32.dll"); DLLVERSIONINFO info; ZeroObj(info); info.cbSize = sizeof(info); fDllGetVersion DllGetVersion = (fDllGetVersion)ComCtl32.GetAddress("DllGetVersion"); if (DllGetVersion) { HRESULT ret = DllGetVersion(&info); d->ThemeAware = info.dwMajorVersion >= 6; LgiTrace("ComCtl32.dll v%i.%i found (ret=%x)\n", info.dwMajorVersion, info.dwMinorVersion, ret); ) */ GArray Ver; LgiGetOs(&Ver); if (Ver.Length() > 1) { // LgiTrace("Windows v%i.%i\n", Ver[0], Ver[1]); if (Ver[0] < 6) { d->ThemeAware = false; } } #ifdef _MSC_VER if (!d->ThemeAware) { SetThemeAppProperties(0); } #endif } DumpTime("init common ctrls"); // Setup LGI Sub-systems GFontSystem::Inst(); DumpTime("font sys"); d->FileSystem = new GFileSystem; DumpTime("file sys"); d->GdcSystem = new GdcDevice; DumpTime("gdc"); // Vars... d->Config = 0; AppWnd = 0; SetAppArgs(AppArgs); DumpTime("vars"); // System font GFontType SysFontType; if (SysFontType.GetSystemFont("System")) { SystemNormal = SysFontType.Create(); if (SystemNormal) { // Force load SystemNormal->Create(); } else { LgiMsg(0, "Error: SysFontType.Create() failed.", "Lgi Error"); LgiExitApp(); } SystemBold = SysFontType.Create(); if (SystemBold) { SystemBold->Bold(true); SystemBold->Create(); } } else { LgiMsg(0, "Error: GetSystemFont failed.", "Lgi Error"); LgiExitApp(); } DumpTime("fonts"); // Other vars and init hNormalCursor = LoadCursor(NULL, IDC_ARROW); LgiRandomize((uint) (LgiCurrentTime()*GetCurrentThreadId())); MouseRollMsg = RegisterWindowMessage(L"MSWHEEL_ROLLMSG"); DumpTime("cursor/rand/msg"); LgiInitColours(); DumpTime("colours"); // Setup mouse hook MouseHook = new GMouseHook; DumpTime("ms hook"); if ( #if 0 // defined(LGI_STATIC) && _MSC_VER < _MSC_VER_VS2010 0 #else ( !ObjArgs || !ObjArgs->NoSkin ) && !GetOption("noskin") #endif ) { // Load library char SkinLibName[] = #ifdef __GNUC__ "lib" #endif "lgiskin" #if _MSC_VER == _MSC_VER_VC7 "7" #endif #if _MSC_VER == _MSC_VER_VS2005 "8" #endif #if _MSC_VER == _MSC_VER_VS2008 "9" #endif #if _MSC_VER == _MSC_VER_VS2010 "10" #endif #if _MSC_VER == _MSC_VER_VS2012 "11" #endif #if _MSC_VER == _MSC_VER_VS2013 "12" #endif #if _MSC_VER == _MSC_VER_VS2015 "14" #endif #ifdef _DEBUG "d" #endif ; #if HAS_SHARED_OBJECT_SKIN d->SkinLib = new GLibrary(SkinLibName); if (d->SkinLib) { if (d->SkinLib->IsLoaded()) { Proc_CreateSkinEngine CreateSkinEngine = (Proc_CreateSkinEngine)d->SkinLib->GetAddress(LgiSkinEntryPoint); if (CreateSkinEngine) { SkinEngine = CreateSkinEngine(this); } } else { DeleteObj(d->SkinLib); printf("Failed to load '%s'\n", SkinLibName); } } #else extern GSkinEngine *CreateSkinEngine(GApp *App); SkinEngine = CreateSkinEngine(this); #endif } DumpTime("skin"); } GApp::~GApp() { DeleteObj(AppWnd); DeleteObj(SystemNormal); DeleteObj(SystemBold); DeleteObj(MouseHook); TheApp = 0; DeleteObj(SkinEngine); DeleteObj(GFontSystem::Me); DeleteObj(d->FileSystem); DeleteObj(d->GdcSystem); DeleteObj(d->Config); d->Classes.DeleteObjects(); CoUninitialize(); OleUninitialize(); DeleteObj(d); DeleteCriticalSection(&StackTraceSync); } bool GApp::IsOk() { bool Status = (this != 0) && (d != 0) && (d->FileSystem != 0) && (d->GdcSystem != 0); if (!Status) LgiAssert(!"Hash table error"); return Status; } int GApp::GetCpuCount() { SYSTEM_INFO si; ZeroObj(si); GetSystemInfo(&si); return si.dwNumberOfProcessors ? si.dwNumberOfProcessors : -1; } OsThread GApp::_GetGuiThread() { return d->GuiThread; } bool GApp::InThread() { auto GuiId = GetGuiThreadId(); auto MyId = GetCurrentThreadId(); return GuiId == MyId; } OsThreadId GApp::GetGuiThreadId() { return GetThreadId(d->GuiThread); } GApp::ClassContainer *GApp::GetClasses() { return IsOk() ? &d->Classes : 0; } OsAppArguments *GApp::GetAppArgs() { return IsOk() ? &d->Args : 0; } GXmlTag *GApp::GetConfig(const char *Tag) { if (IsOk() && !d->Config) { const char File[] = "lgi.conf"; char Path[MAX_PATH]; if (LgiGetExePath(Path, sizeof(Path))) { LgiMakePath(Path, sizeof(Path), Path, File); if (FileExists(Path)) { d->Config = new GXmlTag("Config"); if (d->Config) { GFile f; if (f.Open(Path, O_READ)) { GXmlTree t; t.Read(d->Config, &f, 0); } } } } if (!d->Config) { d->Config = new GXmlTag("Options"); } } if (Tag && d->Config) { return d->Config->GetChildTag(Tag); } return 0; } void GApp::SetConfig(GXmlTag *Tag) { if (IsOk() && Tag) { GXmlTag *Old = GetConfig(Tag->GetTag()); if (Old) { Old->RemoveTag(); DeleteObj(Old); } if (!d->Config) { GetConfig(0); } if (d->Config) { d->Config->InsertTag(Tag); } } } const char *GApp::GetArgumentAt(int n) { if (d->Args.lpCmdLine) { char16 *s = d->Args.lpCmdLine; for (int i=0; i<=n; i++) { char16 *e = 0; while (*s && strchr(WhiteSpace, *s)) s++; if (*s == '\'' || *s == '\"') { char16 Delim = *s++; e = StrchrW(s, Delim); } else { for (e = s; *e && !strchr(WhiteSpace, *e); e++) ; } if (i == n) { return WideToUtf8(s, e - s); } s = *e ? e + 1 : e; } } return 0; } bool GApp::GetOption(const char *Option, GString &Buf) { if (!ValidStr(Option)) { return false; } char16 *c = d->Args.lpCmdLine; char16 *Opt = Utf8ToWide(Option); auto OptLen = StrlenW(Opt); while (c && *c) { if (*c == '/' || *c == '-') { c++; char16 *e = c; while (*e && (IsAlpha(*e)) || StrchrW(L"_-", *e)) e++; if (e - c == OptLen && !StrnicmpW(c, Opt, OptLen)) { c += OptLen; if (*c) { // skip leading whitespace while (*c && strchr(WhiteSpace, *c)) { c++; } // write str out if they want it char16 End = (*c == '\'' || *c == '\"') ? *c++ : ' '; char16 *e = StrchrW(c, End); if (!e) e = c + StrlenW(c); Buf.SetW(c, e-c); } // yeah we got the option DeleteArray(Opt); return true; } } c = StrchrW(c, ' '); if (c) c++; } DeleteArray(Opt); return false; } bool GApp::GetOption(const char *Option, char *Dest, int DestLen) { GString Buf; if (GetOption(Option, Buf)) { if (Dest) strcpy_s(Dest, DestLen, &Buf[0]); return true; } return false; } // #include "GInput.h" void GApp::SetAppArgs(OsAppArguments &AppArgs) { d->Args = AppArgs; } void GApp::OnCommandLine() { char WhiteSpace[] = " \r\n\t"; char *CmdLine = WideToUtf8(d->Args.lpCmdLine); if (ValidStr(CmdLine)) { // LgiTrace("CmdLine='%s'\n", CmdLine); GArray Files; char *Delim = "\'\""; char *s; for (s = CmdLine; *s; ) { // skip ws while (*s && strchr(WhiteSpace, *s)) s++; // read to end of token char *e = s; if (strchr(Delim, *s)) { char Delim = *s++; e = strchr(s, Delim); if (!e) e = s + strlen(s); } else { for (; *e && !strchr(WhiteSpace, *e); e++) ; } char *Arg = NewStr(s, e - s); if (Arg) { if (FileExists(Arg)) { Files.Add(Arg); } else { DeleteArray(Arg); } } // next s = (*e) ? e + 1 : e; } // call app if (Files.Length() > 0) { OnReceiveFiles(Files); } // clear up Files.DeleteArrays(); } DeleteArray(CmdLine); } void GApp::OnUrl(const char *Url) { if (AppWnd) AppWnd->OnUrl(Url); } void GApp::OnReceiveFiles(GArray &Files) { if (AppWnd) AppWnd->OnReceiveFiles(Files); } int32 GApp::GetMetric(LgiSystemMetric Metric) { int32 Status = 0; switch (Metric) { case LGI_MET_DECOR_X: { Status = GetSystemMetrics(SM_CXFRAME) * 2; break; } case LGI_MET_DECOR_Y: { Status = GetSystemMetrics(SM_CYFRAME) * 2; Status += GetSystemMetrics(SM_CYCAPTION); break; } case LGI_MET_DECOR_CAPTION: { Status = GetSystemMetrics(SM_CYCAPTION); break; } case LGI_MET_MENU: { Status = GetSystemMetrics(SM_CYMENU); break; } case LGI_MET_THEME_AWARE: { Status = d->ThemeAware; break; } } return Status; } GViewI *GApp::GetFocus() { HWND h = ::GetFocus(); if (h) { return GWindowFromHandle(h); } return 0; } HINSTANCE GApp::GetInstance() { if (this && IsOk()) { return d->Args.hInstance; } return (HINSTANCE)GetCurrentProcess(); } OsProcessId GApp::GetProcessId() { if (this) { return IsOk() ? d->Args.Pid : 0; } return GetCurrentProcessId(); } int GApp::GetShow() { return IsOk() ? d->Args.nCmdShow : 0; } class GWnd { public: LMutex *GetLock(GWindow *w) { return w->_Lock; } }; bool GApp::Run(bool Loop, OnIdleProc IdleCallback, void *IdleParam) { MSG Msg; bool status = true; ZeroObj(Msg); if (Loop) { OnCommandLine(); if (IdleCallback) { bool DontWait = true; while (!d->QuitReceived) { while (1) { bool Status; if (DontWait) { Status = PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE) != 0; } else { Status = GetMessage(&Msg, NULL, 0, 0) > 0; DontWait = true; } #if 0 char m[256]; sprintf_s(m, sizeof(m), "Msg=%i hwnd=%p %i,%i\n", Msg.message, Msg.hwnd, Msg.wParam, Msg.lParam); OutputDebugStringA(m); #endif if (!Status || Msg.message == WM_QUIT) break; #ifdef _DEBUG int64 Last = LgiCurrentTime(); #endif TranslateMessage(&Msg); DispatchMessage(&Msg); #ifdef _DEBUG int64 Now = LgiCurrentTime(); if (Now - Last > 10000) { LgiTrace("%s:%i - Msg Loop Blocked: %i ms (Msg: 0x%.4x)\n", _FL, (int) (Now - Last), Msg.message); } #endif } if (Msg.message == WM_QUIT) { d->QuitReceived = true; } else { DontWait = IdleCallback(IdleParam); } } } else { while (!d->QuitReceived && GetMessage(&Msg, NULL, 0, 0) > 0) { #ifdef _DEBUG int64 Last = LgiCurrentTime(); #endif TranslateMessage(&Msg); DispatchMessage(&Msg); #ifdef _DEBUG int64 Now = LgiCurrentTime(); if (Now - Last > 1000) { LgiTrace("%s:%i - Msg Loop Blocked: %i ms (Msg: 0x%.4x)\n", _FL, (int) (Now - Last), Msg.message); } #endif } } } else { while ( PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE) && Msg.message != WM_QUIT) { TranslateMessage(&Msg); DispatchMessage(&Msg); } if (Msg.message == WM_QUIT) { d->QuitReceived = true; } } return Msg.message != WM_QUIT; } void GApp::Exit(int Code) { if (Code) { exit(0); } else { PostQuitMessage(Code); } } GAutoString GApp::GetFileMimeType(const char *File) { GAutoString r; char m[128]; if (LGetFileMimeType(File, m, sizeof(m))) r.Reset(NewStr(m)); return r; } bool GApp::GetAppsForMimeType(char *Mime, GArray &Apps) { LgiAssert(!"Not impl."); return false; } GSymLookup *GApp::GetSymLookup() { if (!this) return 0; if (!d->SymLookup) d->SymLookup = new GSymLookup; return d->SymLookup; } bool GApp::IsWine() { if (d->LinuxWine < 0) { HMODULE hntdll = GetModuleHandle(L"ntdll.dll"); if (hntdll) { typedef const char * (CDECL *pwine_get_version)(void); pwine_get_version wine_get_version = (pwine_get_version)GetProcAddress(hntdll, "wine_get_version"); d->LinuxWine = wine_get_version != 0; } } return d->LinuxWine > 0; } bool GApp::IsElevated() { bool fRet = false; HANDLE hToken = NULL; if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken )) { TOKEN_ELEVATION Elevation; DWORD cbSize = sizeof(Elevation); if (GetTokenInformation(hToken, TokenElevation, &Elevation, sizeof(Elevation), &cbSize)) fRet = Elevation.TokenIsElevated != 0; } if (hToken) CloseHandle(hToken); return fRet; } GFontCache *GApp::GetFontCache() { if (!d->FontCache) d->FontCache.Reset(new GFontCache(SystemNormal)); return d->FontCache; } diff --git a/src/win32/Lgi/GClipBoard.cpp b/src/win32/Lgi/GClipBoard.cpp --- a/src/win32/Lgi/GClipBoard.cpp +++ b/src/win32/Lgi/GClipBoard.cpp @@ -1,879 +1,879 @@ #include "Lgi.h" #include "GClipBoard.h" #include "GPalette.h" #include "GCom.h" class GClipBoardPriv { public: GAutoString Utf8; GAutoWString Wide; }; #if 0 class LFileEnum : public GUnknownImpl { int Idx; GArray Types; public: LFileEnum(GClipBoard::FormatType type) { Idx = 0; // Types.Add(type); Types.Add(CF_HDROP); AddInterface(IID_IEnumFORMATETC, (IEnumFORMATETC*)this); } ~LFileEnum() { LgiTrace("%s:%i - ~LFileEnum()\n", _FL); } HRESULT STDMETHODCALLTYPE Next(ULONG celt, FORMATETC *fmt, ULONG *pceltFetched) { if (!fmt || Idx >= Types.Length()) return S_FALSE; fmt->cfFormat = Types[Idx]; fmt->dwAspect = DVASPECT_CONTENT; fmt->lindex = -1; fmt->ptd = NULL; fmt->tymed = TYMED_HGLOBAL; if (pceltFetched) *pceltFetched = 1; Idx += celt; LgiTrace("%s:%i - Next(%i) returned '%s'\n", _FL, celt, GClipBoard::FmtToStr(fmt->cfFormat).Get()); return S_OK; } HRESULT STDMETHODCALLTYPE Skip(ULONG celt) { return S_OK; } HRESULT STDMETHODCALLTYPE Reset() { return S_OK; } HRESULT STDMETHODCALLTYPE Clone(IEnumFORMATETC **ppenum) { return E_NOTIMPL; } }; struct LFileName : public GUnknownImpl { char16 *w; LFileName(STGMEDIUM *Med, const char *u) { Med->tymed = TYMED_FILE; Med->lpszFileName = w = Utf8ToWide(u); Med->pUnkForRelease = this; } ~LFileName() { DeleteArray(w); } }; class LFileData : public GUnknownImpl { int Cur; GClipBoard::FormatType Type, PrefDrop, ShellIdList; public: GString::Array Files; LFileData(GString::Array &files) : Files(files) { // TraceRefs = true; Type = GClipBoard::StrToFmt(CFSTR_FILENAMEA); PrefDrop = GClipBoard::StrToFmt(CFSTR_PREFERREDDROPEFFECT); ShellIdList = GClipBoard::StrToFmt(CFSTR_SHELLIDLIST); Cur = 0; AddInterface(IID_IDataObject, (IDataObject*)this); LgiTrace("%s:%i - LFileData() = %p\n", _FL, this); } ~LFileData() { LgiTrace("%s:%i - ~LFileData()\n", _FL); } HRESULT STDMETHODCALLTYPE GetData(FORMATETC *Fmt, STGMEDIUM *Med) { GString sFmt = GClipBoard::FmtToStr(Fmt->cfFormat); LgiTrace("%s:%i - GetData(%s) starting...\n", _FL, sFmt.Get()); if (!Med) return E_INVALIDARG; if (Fmt->cfFormat == PrefDrop) { Med->tymed = TYMED_HGLOBAL; Med->hGlobal = GlobalAlloc(GHND, sizeof(DWORD)); DWORD* data = (DWORD*)GlobalLock(Med->hGlobal); *data = DROPEFFECT_COPY; GlobalUnlock(Med->hGlobal); Med->pUnkForRelease = NULL; } else if (Fmt->cfFormat == Type) { new LFileName(Med, Files[Cur++]); } else if (Fmt->cfFormat == CF_HDROP) { GDragDropSource Src; GDragData Data; GMouse m; if (!Src.CreateFileDrop(&Data, m, Files)) { LgiTrace("%s:%i - CreateFileDrop failed.\n", _FL); return E_FAIL; } GVariant &d = Data.Data[0]; Med->tymed = TYMED_HGLOBAL; Med->hGlobal = GlobalAlloc(GHND, d.Value.Binary.Length); if (Med->hGlobal == NULL) { LgiTrace("%s:%i - GlobalAlloc failed.\n", _FL); return E_FAIL; } char* data = (char*)GlobalLock(Med->hGlobal); memcpy(data, d.Value.Binary.Data, d.Value.Binary.Length); GlobalUnlock(Med->hGlobal); Med->pUnkForRelease = NULL; } else if (Fmt->cfFormat == ShellIdList) { LgiTrace("%s:%i - GetData ShellIdList not supported.\n", _FL); return E_NOTIMPL; /* LPIDA Data = NULL; ITEMIDLIST *IdList = NULL; size_t Size = sizeof(CIDA) + (Files.Length() * sizeof(UINT)) + (Files.Length() * sizeof(ITEMIDLIST)); Med->hGlobal = GlobalAlloc(GHND, Size); if (Med->hGlobal == NULL) return E_FAIL; Data = (LPIDA) GlobalLock(Med->hGlobal); Data->cidl = Files.Length(); LPITEMIDLIST *Parent = (LPITEMIDLIST) (Data + 1); Data->aoffset[0] = (char*)Parent - (char*)Data; GPointer p; p.vp = Parent + 1; for (unsigned i=0; iaoffset[i+1] } GlobalUnlock(Med->hGlobal); Med->pUnkForRelease = NULL; */ } else { LgiTrace("%s:%i - GetData(%s) not supported.\n", _FL, sFmt.Get()); return DV_E_FORMATETC; } LgiTrace("%s:%i - GetData(%s) OK.\n", _FL, sFmt.Get()); return S_OK; } HRESULT STDMETHODCALLTYPE GetDataHere(FORMATETC *pformatetc, STGMEDIUM *pmedium) { LgiTrace("%s:%i - GetDataHere not impl\n", _FL); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE QueryGetData(FORMATETC *Fmt) { if (Fmt->cfFormat == Type || Fmt->cfFormat == CF_HDROP) return S_OK; GString sFmt = GClipBoard::FmtToStr(Fmt->cfFormat); LgiTrace("%s:%i - QueryGetData(%s) not supported.\n", _FL, sFmt.Get()); return DV_E_FORMATETC; } HRESULT STDMETHODCALLTYPE GetCanonicalFormatEtc(FORMATETC *Fmt, FORMATETC *pformatetcOut) { LgiTrace("%s:%i - GetCanonicalFormatEtc not impl\n", _FL); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE SetData(FORMATETC *Fmt, STGMEDIUM *pmedium, BOOL fRelease) { GString sFmt = GClipBoard::FmtToStr(Fmt->cfFormat); LgiTrace("%s:%i - SetData(%s)\n", _FL, sFmt.Get()); return S_OK; } HRESULT STDMETHODCALLTYPE EnumFormatEtc(DWORD dir, IEnumFORMATETC **enumFmt) { if (dir == DATADIR_GET) { if (*enumFmt = new LFileEnum(Type)) (*enumFmt)->AddRef(); LgiTrace("%s:%i - Returning LFileEnum obj.\n", _FL); return S_OK; } else if (dir == DATADIR_SET) { } LgiTrace("%s:%i - EnumFormatEtc error\n", _FL); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection) { LgiTrace("%s:%i - DAdvise not impl\n", _FL); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE DUnadvise(DWORD dwConnection) { LgiTrace("%s:%i - DUnadvise not impl\n", _FL); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE EnumDAdvise(IEnumSTATDATA **ppenumAdvise) { LgiTrace("%s:%i - EnumDAdvise not impl\n", _FL); return E_NOTIMPL; } }; #endif GString::Array GClipBoard::Files() { GString::Array f; if (Open) { CloseClipboard(); Open = FALSE; } LPDATAOBJECT pObj = NULL; auto r = OleGetClipboard(&pObj); if (SUCCEEDED(r)) { GArray Fmts; IEnumFORMATETC *pEnum = NULL; r = pObj->EnumFormatEtc(DATADIR_GET, &pEnum); if (SUCCEEDED(r)) { FORMATETC Fmt; r = pEnum->Next(1, &Fmt, NULL); while (r == S_OK) { Fmts.Add(Fmt.cfFormat); LgiTrace("Got format: 0x%x = %s\n", Fmt.cfFormat, FmtToStr(Fmt.cfFormat).Get()); if (Fmt.cfFormat == CF_HDROP) { STGMEDIUM Med; auto Res = pObj->GetData(&Fmt, &Med); if (Res == S_OK) { switch (Med.tymed) { case TYMED_HGLOBAL: { auto Sz = GlobalSize(Med.hGlobal); DROPFILES *p = (DROPFILES*)GlobalLock(Med.hGlobal); if (p) { GPointer End; End.c = (char*)p + Sz; if (p->fWide) { wchar_t *w = (wchar_t*) ((char*)p + p->pFiles); while (w < End.w && *w) { f.Add(w); w += Strlen(w) + 1; } } else { char *n = (char*)p + p->pFiles; while (n < End.c && *n) { GAutoString u(LgiFromNativeCp(n)); f.Add(u.Get()); n += Strlen(n) + 1; } } } /* int Count = DragQueryFileW(hDrop, -1, NULL, 0); for (int i=0; i 0) { FileNames.Add(WideToUtf8(FileName)); } } */ GlobalUnlock(Med.hGlobal); break; } } } } r = pEnum->Next(1, &Fmt, NULL); } pEnum->Release(); } pObj->Release(); } else { GArray Fmts; if (EnumFormats(Fmts)) { for (auto f : Fmts) { auto s = FmtToStr(f); LgiTrace("ClipFmt: %s\n", s.Get()); } } } return f; } bool GClipBoard::Files(GString::Array &Paths, bool AutoEmpty) { GDragDropSource Src; GDragData Output; GMouse m; if (Owner) Owner->GetMouse(m, true); if (!Src.CreateFileDrop(&Output, m, Paths)) return false; GVariant &v = Output.Data[0]; if (v.Type != GV_BINARY) return false; HGLOBAL hMem = GlobalAlloc(GMEM_ZEROINIT|GMEM_MOVEABLE|GMEM_DDESHARE, v.Value.Binary.Length); auto *p = GlobalLock(hMem); CopyMemory(p, v.Value.Binary.Data, v.Value.Binary.Length); GlobalUnlock(hMem); OpenClipboard(NULL); if (AutoEmpty) EmptyClipboard(); auto r = SetClipboardData(CF_HDROP, hMem); CloseClipboard(); return r != NULL; } /////////////////////////////////////////////////////////////////////////////////////////////// GString GClipBoard::FmtToStr(FormatType Fmt) { TCHAR n[256] = {0}; int r = GetClipboardFormatName(Fmt, n, CountOf(n)); if (!r) { switch (Fmt) { case CF_TEXT: return "CF_TEXT"; case CF_BITMAP: return "CF_BITMAP"; case CF_HDROP: return "CF_HDROP"; case CF_UNICODETEXT: return "CF_UNICODETEXT"; default: LgiAssert(!"Not impl."); break; } } return n; } GClipBoard::FormatType GClipBoard::StrToFmt(GString Fmt) { return RegisterClipboardFormatA(Fmt); } /////////////////////////////////////////////////////////////////////////////////////////////// GClipBoard::GClipBoard(GView *o) { d = new GClipBoardPriv; Open = false; Owner = o; if (Owner) Open = OpenClipboard(Owner->Handle()) != 0; } GClipBoard::~GClipBoard() { if (Open) { CloseClipboard(); Open = FALSE; } DeleteObj(d); } GClipBoard &GClipBoard::operator =(GClipBoard &c) { LgiAssert(0); return *this; } bool GClipBoard::EnumFormats(GArray &Formats) { UINT Idx = 0; UINT Fmt; while (Fmt = EnumClipboardFormats(Idx)) { Formats.Add(Fmt); Idx++; } return Formats.Length() > 0; } bool GClipBoard::Empty() { d->Utf8.Reset(); d->Wide.Reset(); return EmptyClipboard() != 0; } // Text bool GClipBoard::Text(char *Str, bool AutoEmpty) { bool Status = false; if (Str) { GAutoString Native(LgiToNativeCp(Str)); if (Native) { Status = Binary(CF_TEXT, (uchar*)Native.Get(), strlen(Native)+1, AutoEmpty); } else LgiTrace("%s:%i - Conversion to native cs failed.\n", _FL); } else LgiTrace("%s:%i - No text.\n", _FL); return Status; } char *GClipBoard::Text() { ssize_t Len = 0; - GAutoPtr Str; + GAutoPtr Str; if (Binary(CF_TEXT, Str, &Len)) { d->Utf8.Reset(LgiFromNativeCp((char*)Str.Get())); return d->Utf8; } return NULL; } bool GClipBoard::TextW(char16 *Str, bool AutoEmpty) { if (Str) { auto Len = StrlenW(Str); return Binary(CF_UNICODETEXT, (uchar*) Str, (Len+1) * sizeof(ushort), AutoEmpty); } return false; } char16 *GClipBoard::TextW() { ssize_t Len = 0; - GAutoPtr Str; + GAutoPtr Str; if (Binary(CF_UNICODETEXT, Str, &Len)) { d->Wide.Reset(NewStrW((char16*) Str.Get(), Len / 2)); return d->Wide; } return NULL; } #ifndef CF_HTML #define CF_HTML RegisterClipboardFormatA("HTML Format") #endif bool GClipBoard::Html(const char *Doc, bool AutoEmpty) { if (!Doc) return false; GString s; s.Printf("Version:0.9\n" "StartHTML:000000\n" "EndHTML:000000\n" ""); auto Start = s.Length(); s += Doc; auto End = s.Length(); auto p = s.Split("000000", 2); if (p.Length() != 3) return false; GString n; n.Printf("%06i", (int)Start); s = p[0] + n; n.Printf("%06i", (int)End); s += p[1] + n + p[2]; auto Len = Strlen(Doc); return Binary(CF_HTML, (uchar*) s.Get(), s.Length(), AutoEmpty); } GString GClipBoard::Html() { - GAutoPtr Buf; + GAutoPtr Buf; ssize_t Len; if (!Binary(CF_HTML, Buf, &Len)) return NULL; GString Txt((char*)Buf.Get(), Len); auto Ln = Txt.Split("\n", 20); ssize_t Start = -1, End = -1; for (auto l : Ln) { auto p = l.Strip().Split(":", 1); if (p.Length() == 0) ; else if (p[0].Equals("StartHTML")) Start = (ssize_t)p[1].Int(); else if (p[0].Equals("EndHTML")) End = (ssize_t)p[1].Int(); } if (Start <= 0 || End <= 0) return false; return Txt(Start, End).Strip(); } // Bitmap bool GClipBoard::Bitmap(GSurface *pDC, bool AutoEmpty) { bool Status = FALSE; Empty(); if (pDC) { int Bytes = BMPWIDTH(pDC->X() * pDC->GetBits()); int Colours = (pDC->GetBits()>8) ? ((pDC->GetBits() != 24) ? 3 : 0) : 1 << pDC->GetBits(); int HeaderSize = sizeof(BITMAPINFOHEADER); int PaletteSize = Colours * sizeof(RGBQUAD); int MapSize = Bytes * pDC->Y(); int TotalSize = HeaderSize + PaletteSize + MapSize; HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, TotalSize); if (hMem) { BITMAPINFO *Info = (BITMAPINFO*) GlobalLock(hMem); if (Info) { // Header Info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); Info->bmiHeader.biWidth = pDC->X(); Info->bmiHeader.biHeight = pDC->Y(); Info->bmiHeader.biPlanes = 1; Info->bmiHeader.biBitCount = pDC->GetBits(); if (pDC->GetBits() == 16 || pDC->GetBits() == 32) { Info->bmiHeader.biCompression = BI_BITFIELDS; } else { Info->bmiHeader.biCompression = BI_RGB; } Info->bmiHeader.biSizeImage = MapSize; Info->bmiHeader.biXPelsPerMeter = 0; Info->bmiHeader.biYPelsPerMeter = 0; Info->bmiHeader.biClrUsed = 0; Info->bmiHeader.biClrImportant = 0; if (pDC->GetBits() <= 8) { // Palette GPalette *Pal = pDC->Palette(); RGBQUAD *Rgb = Info->bmiColors; if (Pal) { GdcRGB *p = (*Pal)[0]; if (p) { for (int i=0; irgbRed = p->r; Rgb->rgbGreen = p->g; Rgb->rgbBlue = p->b; Rgb->rgbReserved = p->a; } } } else { memset(Rgb, 0, Colours * sizeof(RGBQUAD)); } } else { int *Primaries = (int*) Info->bmiColors; switch (pDC->GetBits()) { case 16: { Primaries[0] = Rgb16(255, 0, 0); Primaries[1] = Rgb16(0, 255, 0); Primaries[2] = Rgb16(0, 0, 255); break; } case 32: { Primaries[0] = Rgba32(255, 0, 0, 0); Primaries[1] = Rgba32(0, 255, 0, 0); Primaries[2] = Rgba32(0, 0, 255, 0); break; } } } // Bits uchar *Dest = ((uchar*)Info) + HeaderSize + PaletteSize; for (int y=pDC->Y()-1; y>=0; y--) { uchar *s = (*pDC)[y]; if (s) { memcpy(Dest, s, Bytes); } Dest += Bytes; } #if 0 GFile f; if (f.Open("c:\\tmp\\out.bmp", O_WRITE)) { f.SetSize(0); f.Write(Info, TotalSize); f.Close(); } #endif Status = SetClipboardData(CF_DIB, hMem) != 0; } else { GlobalFree(hMem); } } } return Status; } GSurface *GClipBoard::ConvertFromPtr(void *Ptr) { GSurface *pDC = NULL; if (Ptr) { BITMAPINFO *Info = (BITMAPINFO*) Ptr; if (Info) { pDC = new GMemDC; if (pDC && (Info->bmiHeader.biCompression == BI_RGB || Info->bmiHeader.biCompression == BI_BITFIELDS)) { if (pDC->Create(Info->bmiHeader.biWidth, Info->bmiHeader.biHeight, GBitsToColourSpace(max(Info->bmiHeader.biPlanes * Info->bmiHeader.biBitCount, 8)))) { int Colours = 0; char *Source = (char*) Info->bmiColors; // do palette if (pDC->GetBits() <= 8) { if (Info->bmiHeader.biClrUsed > 0) { Colours = Info->bmiHeader.biClrUsed; } else { Colours = 1 << pDC->GetBits(); } GPalette *Pal = new GPalette(NULL, Colours); if (Pal) { GdcRGB *d = (*Pal)[0]; RGBQUAD *s = (RGBQUAD*) Source; if (d) { for (int i=0; ir = s->rgbRed; d->g = s->rgbGreen; d->b = s->rgbBlue; d->a = s->rgbReserved; } } Source = (char*) s; pDC->Palette(Pal); } } if (Info->bmiHeader.biCompression == BI_BITFIELDS) { Source += sizeof(DWORD) * 3; } // do pixels int Bytes = BMPWIDTH(pDC->X() * pDC->GetBits()); for (int y=pDC->Y()-1; y>=0; y--) { uchar *d = (*pDC)[y]; if (d) { memcpy(d, Source, Bytes); } Source += Bytes; } } } } } return pDC; } GSurface *GClipBoard::Bitmap() { HGLOBAL hMem = GetClipboardData(CF_DIB); void *Ptr = GlobalLock(hMem); GSurface *pDC = 0; if (Ptr) { #if 0 SIZE_T TotalSize = GlobalSize(hMem); GFile f; if (f.Open("c:\\tmp\\in.bmp", O_WRITE)) { f.SetSize(0); f.Write(Ptr, TotalSize); f.Close(); } #endif pDC = ConvertFromPtr(Ptr); GlobalUnlock(hMem); } return pDC; } bool GClipBoard::Binary(FormatType Format, uchar *Ptr, ssize_t Len, bool AutoEmpty) { bool Status = FALSE; if (AutoEmpty) { Empty(); } if (Ptr && Len > 0) { HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, Len); if (hMem) { char *Data = (char*) GlobalLock(hMem); if (Data) { memcpy(Data, Ptr, Len); GlobalUnlock(hMem); Status = SetClipboardData(Format, hMem) != 0; if (!Status) LgiTrace("%s:%i - SetClipboardData failed.\n", _FL); } else { GlobalFree(hMem); } } else LgiTrace("%s:%i - Alloc failed.\n", _FL); } else LgiTrace("%s:%i - No data to set.\n", _FL); return Status; } -bool GClipBoard::Binary(FormatType Format, GAutoPtr &Ptr, ssize_t *Length) +bool GClipBoard::Binary(FormatType Format, GAutoPtr &Ptr, ssize_t *Length) { bool Status = false; HGLOBAL hMem = GetClipboardData(Format); if (hMem) { auto Len = GlobalSize(hMem); if (Length) *Length = Len; uchar *Data = (uchar*) GlobalLock(hMem); if (Data) { if (Ptr.Reset(new uchar[Len+sizeof(char16)])) { memcpy(Ptr, Data, Len); // String termination memset(Ptr + Len, 0, sizeof(char16)); Status = true; } } GlobalUnlock(hMem); } return Status; } diff --git a/src/win32/Lgi/GGeneral.cpp b/src/win32/Lgi/GGeneral.cpp --- a/src/win32/Lgi/GGeneral.cpp +++ b/src/win32/Lgi/GGeneral.cpp @@ -1,1206 +1,1206 @@ #define _WIN32_WINNT 0x500 // Win32 Implementation of General LGI functions #include #include #include #include #include "Lgi.h" #include "GRegKey.h" //////////////////////////////////////////////////////////////// // Local helper functions bool LgiCheckFile(char *Path, int PathSize) { if (Path) { if (FileExists(Path)) { // file is there return true; } else { // shortcut? char Link[MAX_PATH]; sprintf_s(Link, sizeof(Link), "%s.lnk", Path); // resolve shortcut if (FileExists(Link) && ResolveShortcut(Link, Link, sizeof(Link))) { // check destination of link if (FileExists(Link)) { strcpy_s(Path, PathSize, Link); return true; } } } } return false; } //////////////////////////////////////////////////////////////// // Implementations void LgiSleep(DWORD i) { ::Sleep(i); } GString LCurrentUserName() { TCHAR username[256]; DWORD username_len = sizeof(username); GetUserName(username, &username_len); return username; } bool LgiGetMimeTypeExtensions(const char *Mime, GArray &Ext) { auto Start = Ext.Length(); char *e; GRegKey t(false, "HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\MIME\\Database\\Content Type\\%s", Mime); if (t.IsOk() && (e = t.GetStr("Extension"))) { if (*e == '.') e++; Ext.Add(e); } else { #define HardCodeExtention(mime, Ext1, Ext2) \ else if (!stricmp(Mime, mime)) \ { if (Ext1) Ext.Add(Ext1); \ if (Ext2) Ext.Add(Ext2); } if (!Mime); HardCodeExtention("text/calendar", "ics", 0) HardCodeExtention("text/x-vcard", "vcf", 0) HardCodeExtention("text/mbox", "mbx", "mbox") HardCodeExtention("text/html", "html", 0) HardCodeExtention("text/plain", "txt", 0) HardCodeExtention("message/rfc822", "eml", 0) HardCodeExtention("audio/mpeg", "mp3", 0) HardCodeExtention("application/msword", "doc", 0) HardCodeExtention("application/pdf", "pdf", 0) } return Ext.Length() > Start; } GString LGetFileMimeType(const char *File) { if (File) { char *Dot = strrchr((char*)File, '.'); if (Dot) { bool AssertOnError = GRegKey::AssertOnError; GRegKey::AssertOnError = false; GRegKey Key(false, "HKEY_CLASSES_ROOT\\%s", Dot); if (Key.IsOk()) { char *Ct = Key.GetStr("Content Type"); if (Ct && !stricmp(Dot, ".dsw") == 0 && !stricmp(Dot, ".dsp") == 0) { return Ct; } else { // Search mime type DB. GRegKey Db(false, "HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\MIME\\Database\\Content Type"); List Sub; Db.GetKeyNames(Sub); for (char *k = Sub.First(); k; k = Sub.Next()) { GRegKey Type(false, "HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\MIME\\Database\\Content Type\\%s", k); char *Ext = Type.GetStr("Extension"); if (Ext && stricmp(Ext, Dot) == 0) { return k; } } Sub.DeleteArrays(); // This is a hack to get around file types without a MIME database entry // but do have a .ext entry. LgiGetAppsForMimeType knows about the hack too. GString MimeType; MimeType.Printf("application/%s", Dot); return MimeType; } } GRegKey::AssertOnError = AssertOnError; } // no extension? // no registry entry for file type? return "application/octet-stream"; } return GString(); } bool _GetApps_Add(GArray &Apps, char *In) { GAutoString Path; if (!In) return false; while (*In && strchr(WhiteSpace, *In)) In++; if (!*In) return false; for (char *i = In; true; i++) { if (*i == '\'' || *i == '\"') { char delim = *i++; char *end = strchr(i, delim); if (!end) end = i + strlen(i); Path.Reset(NewStr(i, end-i)); In = end + (*end != 0); break; } else if (!*i || strchr(WhiteSpace, *i)) { char old = *i; *i = 0; if (FileExists(In)) { Path.Reset(NewStr(In)); } *i = old; if (Path) { In = i + (*i != 0); break; } } if (!*i) break; } if (Path) { GStringPipe p; char *RootVar = "%SystemRoot%"; char *SysRoot = stristr(Path, RootVar); if (SysRoot) { // correct path for variables TCHAR Temp[256]; UINT Ch = GetWindowsDirectory(Temp, CountOf(Temp)); GString Tmp = Temp; if (Tmp(-1) != DIR_CHAR) Tmp += DIR_STR; p.Push(Tmp); char *End = SysRoot + strlen(RootVar); p.Push(*End == DIR_CHAR ? End + 1 : End); } else { p.Push(Path); } GAppInfo *a = new GAppInfo; if (a) { Apps[Apps.Length()] = a; a->Params.Reset(TrimStr(In)); a->Path.Reset(p.NewStr()); if (a->Path) { char e[MAX_PATH]; char *d = strrchr(a->Path, DIR_CHAR); if (d) strcpy_s(e, sizeof(e), d + 1); else strcpy_s(e, sizeof(e), a->Path); d = strchr(e, '.'); if (d) *d = 0; e[0] = toupper(e[0]); a->Name.Reset(NewStr(e)); if (ValidStr(a->Name)) { bool AllCaps = true; for (char *s=a->Name; *s; s++) { if (islower(*s)) { AllCaps = false; break; } } if (AllCaps) { Strlwr(a->Name + 1); } } } return true; } } return false; } bool LgiGetAppsForMimeType(const char *Mime, GArray &Apps, int Limit) { bool Status = false; if (Mime) { if (stricmp(Mime, "application/email") == 0) { // get email app GRegKey Key(false, "HKEY_CLASSES_ROOT\\mailto\\shell\\open\\command"); if (Key.IsOk()) { // get app path char *Str = Key.GetStr(); // if (RegQueryValueEx(hKey, 0, 0, &Type, (uchar*)Str, &StrLen) == ERROR_SUCCESS) if (Str) { Status = _GetApps_Add(Apps, Str); } } } else if (!stricmp(Mime, "application/browser")) { // get default browser char *Keys[] = { "HKCU", "HKLM" }; char Base[] = "SOFTWARE\\Clients\\StartMenuInternet"; for (int i=0; i Keys; if (Shell.GetKeyNames(Keys)) { GRegKey First(false, "HKEY_CLASSES_ROOT\\Applications\\%s\\shell\\%s\\command", Application, Keys.First()); char *Path; if (Path = First.GetStr()) { Status |= _GetApps_Add(Apps, Path); } } Keys.DeleteArrays(); } } DeleteArray(Mru); } if (!Status) { // Explorers file extensions GRegKey FileExt(false, "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\%s", Ext); char *Application; if (Application = FileExt.GetStr("Application")) { GRegKey App(false, "HKEY_CLASSES_ROOT\\Applications\\%s\\shell\\open\\command", Application); char *Path; if (Path = App.GetStr()) { Status = _GetApps_Add(Apps, Path); } } } if (!Status) { // get classes location GRegKey ExtEntry(false, "HKEY_CLASSES_ROOT\\%s", Ext); GRegKey TypeEntry(false, "HKEY_CLASSES_ROOT\\%s\\shell\\open\\command", ExtEntry.GetStr()); char *Path = TypeEntry.GetStr(); if (Path) { const char *c = Path; char *Part = LgiTokStr(c); if (Part) { char AppPath[256]; _snprintf_s(AppPath, sizeof(AppPath), "\"%s\"", Part); Status = _GetApps_Add(Apps, AppPath); DeleteArray(Part); } else { Status = _GetApps_Add(Apps, Path); } } } } } } } return Status; } GString LGetAppForMimeType(const char *Mime) { GString App; GArray Apps; if (LgiGetAppsForMimeType(Mime, Apps, 1)) App = Apps[0]->Path.Get(); Apps.DeleteObjects(); return App; } int LgiRand(int i) { return (rand() % i); } bool LgiPlaySound(const char *FileName, int Flags) { bool Status = false; HMODULE hDll = LoadLibrary(_T("winmm.dll")); if (hDll) { typedef BOOL (__stdcall *Proc_sndPlaySound)(LPCSTR pszSound, UINT fuSound); Proc_sndPlaySound psndPlaySound = (Proc_sndPlaySound)GetProcAddress(hDll, "sndPlaySoundA"); if (psndPlaySound) { if (LgiGetOs() == LGI_OS_WIN9X) { // async broken on 98? Flags = 0; } Status = psndPlaySound(FileName, Flags) != 0; } FreeLibrary(hDll); } return Status; } static char *LgiFindArgsStart(char *File) { for (char *a = File; *a; a++) { if (*a == '\'' || *a == '\"') { char delim = *a++; char *e = strchr(a, delim); if (e) a = e; else return 0; } else if (strchr(" \t\r\n", *a)) { return a; } } return 0; } #include -GAutoString LgiErrorCodeToString(uint32 ErrorCode) +GAutoString LgiErrorCodeToString(uint32_t ErrorCode) { GAutoString Str; HMODULE hModule = NULL; LPSTR MessageBuffer; DWORD dwBufferLength; DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM ; if (ErrorCode >= NERR_BASE && ErrorCode <= MAX_NERR) { hModule = LoadLibraryEx( TEXT("netmsg.dll"), NULL, LOAD_LIBRARY_AS_DATAFILE); if (hModule != NULL) dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE; } if (dwBufferLength = FormatMessageA(dwFormatFlags, hModule, // module to get message from (NULL == system) ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language (LPSTR) &MessageBuffer, 0, NULL)) { // DWORD dwBytesWritten; Str.Reset(TrimStr(NewStr(MessageBuffer, dwBufferLength))); LocalFree(MessageBuffer); } if (hModule != NULL) FreeLibrary(hModule); return Str; } bool LgiExecute(const char *File, const char *Arguments, const char *Dir, GAutoString *ErrorMsg) { int Error = 0; HINSTANCE Status = NULL; if (!File) return false; uint64 Now = LgiCurrentTime(); if (LgiGetOs() == LGI_OS_WIN9X) { GAutoString f(LgiToNativeCp(File)); GAutoString a(LgiToNativeCp(Arguments)); GAutoString d(LgiToNativeCp(Dir)); if (f) { Status = ShellExecuteA(NULL, "open", f, a, d, 5); if ((size_t)Status <= 32) Error = GetLastError(); } } else { GAutoWString f(Utf8ToWide(File)); GAutoWString a(Utf8ToWide(Arguments)); GAutoWString d(Utf8ToWide(Dir)); if (f) { Status = ShellExecuteW(NULL, L"open", f, a, d, 5); if ((size_t)Status <= 32) Error = GetLastError(); } } #ifdef _DEBUG if ((size_t)Status <= 32) LgiTrace("ShellExecuteW failed with %p (LastErr=0x%x)\n", Status, Error); if (LgiCurrentTime() - Now > 1000) LgiTrace("ShellExecuteW took %I64i\n", LgiCurrentTime() - Now); #endif if (ErrorMsg) *ErrorMsg = LgiErrorCodeToString(Error); return (size_t)Status > 32; } //////////////////////////////////////////////////////////////////////////////////// HKEY GetRootKey(char *s) { HKEY Root = 0; #define TestKey(Name) \ if (strncmp(s, #Name, strlen(#Name)) == 0) \ { \ Root = Name; \ } TestKey(HKEY_CLASSES_ROOT) else TestKey(HKEY_CURRENT_CONFIG) else TestKey(HKEY_CURRENT_USER) else TestKey(HKEY_LOCAL_MACHINE) else TestKey(HKEY_USERS) #undef TestKey return Root; } bool GRegKey::AssertOnError = true; GRegKey::GRegKey(bool WriteAccess, char *Key, ...) { char Buffer[1025]; k = 0; s[0] = 0; Root = (HKEY)-1; va_list Arg; va_start(Arg, Key); vsprintf_s(Buffer, Key, Arg); va_end(Arg); KeyName = Buffer; if (KeyName) { size_t Len = 0; char *SubKey = 0; #define TestKey(Long, Short) \ if (!strnicmp(KeyName, #Long, Len = strlen(#Long)) || \ !strnicmp(KeyName, #Short, Len = strlen(#Short))) \ { \ Root = Long; \ SubKey = KeyName.Get()[Len] ? KeyName.Get() + Len + 1 : 0; \ } TestKey(HKEY_CLASSES_ROOT, HKCR) else TestKey(HKEY_CURRENT_CONFIG, HKCC) else TestKey(HKEY_CURRENT_USER, HKCU) else TestKey(HKEY_LOCAL_MACHINE, HKLM) else TestKey(HKEY_USERS, HKU) else return; LONG ret = RegOpenKeyExA(Root, SubKey, 0, WriteAccess ? KEY_ALL_ACCESS : KEY_READ, &k); if (ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND) { DWORD err = GetLastError(); if (AssertOnError) LgiAssert(!"RegOpenKeyEx failed"); } } } GRegKey::~GRegKey() { if (k) RegCloseKey(k); } bool GRegKey::IsOk() { return k != NULL; } bool GRegKey::Create() { bool Status = false; if (!k && KeyName) { char *Sub = strchr(KeyName, '\\'); if (Sub) { LONG Ret = RegCreateKeyA(Root, Sub+1, &k); if (Ret == ERROR_SUCCESS) { Status = IsOk(); } else { DWORD err = GetLastError(); if (AssertOnError) LgiAssert(!"RegCreateKey failed"); } } } return Status; } char *GRegKey::Name() { return KeyName; } bool GRegKey::DeleteValue(char *Name) { if (k) { if (RegDeleteValueA(k, Name) == ERROR_SUCCESS) { return true; } else { DWORD Err = GetLastError(); LgiAssert(!"RegDeleteValue failed"); } } return false; } bool GRegKey::DeleteKey() { bool Status = false; if (k) { char *n = strchr(KeyName, '\\'); if (n++) { RegCloseKey(k); k = 0; HKEY Root = GetRootKey(KeyName); int Ret = RegDeleteKeyA(Root, n); Status = Ret == ERROR_SUCCESS; if (!Status) { if (AssertOnError) LgiAssert(!"RegDeleteKey failed."); } KeyName.Empty(); } } return false; } char *GRegKey::GetStr(const char *Name) { if (!k) { LgiAssert(!"No key to read from."); return NULL; } DWORD Size = sizeof(s), Type; LONG Ret = RegQueryValueExA(k, Name, 0, &Type, (uchar*)s, &Size); if (Ret != ERROR_SUCCESS) { if (AssertOnError) LgiAssert(!"RegQueryValueEx failed."); return NULL; } return s; } bool GRegKey::GetStr(const char *Name, GString &Str) { if (!k) { if (AssertOnError) LgiAssert(!"No key to read from."); return false; } DWORD Size = 0, Type; LONG Ret = RegQueryValueExA(k, Name, 0, &Type, NULL, &Size); if (Ret != ERROR_SUCCESS) goto OnError; { GString Tmp((char*)NULL, Size); Ret = RegQueryValueExA(k, Name, 0, &Type, (LPBYTE)Tmp.Get(), &Size); if (Ret != ERROR_SUCCESS) goto OnError; Str = Tmp; return true; } OnError: if (AssertOnError) LgiAssert(!"RegQueryValueEx failed."); return false; } bool GRegKey::SetStr(const char *Name, const char *Value) { if (!k) { LgiAssert(!"No key open."); return false; } LONG Ret = RegSetValueExA(k, Name, 0, REG_SZ, (uchar*)Value, Value ? (DWORD)strlen(Value) : 0); if (Ret != ERROR_SUCCESS) { if (AssertOnError) LgiAssert(!"RegSetValueEx failed."); return false; } return true; } -bool GRegKey::GetInt(const char *Name, uint32 &Value) +bool GRegKey::GetInt(const char *Name, uint32_t &Value) { if (!k) return false; DWORD Size = sizeof(Value), Type; LSTATUS r = RegQueryValueExA(k, Name, 0, &Type, (uchar*)&Value, &Size); return r == ERROR_SUCCESS; } -bool GRegKey::SetInt(const char *Name, uint32 Value) +bool GRegKey::SetInt(const char *Name, uint32_t Value) { if (!k) return false; LONG r = RegSetValueExA(k, Name, 0, REG_DWORD, (uchar*)&Value, sizeof(Value)); return r == ERROR_SUCCESS; } bool GRegKey::GetBinary(char *Name, void *&Ptr, int &Len) { DWORD Size = 0, Type; if (k && RegQueryValueExA(k, Name, 0, &Type, 0, &Size) == ERROR_SUCCESS) { Len = Size; Ptr = new uchar[Len]; return RegQueryValueExA(k, Name, 0, &Type, (uchar*)Ptr, &Size) == ERROR_SUCCESS; } return false; } bool GRegKey::SetBinary(char *Name, void *Ptr, int Len) { return false; } bool GRegKey::GetKeyNames(List &n) { FILETIME t; TCHAR Buf[256]; DWORD Size = CountOf(Buf), i = 0; while (RegEnumKeyEx(k, i++, Buf, &Size, 0, 0, 0, &t) == ERROR_SUCCESS) { n.Insert(WideToUtf8(Buf)); Size = sizeof(Buf); } return n.First() != 0; } bool GRegKey::GetValueNames(List &n) { TCHAR Buf[256]; DWORD Type, Size = CountOf(Buf), i = 0; while (RegEnumValue(k, i++, Buf, &Size, 0, &Type, 0, 0) == ERROR_SUCCESS) { n.Insert(WideToUtf8(Buf)); Size = sizeof(Buf); } return n.First() != 0; } ////////////////////////////////////////////////////////////////////////////////////// GString WinGetSpecialFolderPath(int Id) { GLibrary Shell("Shell32"); GString s; char16 wp[MAX_PATH] = { 0 }; pSHGetSpecialFolderPathW w = (pSHGetSpecialFolderPathW) Shell.GetAddress("SHGetSpecialFolderPathW"); if (w) { BOOL result = w(0, wp, Id, false); if (result && ValidStrW(wp)) { GAutoString Tmp(WideToUtf8(wp)); s = Tmp; } else { DWORD e = GetLastError(); LgiAssert(!"Error getting system folder."); } } return s; } ////////////////////////////////////////////////////////////////////// #ifndef LGI_STATIC int LgiAssertDlg(GString Msg) { GAlert a(0, "Assert Failed", Msg, "Abort", "Debug", "Ignore"); a.SetAppModal(); return a.DoModal(); } #endif void _lgi_assert(bool b, const char *test, const char *file, int line) { static bool Asserting = false; if (!b) { #ifdef LGI_STATIC assert(b); #else if (Asserting || !LgiApp || !SysFont) { // Woah boy... assert(0); } else { Asserting = true; printf("%s:%i - Assert failed:\n%s\n", file, line, test); #ifdef _DEBUG GString Msg; Msg.Printf("Assert failed, file: %s, line: %i\n%s", file, line, test); int Result = 0; if (LgiApp->InThread()) { // We are in the GUI thread, show the dialog inline Result = LgiAssertDlg(Msg); } /* This could not work if the app is locked up somewhere... else if (LgiApp->AppWnd) { // Ask the GUI thread to show the dialog LgiApp->AppWnd->PostEvent(M_ASSERT_UI, (GMessage::Param)&Result, (GMessage::Param)&Msg); while (!Result) LgiSleep(10); } */ else { // Fall back to windows UI int r = MessageBoxA(LgiApp->AppWnd ? LgiApp->AppWnd->Handle() : NULL, Msg, "Lgi.Assert", MB_ABORTRETRYIGNORE); if (r == IDABORT) Result = 1; else if (r == IDRETRY) Result = 2; else Result = 3; } switch (Result) { case 1: { exit(-1); break; } case 2: { // Bring up the debugger... #if defined(_WIN64) || !defined(_MSC_VER) assert(0); #else _asm int 3 #endif break; } default: case 3: { break; } } #endif Asserting = false; } #endif } } ////////////////////////////////////////////////////////////////////// // The following code is from: // Web: http://www.codeproject.com/Articles/28071/Toggle-hardware-data-read-execute-breakpoints-prog // License: http://www.codeproject.com/info/cpol10.aspx struct HWBRK { void *a; HANDLE hT; HWBRK_TYPE Type; HWBRK_SIZE Size; HANDLE hEv; int iReg; int Opr; bool SUCC; HWBRK() { Opr = 0; a = 0; hT = 0; hEv = 0; iReg = 0; SUCC = false; } }; static void SetBits(DWORD_PTR& dw, int lowBit, int bits, int newValue) { DWORD_PTR mask = (1 << bits) - 1; dw = (dw & ~(mask << lowBit)) | (newValue << lowBit); } static DWORD WINAPI BreakpointThread(LPVOID lpParameter) { HWBRK* h = (HWBRK*)lpParameter; int j = 0; int y = 0; j = SuspendThread(h->hT); y = GetLastError(); CONTEXT ct = {0}; ct.ContextFlags = CONTEXT_DEBUG_REGISTERS; j = GetThreadContext(h->hT,&ct); y = GetLastError(); int FlagBit = 0; bool Dr0Busy = false; bool Dr1Busy = false; bool Dr2Busy = false; bool Dr3Busy = false; if (ct.Dr7 & 1) Dr0Busy = true; if (ct.Dr7 & 4) Dr1Busy = true; if (ct.Dr7 & 16) Dr2Busy = true; if (ct.Dr7 & 64) Dr3Busy = true; if (h->Opr == 1) { // Remove if (h->iReg == 0) { FlagBit = 0; ct.Dr0 = 0; Dr0Busy = false; } if (h->iReg == 1) { FlagBit = 2; ct.Dr1 = 0; Dr1Busy = false; } if (h->iReg == 2) { FlagBit = 4; ct.Dr2 = 0; Dr2Busy = false; } if (h->iReg == 3) { FlagBit = 6; ct.Dr3 = 0; Dr3Busy = false; } ct.Dr7 &= ~(1 << FlagBit); } else { if (!Dr0Busy) { h->iReg = 0; ct.Dr0 = (DWORD_PTR)h->a; Dr0Busy = true; } else if (!Dr1Busy) { h->iReg = 1; ct.Dr1 = (DWORD_PTR)h->a; Dr1Busy = true; } else if (!Dr2Busy) { h->iReg = 2; ct.Dr2 = (DWORD_PTR)h->a; Dr2Busy = true; } else if (!Dr3Busy) { h->iReg = 3; ct.Dr3 = (DWORD_PTR)h->a; Dr3Busy = true; } else { h->SUCC = false; j = ResumeThread(h->hT); y = GetLastError(); SetEvent(h->hEv); return 0; } ct.Dr6 = 0; int st = 0; if (h->Type == HWBRK_TYPE_CODE) st = 0; if (h->Type == HWBRK_TYPE_READWRITE) st = 3; if (h->Type == HWBRK_TYPE_WRITE) st = 1; int le = 0; if (h->Size == HWBRK_SIZE_1) le = 0; if (h->Size == HWBRK_SIZE_2) le = 1; if (h->Size == HWBRK_SIZE_4) le = 3; if (h->Size == HWBRK_SIZE_8) le = 2; SetBits(ct.Dr7, 16 + h->iReg*4, 2, st); SetBits(ct.Dr7, 18 + h->iReg*4, 2, le); SetBits(ct.Dr7, h->iReg*2,1,1); } ct.ContextFlags = CONTEXT_DEBUG_REGISTERS; j = SetThreadContext(h->hT,&ct); y = GetLastError(); ct.ContextFlags = CONTEXT_DEBUG_REGISTERS; j = GetThreadContext(h->hT,&ct); y = GetLastError(); j = ResumeThread(h->hT); y = GetLastError(); h->SUCC = true; SetEvent(h->hEv); return 0; } HANDLE SetHardwareBreakpoint(HANDLE hThread, HWBRK_TYPE Type, HWBRK_SIZE Size, void *s) { HWBRK *h = new HWBRK; h->a = s; h->Size = Size; h->Type = Type; h->hT = hThread; if (hThread == GetCurrentThread()) { DWORD pid = GetCurrentThreadId(); h->hT = OpenThread(THREAD_ALL_ACCESS,0,pid); } h->hEv = CreateEvent(0, 0, 0, 0); h->Opr = 0; // Set Break HANDLE hY = CreateThread(0, 0, BreakpointThread, (LPVOID)h, 0, 0); WaitForSingleObject(h->hEv, INFINITE); CloseHandle(h->hEv); h->hEv = 0; if (hThread == GetCurrentThread()) { CloseHandle(h->hT); } h->hT = hThread; if (!h->SUCC) { delete h; return 0; } return (HANDLE)h; } bool RemoveHardwareBreakpoint(HANDLE hBrk) { HWBRK* h = (HWBRK*)hBrk; if (!h) return false; bool C = false; if (h->hT == GetCurrentThread()) { DWORD pid = GetCurrentThreadId(); h->hT = OpenThread(THREAD_ALL_ACCESS,0,pid); C = true; } h->hEv = CreateEvent(0,0,0,0); h->Opr = 1; // Remove Break HANDLE hY = CreateThread(0,0,BreakpointThread,(LPVOID)h,0,0); WaitForSingleObject(h->hEv,INFINITE); CloseHandle(h->hEv); h->hEv = 0; if (C) { CloseHandle(h->hT); } delete h; return true; } diff --git a/src/win32/Lgi/GView.cpp b/src/win32/Lgi/GView.cpp --- a/src/win32/Lgi/GView.cpp +++ b/src/win32/Lgi/GView.cpp @@ -1,2086 +1,2086 @@ /*hdr ** FILE: GView.cpp ** AUTHOR: Matthew Allen ** DATE: 23/4/98 ** DESCRIPTION: Win32 GView Implementation ** ** Copyright (C) 1998-2003, Matthew Allen ** fret@memecode.com */ #include #include #include "Lgi.h" #include "Base64.h" #include "GCom.h" #include "GDragAndDrop.h" #include "GDropFiles.h" #include "GdiLeak.h" #include "GViewPriv.h" #include "GCss.h" #include "GEdit.h" #define DEBUG_MOUSE_CLICKS 0 #define DEBUG_OVER 0 #define OLD_WM_CHAR_MODE 1 //////////////////////////////////////////////////////////////////////////////////////////////////// bool In_SetWindowPos = false; HWND GViewPrivate::hPrevCapture = 0; GViewPrivate::GViewPrivate() { SinkHnd = -1; Font = 0; FontOwnType = GV_FontPtr; CtrlId = -1; WndStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN; WndExStyle = 0; WndDlgCode = 0; TimerId = 0; DropTarget = NULL; DropSource = NULL; Parent = 0; ParentI = 0; Notify = 0; hTheme = NULL; IsThemed = true; } GViewPrivate::~GViewPrivate() { 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 (LgiGetOs() == 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); } GKey::GKey(int v, int flags) { const char *Cp = 0; vkey = v; 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)); GString Cls = ClsName; // LgiTrace("%s:%i - Error: hWnd=%p/%s, GWL_LGI_MAGIC=%i\n", _FL, hWnd, Cls.Get(), magic); return false; } Ptr = dynamic_cast((GViewI*)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 GWin32Class::Redir(HWND hWnd, UINT m, WPARAM a, LPARAM b) { if (m == WM_NCCREATE) { LPCREATESTRUCT Info = (LPCREATESTRUCT) b; GViewI *ViewI = (GViewI*) Info->lpCreateParams; if (ViewI) { GView *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); } } GViewI *Wnd = (GViewI*) #if _MSC_VER >= _MSC_VER_VS2005 GetWindowLongPtr(hWnd, GWLP_USERDATA); #else GetWindowLong(hWnd, GWL_USERDATA); #endif if (Wnd) { GMessage Msg(m, a, b); Msg.hWnd = hWnd; return Wnd->OnEvent(&Msg); } return DefWindowProcW(hWnd, m, a, b); } LRESULT CALLBACK GWin32Class::SubClassRedir(HWND hWnd, UINT m, WPARAM a, LPARAM b) { if (m == WM_NCCREATE) { LPCREATESTRUCT Info = (LPCREATESTRUCT) b; GViewI *ViewI = 0; if (Info->lpCreateParams) { if (ViewI = (GViewI*) Info->lpCreateParams) { GView *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); } GViewI *Wnd = (GViewI*) #if _MSC_VER >= _MSC_VER_VS2005 GetWindowLongPtr(hWnd, GWLP_USERDATA); #else GetWindowLong(hWnd, GWL_USERDATA); #endif if (Wnd) { GMessage Msg(m, a, b); Msg.hWnd = hWnd; GMessage::Result Status = Wnd->OnEvent(&Msg); return Status; } return DefWindowProcW(hWnd, m, a, b); } GWin32Class::GWin32Class(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; } GWin32Class::~GWin32Class() { UnregisterClassW(NameW(), LgiProcessInst()); Class.lpszClassName = NULL; } GWin32Class *GWin32Class::Create(const char *ClassName) { if (!LgiApp) return NULL; GApp::ClassContainer *Classes = LgiApp->GetClasses(); if (!Classes) return NULL; GWin32Class *c = Classes->Find(ClassName); if (!c) { c = new GWin32Class(ClassName); if (c) Classes->Add(ClassName, c); } return c; } bool GWin32Class::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 GWin32Class::Register() { bool Status = false; if (IsSystem(Name())) { ZeroObj(Class); Class.cbSize = sizeof(Class); Status = GetClassInfoExW(LgiProcessInst(), NameW(), &Class) != 0; LgiAssert(Status); } else if (!Class.lpszClassName) { Class.hInstance = LgiProcessInst(); Class.lpszClassName = NameW(); Status = RegisterClassExW(&Class) != 0; LgiAssert(Status); } return Status; } bool GWin32Class::SubClass(char *Parent) { bool Status = false; if (!Class.lpszClassName) { HBRUSH hBr = Class.hbrBackground; GAutoWString p(Utf8ToWide(Parent)); if (p) { if (GetClassInfoExW(LgiProcessInst(), p, &Class)) { ParentProc = Class.lpfnWndProc; if (hBr) { Class.hbrBackground = hBr; } Class.cbWndExtra = max(Class.cbWndExtra, GWL_EXTRA_BYTES); Class.hInstance = LgiProcessInst(); Class.lpfnWndProc = (WNDPROC) SubClassRedir; Class.lpszClassName = NameW(); Status = RegisterClassExW(&Class) != 0; LgiAssert(Status); } } } else Status = true; return Status; } LRESULT CALLBACK GWin32Class::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); } } ////////////////////////////////////////////////////////////////////////////// GViewI *GWindowFromHandle(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(LgiApp->GetInstance(), name, &cls)) { if (cls.cbWndExtra >= 8) { LgiAssert(!"Really?"); } } } } #endif if (m == LGI_GViewMagic) { return (GViewI*) #if _MSC_VER >= _MSC_VER_VS2005 GetWindowLongPtr(hWnd, GWLP_USERDATA); #else GetWindowLong(hWnd, GWL_USERDATA); #endif } } return 0; } ////////////////////////////////////////////////////////////////////////////// const char *GView::GetClass() { return "GView"; } void GView::_Delete() { if (_View && d->DropTarget) { RevokeDragDrop(_View); } GViewI *c; #ifdef _DEBUG // Sanity check.. // GArray HasView; for (c = Children.First(); c; c = Children.Next()) { // LgiAssert(!HasView.HasItem(c)); // HasView.Add(c); LgiAssert(((GViewI*)c->GetParent()) == this || c->GetParent() == 0); } #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 while (c = Children.First()) { // 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); LgiAssert(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; } GWindow *Wnd = GetWindow(); if (Wnd) Wnd->SetFocus(this, GWindow::ViewDelete); // this should only exist in an ex-GWindow, due to the way // C++ deletes objects it needs to be here. DeleteObj(_Lock); } void GView::Quit(bool DontDelete) { if (_View) { if (!DontDelete) { WndFlags |= GWF_QUIT_WND; } DestroyWindow(_View); } } -uint32 GView::GetDlgCode() +uint32_t GView::GetDlgCode() { return d->WndDlgCode; } -void GView::SetDlgCode(uint32 i) +void GView::SetDlgCode(uint32_t i) { d->WndDlgCode = i; } -uint32 GView::GetStyle() +uint32_t GView::GetStyle() { return d->WndStyle; } -void GView::SetStyle(uint32 i) +void GView::SetStyle(uint32_t i) { d->WndStyle = i; } -uint32 GView::GetExStyle() +uint32_t GView::GetExStyle() { return d->WndExStyle; } -void GView::SetExStyle(uint32 i) +void GView::SetExStyle(uint32_t i) { d->WndExStyle = i; } const char *GView::GetClassW32() { return d->WndClass; } void GView::SetClassW32(const char *c) { d->WndClass = c; } GWin32Class *GView::CreateClassW32(const char *Class, HICON Icon, int AddStyles) { if (Class) { SetClassW32(Class); } if (GetClassW32()) { GWin32Class *c = GWin32Class::Create(GetClassW32()); if (c) { if (Icon) { c->Class.hIcon = Icon; } if (AddStyles) { c->Class.style |= AddStyles; } c->Register(); return c; } } return 0; } bool GView::IsAttached() { return _View && IsWindow(_View); } bool GView::Attach(GViewI *p) { bool Status = false; SetParent(p); GView *Parent = d->GetParent(); 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 = GWin32Class::IsSystem(ClsName); GWin32Class *Cls = GWin32Class::Create(ClsName); if (Cls) Cls->Register(); else if (!IsSystemClass) return false; LgiAssert(!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); char16 *Text = GBase::NameW(); GAutoWString WCls(Utf8ToWide(ClsName)); _View = CreateWindowExW(ExStyle, WCls, Text, Style, Pos.x1, Pos.y1, Pos.X(), Pos.Y(), Parent ? Parent->Handle() : 0, NULL, LgiProcessInst(), (GViewI*) this); #if 1 // def _DEBUG if (!_View) { DWORD e = GetLastError(); LgiTrace("%s:%i - CreateWindowExW failed with 0x%x\n", _FL, e); LgiAssert(!"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); } OnAttach(); } else { // Virtual window (no HWND) Status = true; } if (Status && d->Parent) { if (!d->Parent->HasView(this)) { d->Parent->AddView(this); } d->Parent->OnChildrenChanged(this, true); } return Status; } bool GView::Detach() { bool Status = false; if (_Window) { GWindow *Wnd = dynamic_cast(_Window); if (Wnd) Wnd->SetFocus(this, GWindow::ViewDelete); _Window = NULL; } if (d->Parent) { Visible(false); 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(); LgiAssert(Status != 0); } } return Status; } GRect &GView::GetClient(bool InClientSpace) { static GRect 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.Size(_BorderSize, _BorderSize); } } if (InClientSpace) Client.Offset(-Client.x1, -Client.y1); return Client; } LgiCursor GView::GetCursor(int x, int y) { return LCUR_Normal; } #ifndef GCL_HCURSOR #define GCL_HCURSOR -12 #endif bool LgiToWindowsCursor(OsView Hnd, LgiCursor 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: { GArray Ver; int Os = LgiGetOs(&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; } void GView::PointToScreen(GdcPt2 &p) { POINT pt = {p.x, p.y}; GViewI *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; } void GView::PointToView(GdcPt2 &p) { POINT pt = {p.x, p.y}; GViewI *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; } bool GView::GetMouse(GMouse &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_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 GView::SetPos(GRect &p, bool Repaint) { bool Status = true; GRect OldPos = Pos; if (Pos != p) { Pos = p; if (_View) { HWND hOld = GetFocus(); bool WasVis = IsWindowVisible(_View) != 0; In_SetWindowPos = true; Status = SetWindowPos( _View, NULL, Pos.x1, Pos.y1, Pos.X(), Pos.Y(), // ((Repaint) ? 0 : SWP_NOREDRAW) | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER) != 0; In_SetWindowPos = false; } else if (GetParent()) { OnPosChange(); } if (Repaint) { Invalidate(); } } return Status; } bool GView::Invalidate(GRect *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 { GRect Up; GViewI *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()) { GViewI *Par = p->GetParent(); GView *VPar = Par?Par->GetGView():0; GRect w = p->GetPos(); GRect 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 -GView::TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, uint32 dwTime) +GView::TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, uint32_t dwTime) { GView *View = (GView*) idEvent; if (View) { View->OnPulse(); } } void GView::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; } } } static int ConsumeTabKey = 0; bool SysOnKey(GView *w, GMessage *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; GViewI *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 GView::DrawThemeBorder(GSurface *pDC, GRect &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] = GRect(r.x1, r.y1, r.x1 + 1, r.y2); // left clip[1] = GRect(r.x1 + 2, r.y1, r.x2 - 2, r.y1 + 1); // top clip[2] = GRect(r.x2 - 1, r.y1, r.x2, r.y2); // right clip[3] = GRect(r.x1 + 2, r.y2 - 1, r.x2 - 2, r.y2); // bottom GColour cols[4] = { GColour(255, 0, 0), GColour(0, 255, 0), GColour(0, 0, 255), GColour(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(LC_MED, 24); 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.Size(2, 2); } else { LgiWideBorder(pDC, r, Sunken() ? DefaultSunkenEdge : DefaultRaisedEdge); d->IsThemed = false; } } #else void GView::DrawThemeBorder(GSurface *pDC, GRect &r) { LgiWideBorder(pDC, r, DefaultSunkenEdge); } #endif bool IsKeyChar(GKey &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)) GMessage::Result GView::OnEvent(GMessage *Msg) { int Status = 0; if (MsgCode(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; } if (_View) { switch (Msg->m) { case WM_CTLCOLOREDIT: case WM_CTLCOLORSTATIC: { HDC hdc = (HDC)MsgA(Msg); HWND hwnd = (HWND)MsgB(Msg); GViewI *v = FindControl(hwnd); GView *gv = v ? v->GetGView() : NULL; if (gv) { int Depth = dynamic_cast(gv) ? 1 : 10; GColour Fore = gv->StyleColour(GCss::PropColor, GColour(), Depth); GColour Back = gv->StyleColour(GCss::PropBackgroundColor, GColour(), 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; } case 5700: { // I forget what this is for... break; } case WM_ERASEBKGND: { return 1; } case WM_GETFONT: { GFont *f = GetFont(); return (GMessage::Result) (f ? f->Handle() : SysFont->Handle()); break; } case WM_MENUCHAR: case WM_MEASUREITEM: { return GMenu::_OnEvent(Msg); break; } case WM_DRAWITEM: { DRAWITEMSTRUCT *di = (DRAWITEMSTRUCT*)Msg->B(); if (di) { if (di->CtlType == ODT_MENU) { return GMenu::_OnEvent(Msg); } /* else if (di->CtlType == ODT_BUTTON) { GView *b; if (CastHwnd(b, di->hwndItem) && b->GetCss()) { GScreenDC dc(di->hDC, di->hwndItem); switch (di->itemAction) { case ODA_DRAWENTIRE: { GRect c = di->rcItem; GMemDC m(c.X(), c.Y(), GdcD->GetColourSpace()); HDC hdc = m.StartDC(); m.Colour(GColour(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: { GViewI *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 short xPos = (short) LOWORD(Msg->b); // horizontal position of pointer short yPos = (short) HIWORD(Msg->b); // vertical position of pointer int nScrollLines = - _lgi_mouse_wheel_lines(); double Lines = ((double)zDelta * (double)nScrollLines) / WHEEL_DELTA; // 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 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: { GWindow *w = GetWindow(); GViewI *Ctrl = w ? w->FindControl((int)Msg->a) : 0; if (Ctrl) { return OnNotify(Ctrl, (int)Msg->b); } else { LgiTrace("Ctrl %i not found.\n", Msg->a); } break; } case M_COMMAND: { // GViewI *Ci = FindControl((HWND) Msg->b); // GView *Ctrl = Ci ? Ci->GetGView() : 0; GView *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, 0); 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); GWindow *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); } } OnCreate(); break; } case WM_SETFOCUS: { GWindow *w = GetWindow(); if (w) { w->SetFocus(this, GWindow::GainFocus); } else { // This can happen in popup sub-trees of views. Where the focus // is tracked separately from the main GWindow. OnFocus(true); Invalidate((GRect*)NULL, false, true); } break; } case WM_KILLFOCUS: { GWindow *w = GetWindow(); if (w) { w->SetFocus(this, GWindow::LoseFocus); } else { // This can happen when the GWindow is being destroyed Invalidate((GRect*)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 { GRect r; r.ZOff(Info->cx-1, Info->cy-1); r.Offset(Info->x, Info->y); if (r.Valid() && r != Pos) { Pos = r; } } } OnPosChange(); } if (!(WndFlags & GWF_DIALOG)) { goto ReturnDefaultProc; } break; } case WM_CAPTURECHANGED: { GViewI *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: { GMouse Ms; Ms.Target = this; Ms.x = (short) (Msg->b&0xFFFF); Ms.y = (short) (Msg->b>>16); Ms.Flags = 0; GViewI *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) { GMouse m = lgi_adjust_click(Ms, _Over); _Over->OnMouseExit(m); #if DEBUG_OVER LgiTrace("LoseOver=%p '%-20s'\\n", _Over, _Over->Name()); #endif } _Over = MouseOver; if (_Over) { #if DEBUG_OVER LgiTrace("GetOver=%p '%-20s'\\n", _Over, _Over->Name()); #endif GMouse m = lgi_adjust_click(Ms, _Over); _Over->OnMouseEnter(m); } } } break; } case M_MOUSEEXIT: { if (_Over) { GMouse 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 (GViewI *o = _Capturing ? _Capturing : _Over; o; o = o->GetParent()) { if (o == this) { Mine = true; break; } } } if (Mine) { if (_Capturing) { GMouse m = lgi_adjust_click(Ms, _Capturing); _Capturing->OnMouseExit(m); } else { #if DEBUG_OVER LgiTrace("LoseOver=%p '%-20s'\\n", _Over, _Over->Name()); #endif _Over->OnMouseExit(Ms); _Over = 0; } } } break; } case WM_MOUSEMOVE: { GMouse 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); GViewI *MouseOver = WindowFromPoint(Ms.x, Ms.y); if (_Over != MouseOver) { if (_Over) { #if DEBUG_OVER LgiTrace("LoseOver=%p '%-20s'\\n", _Over, _Over->Name()); #endif GMouse m = lgi_adjust_click(Ms, _Over); _Over->OnMouseExit(m); } _Over = MouseOver; if (_Over) { GMouse m = lgi_adjust_click(Ms, _Over); _Over->OnMouseEnter(m); #if DEBUG_OVER LgiTrace("GetOver=%p '%-20s'\\n", _Over, _Over->Name()); #endif } } // int CurX = Ms.x, CurY = Ms.y; LgiCursor 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; GWindow *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", LgiCurrentTime(), Hit); return Hit; } if (!(WndFlags & GWF_DIALOG)) { goto ReturnDefaultProc; } break; } case WM_LBUTTONDBLCLK: case WM_LBUTTONDOWN: case WM_LBUTTONUP: { GMouse 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 GString Msg; Msg.Printf("%s.Click", Ms.Target->GetClass()); Ms.Trace(Msg); #endif GWindow *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: { GMouse 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 GString Msg; Msg.Printf("%s.Click", Ms.Target->GetClass()); Ms.Trace(Msg); #endif GWindow *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: { GMouse 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; GWindow *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 GKey Key((int)Msg->a, (int)Msg->b); Key.Flags = KeyFlags; - Key.Data = (uint32)Msg->b; + Key.Data = (uint32_t)Msg->b; Key.Down(IsDown); Key.IsChar = false; if (Key.Ctrl()) { Key.c16 = (char16)Msg->a; } if (Key.c16 == VK_TAB && ConsumeTabKey) { ConsumeTabKey--; } else { GWindow *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: { GKey Key((int)Msg->a, (int)Msg->b); Key.Flags = _lgi_get_key_flags(); - Key.Data = (uint32)Msg->b; + Key.Data = (uint32_t)Msg->b; 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 == VK_TAB && ConsumeTabKey) { ConsumeTabKey--; } else { GWindow *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); GScreenDC Dc(hDC, _View, true); GRect p(0, 0, Dc.X()-1, Dc.Y()-1); OnNcPaint(&Dc, p); } goto ReturnDefaultProc; break; } case WM_NCCALCSIZE: { GMessage::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) { GView *Wnd; if (CastHwnd(Wnd, Hdr->hwndFrom)) Wnd->SysOnNotify(Msg->Msg(), Hdr->code); } break; } default: { if (!(WndFlags & GWF_DIALOG)) goto ReturnDefaultProc; break; } } } return 0; ReturnDefaultProc: #ifdef _DEBUG uint64 start = LgiCurrentTime(); #endif LRESULT r = DefWindowProcW(_View, Msg->m, Msg->a, Msg->b); #ifdef _DEBUG uint64 now = LgiCurrentTime(); 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; } GViewI *GView::FindControl(OsView hCtrl) { if (_View == hCtrl) { return this; } for (List::I i = Children.begin(); i.In(); i++) { GViewI *Ctrl = (*i)->FindControl(hCtrl); if (Ctrl) return Ctrl; } return 0; } diff --git a/src/win32/Widgets/GEdit_Win.cpp b/src/win32/Widgets/GEdit_Win.cpp --- a/src/win32/Widgets/GEdit_Win.cpp +++ b/src/win32/Widgets/GEdit_Win.cpp @@ -1,563 +1,563 @@ // \file // \author Matthew Allen (fret@memecode.com) // \brief Native Win32 Edit Control #include #include #include #include #include "Lgi.h" #include "GSkinEngine.h" #include "GEdit.h" GAutoWString LgiAddReturns(const char16 *n) { GAutoWString w; if (n && StrchrW(n, '\n')) { int Len = 0; const char16 *c; for (c = n; *c; c++) { if (*c == '\n') Len += 2; else if (*c != '\r') Len++; } if (w.Reset(new char16[Len+1])) { char16 *o = w; for (c = n; *c; c++) { if (*c == '\n') { *o++ = '\r'; *o++ = '\n'; } else if (*c != '\r') { *o++ = *c; } } *o = 0; LgiAssert(o - w == Len); } } return w; } /////////////////////////////////////////////////////////////////////////////////////////// class GEditPrivate { public: bool IgnoreNotify; bool InEmptyMode; GCss::ColorDef NonEmptyColor; GAutoWString EmptyText; GEditPrivate() { IgnoreNotify = true; InEmptyMode = false; } ~GEditPrivate() { } }; /////////////////////////////////////////////////////////////////////////////////////////// GEdit::GEdit(int id, int x, int y, int cx, int cy, const char *name) : GControl(LGI_EDITBOX), ResObject(Res_EditBox) { d = new GEditPrivate; GdcPt2 Size = SizeOfStr((name)?name:(char*)"A"); if (cx < 0) cx = Size.x + 6; if (cy < 0) cy = Size.y + 4; WndFlags |= GWF_SYS_BORDER; SetId(id); Sunken(true); if (name) Name(name); GRect r(x, y, x+max(cx, 10), y+max(cy, 10)); SetPos(r); SetStyle(WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | WS_TABSTOP); SetFont(SysFont); if (SubClass) SubClass->SubClass("EDIT"); } GEdit::~GEdit() { DeleteObj(d); } void GEdit::Select(int Start, int Len) { if (_View) { SendMessage(_View, EM_SETSEL, Start, Start+Len-1); } } GRange GEdit::GetSelectionRange() { GRange r; if (!_View) return r; DWORD s = 0, e = 0; SendMessage(_View, EM_GETSEL, (WPARAM)&s, (LPARAM)&e); if (s == e) return r; r.Start = s; r.Len = e - s; return r; } bool GEdit::MultiLine() { return TestFlag(GetStyle(), ES_MULTILINE); } void GEdit::MultiLine(bool m) { int Flags = ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL | ES_WANTRETURN; if (m) SetStyle(GetStyle() | Flags); else SetStyle(GetStyle() & ~Flags); } bool GEdit::Password() { return TestFlag(GetStyle(), ES_PASSWORD); } void GEdit::Password(bool m) { if (m) SetStyle(GetStyle() | ES_PASSWORD); else SetStyle(GetStyle() & ~ES_PASSWORD); } int GEdit::SysOnNotify(int Msg, int Code) { if (Msg == WM_COMMAND && Code == EN_CHANGE && !d->IgnoreNotify && _View) { if (!d->InEmptyMode) { GAutoWString w = SysName(); GBase::NameW(w); } // LgiTrace("GEdit::SysOnNotify EN_CHANGE inempty=%i, n16=%S\n", d->InEmptyMode, GBase::NameW()); SendNotify(0); } return 0; } void GEdit::Value(int64 i) { char Str[32]; sprintf_s(Str, sizeof(Str), LPrintfInt64, i); Name(Str); } int64 GEdit::Value() { char *n = Name(); return (n) ? atoi64(n) : 0; } #define EDIT_PROCESSING 1 GMessage::Result GEdit::OnEvent(GMessage *Msg) { #if EDIT_PROCESSING switch (Msg->m) { case WM_DESTROY: { Name(); _View = 0; break; } case WM_SETTEXT: { if (d->InEmptyMode && SubClass) { // LgiTrace("GEdit WM_SETTEXT - calling parent, inempty=%i\n", d->InEmptyMode); return SubClass->CallParent(Handle(), Msg->m, Msg->a, Msg->b); } else { // LgiTrace("GEdit WM_SETTEXT - dropping through to GControl, inempty=%i.\n", d->InEmptyMode); } break; } case WM_GETDLGCODE: { int Code = 0; Code = DLGC_WANTALLKEYS; return Code; } case WM_SYSKEYUP: case WM_SYSKEYDOWN: case WM_KEYUP: { if (!MultiLine() && (Msg->a == VK_UP || Msg->a == VK_DOWN)) { GView::OnEvent(Msg); return 1; } if (SysOnKey(this, Msg)) { return 0; } break; } case WM_KEYDOWN: { if (!MultiLine() && (Msg->a == VK_UP || Msg->a == VK_DOWN)) { GView::OnEvent(Msg); return 1; } if (Msg->a == VK_TAB && MultiLine()) { // this causes the multi-line Edit control to // insert tabs into the text and not move // to the next field. return 0; } if (Msg->a == VK_ESCAPE) SendNotify(GNotify_EscapeKey); if (tolower((int)Msg->a) == 'u') { if (GetKeyState(VK_CONTROL) & 0xFF00) { DWORD Start = 0, End = 0; SendMessage(_View, EM_GETSEL, (WPARAM) &Start, (LPARAM) &End); int Chars = End - Start; char16 *n = NameW(); if (n) { if (GetKeyState(VK_SHIFT) & 0xFF00) { // upper case selection for (DWORD i=Start; ia == VK_TAB || Msg->a == VK_RETURN) && !MultiLine()) { // single line edit controls make "click" // when they get a TAB char... this // avoids that. return 0; } break; } } #endif auto Status = GControl::OnEvent(Msg); #if EDIT_PROCESSING switch (MsgCode(Msg)) { case WM_CREATE: { d->IgnoreNotify = false; break; } case WM_LBUTTONDBLCLK: case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_RBUTTONDBLCLK: case WM_RBUTTONDOWN: case WM_RBUTTONUP: case WM_MBUTTONDBLCLK: case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_SETFOCUS: case WM_KILLFOCUS: case WM_MOUSEMOVE: { GView::OnEvent(Msg); break; } } #endif return Status; } void GEdit::OnCreate() { if (d->EmptyText) SysEmptyText(); } void GEdit::OnFocus(bool f) { if (d->EmptyText) SysEmptyText(); } bool GEdit::OnKey(GKey &k) { if (!k.IsChar && k.vkey == VK_RETURN) { if (k.Down()) { SendNotify(k.c16); } if (MultiLine()) { return true; } } if ( !k.IsChar && k.Ctrl() && ( tolower(k.c16) == 'x' || tolower(k.c16) == 'c' || tolower(k.c16) == 'v' ) ) { // This makes sure any menu items labeled Ctrl+x, Ctrl+c and // Ctrl+v don't fire as WELL as the built in windows clipboard // handlers. return true; } return false; } ssize_t GEdit::GetCaret(bool Cursor) { DWORD Start, End = 0; if (_View) SendMessage(_View, EM_GETSEL, (GMessage::Param)&Start, (GMessage::Param)&End); return End; } void GEdit::SetCaret(size_t Pos, bool Select, bool ForceFullUpdate) { if (_View) SendMessage(_View, EM_SETSEL, Pos, Pos); } ///////////////////////////////////////////////////////////////////////////////////////// GAutoWString GEdit::SysName() { GAutoWString a; if (_View) { int Length = GetWindowTextLengthW(_View); if (Length) { if (a.Reset(new char16[Length+1])) { a[0] = 0; int Chars = GetWindowTextW(_View, a, Length+1); if (Chars >= 0) { // Remove return characters char16 *i = a; char16 *end = a + Chars; char16 *o = a; while (i < end) { if (*i != '\r') *o++ = *i; i++; } *o = 0; } else a.Reset(); } } } return a; } bool GEdit::SysName(const char16 *n) { if (!_View) return false; GAutoWString w = LgiAddReturns(n); if (w) n = w; return SetWindowTextW(_View, n ? n : L"") != FALSE; } char *GEdit::Name() { if (Handle() && !d->InEmptyMode) { GAutoWString w = SysName(); GBase::NameW(w); } return GBase::Name(); } bool GEdit::Name(const char *n) { bool Old = d->IgnoreNotify; d->IgnoreNotify = true; GBase::Name(n); GAutoWString w = LgiAddReturns(GBase::NameW()); if (w) GBase::NameW(w); bool Status = SysEmptyText(); d->IgnoreNotify = Old; return Status; } char16 *GEdit::NameW() { if (Handle() && !d->InEmptyMode) { GAutoWString w = SysName(); GBase::NameW(w); } return GBase::NameW(); } bool GEdit::NameW(const char16 *s) { bool Old = d->IgnoreNotify; d->IgnoreNotify = true; GAutoWString w = LgiAddReturns(s); GBase::NameW(w ? w : s); bool Status = SysEmptyText(); d->IgnoreNotify = Old; return Status; } bool GEdit::SysEmptyText() { bool Status = false; bool HasFocus = Focus(); bool Empty = ValidStrW(d->EmptyText) && !ValidStrW(GBase::NameW()) && !HasFocus; // LgiTrace("GEdit::SysEmptyText Empty=%i W=%p Focus=%i\n", Empty, GBase::NameW(), HasFocus); if (Empty) { // Show empty text GColour c(LC_LOW, 24); if (!d->InEmptyMode) { d->NonEmptyColor = GetCss(true)->Color(); d->InEmptyMode = true; } GetCss()->Color(GCss::ColorDef(GCss::ColorRgb, c.c32())); bool Old = d->IgnoreNotify; d->IgnoreNotify = true; Status = SysName(d->EmptyText); d->IgnoreNotify = Old; if (_View) { DWORD Style = GetWindowLong(_View, GWL_STYLE); SetWindowLong(_View, GWL_STYLE, Style & ~ES_PASSWORD); SendMessage(_View, EM_SETPASSWORDCHAR, 0, 0); } } else { // Show normal text if (d->InEmptyMode) { GetCss()->Color(d->NonEmptyColor); d->InEmptyMode = false; } if (_View) { - uint32 Style = GetStyle(); + uint32_t Style = GetStyle(); bool HasPass = (Style & ES_PASSWORD) != 0; if (HasPass) { SetWindowLong(_View, GWL_STYLE, Style); SendMessage(_View, EM_SETPASSWORDCHAR, (WPARAM)'*', 0); } } Status = SysName(GBase::NameW()); } Invalidate(); return Status; } void GEdit::SetEmptyText(const char *EmptyText) { d->EmptyText.Reset(Utf8ToWide(EmptyText)); SysEmptyText(); }