diff --git a/Ide/src/Debugger.cpp b/Ide/src/Debugger.cpp --- a/Ide/src/Debugger.cpp +++ b/Ide/src/Debugger.cpp @@ -1,1565 +1,1580 @@ #ifdef POSIX #include #include #include #include #endif #include "lgi/common/Lgi.h" #include "lgi/common/SubProcess.h" #include "lgi/common/Token.h" #include "lgi/common/DocView.h" #include "lgi/common/StringClass.h" #include "lgi/common/LgiString.h" #include "Debugger.h" #define DEBUG_STOP_ON_GTK_ERROR 0 #define DEBUG_SHOW_GDB_IO 0 #define ECHO_GDB_OUTPUT 0 const char sPrompt[] = "(gdb) "; class Callback { public: virtual LString GetResponse(const char *c) = 0; }; class Visualizer { public: virtual ~Visualizer() {} virtual bool Match(LString s) = 0; virtual bool Transform(LString name, LString val, Callback *Cb, LVariant &Value, LString &Detail) = 0; }; class LStringVis : public Visualizer { public: bool Match(LString s) { return s == "LString"; } bool Transform(LString name, LString val, Callback *Cb, LVariant &Value, LString &Detail) { LString::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 { LString cmd; cmd.Printf("p (char*)%s.Str->Str", name.Get()); LString 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 LDebugger, public LThread, public Callback { LDebugEvents *Events = NULL; LAutoPtr Sp; LString Exe, Args, InitDir; LString ChildEnv; bool RunAsAdmin = false; bool AtPrompt = false; char Line[256], *LinePtr = NULL; int CurFrame = 0; bool SetAsmType = false; bool SetPendingOn = false; LArray BreakPoints; int BreakPointIdx = -1; int ProcessId = -1; bool SuppressNextFileLine = false; LArray Vis; LStream *Log = NULL; LMutex StateMutex; bool DebuggingProcess = false; bool Running = false; // Current location tracking LString CurFile; int CurLine = -1; LString::Array Untagged; // Parse state enum ParseType { ParseNone, ParseBreakPoint, } ParseState = ParseNone; LString::Array BreakInfo; // Various output modes. LStream *OutStream = NULL; LString::Array *OutLines = NULL; 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(LString::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' { LString::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 LogMsg(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(LString f) { + Ungrab(); + if (!f.Get() || ProcessId < 0) { // printf("Error: Param error: %s, %i (%s:%i)\n", f.Get(), ProcessId, _FL); return; } LString File, Line; LString::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); LString::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.Int() > 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); LString Bp = LString(" ").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")) { + Ungrab(); Events->OnCrash(0); } else if (*Start == '[') { if (stristr(Start, "Inferior") && stristr(Start, "exited")) { + Ungrab(); OnExit(); } else if (stristr(Start, "New Thread")) { LString s(Start, Length); LString::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); // LogMsg("Pid for Thread %i = %i\n", ThreadId, Pid); if (Pid > 0 && ProcessId < 0) // LgiTrace("Got the thread id: %i, and pid: %i\n", ThreadId, Pid); ProcessId = Pid; #else LAssert(!"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? + Ungrab(); 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", LCurrentTime(), 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 LString 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; } LString::Array a = p.Split(" ", 1); printf("Starting Debugger: %s %s\n", a[0].Get(), a[1].Get()); if (!Sp.Reset(new LSubProcess(a[0], a[1]))) return false; if (InitDir) Sp->SetInitFolder(InitDir); if (ChildEnv) { auto p = ChildEnv.Split("\n"); for (auto &v: p) { auto a = v.Strip().Split("=", 1); if (a.Length() == 2) { LogMsg("%s:%i - env %s=%s\n", _FL, a[0].Get(), a[1].Get()); Sp->SetEnvironment(a[0], a[1]); } else LogMsg("%s:%i - Wrong parts %s.", _FL, v.Get()); } } else LogMsg("%s:%i - No env.", _FL); #if DEBUG_STOP_ON_GTK_ERROR Sp->SetEnvironment("G_DEBUG", "fatal-criticals"); #endif LgiTrace("Starting gdb subprocess...\n"); if (!Sp->Start(true, true, false)) { State = ProcessError; LString ErrMsg = LErrorCodeToString(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); LogMsg("Debugger exited.\n"); return 0; } bool WaitPrompt() { if (State == Init) { uint64 Start = LCurrentTime(); while (State == Init) { uint64 Now = LCurrentTime(); if (Now - Start < 5000) { LSleep(10); } else { LgiTrace("%s:%i - WaitPrompt init wait failed.\n", _FL); return false; } } } uint64 Start = LCurrentTime(); uint64 Now = Start; while (!AtPrompt && Now - Start < 2000 && State == Looping) { Now = LCurrentTime(); LSleep(50); uint64 After = LCurrentTime(); if (After - Now > 65) { printf("Sleep took=%i\n", (int)(After - Now)); } } if (!AtPrompt) { LogMsg("Error: Not at prompt...\n"); return false; } return true; } bool Cmd(const char *c, LStream *Output = NULL, LString::Array *Arr = NULL) { if (!ValidStr(c)) { LgiTrace("%s:%i - Not a valid command.\n", _FL); LAssert(!"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 = LCurrentTime(); auto Wr = Sp->Write(str, ch); if (Wr != ch) return false; if (OutStream || OutLines) { /* uint64 Wait0 = LCurrentTime(); */ WaitPrompt(); /* uint64 Wait1 = LCurrentTime(); LgiTrace("Cmd timing "LGI_PrintfInt64" "LGI_PrintfInt64"\n", Wait0-Start, Wait1-Wait0); */ LAssert(OutStream == NULL && OutLines == NULL); } return true; } + void Ungrab() + { + #if defined(LINUX) + printf("%s:%i - XF86Ungrab...\n", _FL); + system("xdotool key XF86Ungrab"); + #else + printf("%s:%i - Impl some ungrab functionality here.\n", _FL); + #endif + } + public: Gdb(LStream *log) : LThread("Gdb"), Log(log), StateMutex("Gdb.StateMutex") { State = Init; LinePtr = Line; Vis.Add(new LStringVis); } ~Gdb() { if (State == Looping) { #if DEBUG_SESSION_LOGGING LgiTrace("Gdb::~Gdb - waiting for thread to exit...\n"); #endif State = Exiting; while (!IsExited()) { LSleep(1); } #if DEBUG_SESSION_LOGGING LgiTrace("Gdb::~Gdb - thread has exited.\n"); #endif } } bool Load(LDebugEvents *EventHandler, const char *exe, const char *args, bool runAsAdmin, const char *initDir, const char *Env) { Events = EventHandler; Exe = exe; Args = args; RunAsAdmin = runAsAdmin; ChildEnv = Env; InitDir = initDir; Running = false; Run(); return true; } bool SetCurrentThread(int ThreadId) { if (ThreadId < 1) return false; LString c; c.Printf("thread %i", ThreadId); if (!Cmd(c)) return false; return true; } bool GetThreads(LArray &Threads, int *pCurrentThread) { LString::Array t; if (!Cmd("info threads", NULL, &t)) return false; LString *Cur = NULL; for (int i=0; iGet(), l); *Cur = s; } } return true; } bool GetCallStack(LArray &Stack) { LString::Array Bt; if (!Cmd("bt", NULL, &Bt)) return false; for (int i=0; i 0) { // Append to the last line.. LAutoString &Prev = Stack.Last(); char *End = Prev + strlen(Prev); while (End > Prev && strchr(WhiteSpace, End[-1])) *(--End) = 0; LString s; s.Printf("%s%s", Prev.Get(), l); Prev.Reset(NewStr(s)); } } return true; } bool GetFrame(int &Frame, LAutoString &File, int &Line) { LAssert(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); ProcessId = -1; LString 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"); Cmd("handle SIGTTOU ignore nostop"); Cmd("handle SIG34 ignore nostop"); Cmd("handle SIGPIPE nostop"); } LString 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); LStringPipe p; #if 0 // For some reason this is returning the wrong PID... WTH gdb... WTH. // Get process info if (Cmd("info inferiors", &p)) { auto s = p.NewLStr(); // LogMsg("%s\n", s.Get()); auto Ln = s.SplitDelimit("\r\n"); if (Ln.Length() >= 2) { LString::Array a = Ln[1].SplitDelimit(" \t"); for (unsigned i=0; i= 0) { LogMsg("%s:%i - ProcessId was %i, now %i (%s)\n", _FL, ProcessId, Id, Ln[1].Get()); ProcessId = Id; } break; } } } #endif // Redetect the process id from the new threads... ProcessId = -1; bool Status = Cmd("c"); // Continue if (Status) SetState(true, true); LogMsg("[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_LEN]; 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; LString::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 = LCurrentTime(); while (State == Init) { LSleep(5); if (LCurrentTime()-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 = LCurrentTime(); while (State == Init) { LSleep(5); if (LCurrentTime()-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(LArray &bps) { bps = BreakPoints; return false; } void ParseVariables(const char *a, LArray &vars, LDebugger::Variable::ScopeType scope, bool Detailed) { LToken t(a, "\r\n"); LString CurLine; for (unsigned 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) || strchr(".-", *val)) { // Is it floating point? auto isFloat = strchr(val, '.') != NULL; printf("numeric type for '%s' is %i\n", val, isFloat); if (isFloat) { double tmp = atof(val); v.Value = tmp; } else { int64 tmp = atoi64(val); if (tmp & 0xffffffff00000000L) v.Value = tmp; else v.Value = (int)tmp; } } else { v.Value.OwnStr(TrimStr(val)); } if (Detailed) { LStringPipe typePipe, valPipe; LString c; // Get the type... c.Printf("whatis %s", v.Name.Get()); Cmd(c, &typePipe); auto type = typePipe.NewLStr(); printf("Type='%s'\n", type.Get()); c.Printf("p %s", v.Name.Get()); Cmd(c, &valPipe); auto val = valPipe.NewLStr(); if (val) { for (char *s = val; 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 = LSkipDelim(s, WhiteSpace, true); s = LSkipDelim(s, WhiteSpace); } } } } } } bool GetVariables(bool Locals, LArray &vars, bool Detailed) { LStringPipe p(512); if (vars.Length()) { LString 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; auto a = p.NewLStr(); ParseVariables(a, vars, Variable::Arg, Detailed); if (!Cmd("info locals", &p)) return false; a = p.NewLStr(); ParseVariables(a, vars, Variable::Local, Detailed); } return true; } bool GetRegisters(LStream *Out) { if (!Out) return false; return Cmd("info registers", Out); } bool PrintObject(const char *Var, LStream *Output) { if (!Var || !Output) return false; LStringPipe q; char c[256]; // Get type... sprintf_s(c, sizeof(c), "whatis %s", Var); if (!Cmd(c, &q)) return false; auto Type = q.NewLStr().SplitDelimit("=").Last().Strip(); bool IsPtr = Type.Find("*") >= 0; bool IsChar = Type.Find("const char") == 0 || Type.Find("char") == 0; bool IsGString = Type.Find("LString") == 0; #if 1 Output->Print("Type: %s\n", Type.Get()); #else // Debugging Output->Print("Type: %s (IsPtr=%i, IsGString=%i)\n", Type.Get(), IsPtr, IsGString); #endif // Get value... if (IsGString) { if (IsPtr) sprintf_s(c, sizeof(c), "p (char*)%s->Str.Str", Var); else sprintf_s(c, sizeof(c), "p (char*)%s.Str.Str", Var); } else sprintf_s(c, sizeof(c), "p %s%s", IsPtr && !IsChar ? "*" : "", Var); if (!Cmd(c, &q)) { Output->Print("%s:%i - Can't get value.\n", _FL); return false; } auto val = q.NewLStr(); if (!val) { Output->Print("%s:%i - No value.\n", _FL); return false; } // Output->Print("val=%s\n", val.Get()); auto Eq = Strchr(val.Get(), '='); if (Eq) { Eq++; while (Strchr(" \t\r\n", *Eq)) Eq++; } if (Eq && *Eq != '{') { auto s = val.SplitDelimit("=").Last().Strip(); Output->Print("%s\n", s.Get()); } else // Parse object format. { 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, LString *ErrorMsg) { if (!BaseAddr) { if (ErrorMsg) *ErrorMsg = "No base address supplied."; return false; } BaseAddr = BaseAddr.Strip(); LString::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? LString c; LString::Array r; c.Printf("p %s", BaseAddr.Get()); if (Cmd(c, NULL, &r)) { LString::Array p = r[0].SplitDelimit(" \t"); for (unsigned i=0; i= 0) { BaseAddr = p[i]; goto LiteralAddr; } /* LString 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_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(LAutoString &File, int &Line) { LAssert(0); return false; } bool SetLocation(const char *File, int Line) { LAssert(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) { LogMsg("%s:%i - No process ID (yet?).\n", _FL); return false; } SuppressNextFileLine = SuppressFL; // LogMsg("Break: Sending SIGINT to %i(0x%x)...\n", ProcessId, ProcessId); int result = kill(ProcessId, SIGINT); auto ErrNo = errno; // LogMsg("Break: result=%i\n", result); if (!result) { // LogMsg("%s:%i - success... waiting prompt\n", _FL); return WaitPrompt(); } LogMsg("%s:%i - SIGINT failed with %i(0x%x): %s (pid=%i)\n", _FL, ErrNo, ErrNo, LErrorCodeToString(ErrNo).Get(), ProcessId); return false; #else LAssert(!"Impl me"); return false; #endif } bool UserCommand(const char *cmd) { char c[256]; sprintf_s(c, sizeof(c), "%s", cmd); return Cmd(c); } LString GetResponse(const char *c) { LString r; LStringPipe p; if (Cmd(c, &p)) r = p.NewLStr(); return r; } }; LDebugger *CreateGdbDebugger(LStream *Log) { return new Gdb(Log); } diff --git a/include/lgi/common/Filter.h b/include/lgi/common/Filter.h --- a/include/lgi/common/Filter.h +++ b/include/lgi/common/Filter.h @@ -1,184 +1,184 @@ /** \file \author Matthew Allen \date 1/4/97 \brief Graphics file filters */ #ifndef FILTER2_H #define FILTER2_H #include #include "lgi/common/Containers.h" #include "lgi/common/Progress.h" // These are properties defined for use in the LFilter 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_TRANSPARENT "Transparent" // Non-zero if the background should be 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 LFilter's GetVariant method /// A descriptive name for a LFilter #define LGI_FILTER_TYPE "Type" /// A comma separated list of extensions the LFilter can handle #define LGI_FILTER_EXTENSIONS "Extension" LgiFunc int FindHeader(int Offset, const char *Str, LStream *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 /// LFilterFactory. class LgiClass LFilter : public LDom { LArray Buf; protected: LBmpMem *GetSurface(LSurface *pDC) { return pDC->pMem; } LRect *GetClip(LSurface *pDC) { return &pDC->Clip; } ssize_t GetLineLength(LSurface *pDC) { return (pDC->pMem) ? pDC->pMem->Line : 0; } Progress *Meter; inline void Swap(uint8_t *out, const void *in, ssize_t len) { #ifdef __BIG_ENDIAN__ // Swap the bytes... uint8_t *o = out + len - 1; const uint8_t *i = in; while (i < in + len) { *o++ = *i++; } #else // Copy the byte memcpy(out, in, len); #endif } bool Read(LStream *s, void *p, ssize_t 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_t*)p, &Buf[0], len); return true; } bool Write(LStream *s, const void *p, ssize_t 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: LDom *Props; enum Format { FmtJpeg, FmtPng, FmtBmp, FmtIco, FmtTiff, FmtGif, FmtPcx, FmtSpi, FmtTga, }; enum IoStatus { IoError, IoSuccess, IoComponentMissing, IoUnsupportedFormat, IoCancel }; LFilter() { Meter = 0; Props = 0; } virtual ~LFilter() { } 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(LSurface *Out, LStream *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(LStream *Out, LSurface *In) = 0; /// Returns the name of the external component needed to read/write images. virtual const char *GetComponentName() { return NULL; } }; //////////////////////////////////////////////////////////////// /// Factory class for creating filter objects. You should create a static /// instance of a class inheriting from LFilterFactory in your filter code /// that can create instances of your filter. class LgiClass LFilterFactory { static class LFilterFactory *First; class LFilterFactory *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 LGetExtension. 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 LFilter *NewObject() = 0; public: LFilterFactory(); virtual ~LFilterFactory(); static LAutoPtr New(const char *File, int Access, const uchar *Hint); static LAutoPtr NewAt(int i); static int GetItems(); }; #endif diff --git a/include/lgi/common/LgiRes.h b/include/lgi/common/LgiRes.h --- a/include/lgi/common/LgiRes.h +++ b/include/lgi/common/LgiRes.h @@ -1,320 +1,320 @@ #ifndef _LGI_RES_H_ #define _LGI_RES_H_ /** * \defgroup Resources Lgi multi-language resource support * * The LGI resource module allows you to edit resources in LgiRes * and then load them at runtime. A system of tags allows you to * include or exclude controls depending on various conditions. You * might have slightly different builds of the software that have * different UI. * * The system also allows full translation of the UI elements into * any language. Currently the system only loads one language at runtime * to save on memory. To change language you have to restart the * software using Lgi resources. * * There are two numbers that you can use to access resources: * - Reference (Ref): This is a globally unique number for any given * resource. Multiple controls with the same #define name will * have different Ref numbers. * - Identifier (Id): This number is the same for any control having * the same #define name. Usually dialogs only have distinct * control names internal to themselves. Different dialogs can * reuse the same names (Like ID_NEXT could be uses in several * dialogs). Id's are written to the 'resdefs.h' file that you * include in the source. * * \ingroup Lgi */ #include "lgi/common/Res.h" #include "lgi/common/Containers.h" #include "lgi/common/Css.h" #include "lgi/common/AutoPtr.h" #include "lgi/common/FontCache.h" class LResources; /// Resource loader class LgiClass LResourceLoad { public: /// Loading a dialog from the resource collection bool LoadFromResource ( /// The resource ID int Resource, /// The target view to populate LViewI *Parent, /// The size of the view in the resource LRect *Pos = 0, /// The name of the window LAutoString *Name = 0, /// [Optional] List of tags to exclude/include items char *TagList = 0 ); }; /// A string resource /// \ingroup Resources class LgiClass LStringRes { LResources *Res; public: static const char *CodePage; static LLanguage *CurLang; int Ref; int Id; char *Str; char *Tag; // bool IsString; LStringRes(LResources *res); ~LStringRes(); LResources *GetRes() { return Res; } bool Read(LXmlTag *Tag, ResFileFormat Format); }; /// A dialog resource /// \ingroup Resources class LgiClass LDialogRes { LResources *Res; public: LXmlTag *Dialog; LStringRes *Str; LRect Pos; LDialogRes(LResources *res); ~LDialogRes(); LResources *GetRes() { return Res; } bool Read(LXmlTag *Tag, ResFileFormat Format); char *Name() { return (Str) ? Str->Str : 0; } int Id() { return (Str) ? Str->Id : 0; } int X() { return Pos.X(); } int Y() { return Pos.Y(); } }; /// A menu resource /// \ingroup Resources class LgiClass LMenuRes : public LBase { LResources *Res; LHashTbl, LStringRes*> Strings; public: LXmlTag *Tag; LMenuRes(LResources *res); ~LMenuRes(); bool Read(LXmlTag *Tag, ResFileFormat Format); LResources *GetRes() { return Res; } LStringRes *GetString(LXmlTag *Tag); }; /// A resource collection. /// \ingroup Resources class LgiClass LResources : public ResFactory { friend class LResourceLoad; friend class LMenu; friend class LStringRes; class LResourcesPrivate *d; List Dialogs; List Menus; LEventsI *ScriptEngine; /// Array of languages available in the loaded file. LArray Languages; /// Add a language to the Languages array void AddLang(LLanguageId id); /// If this is true then all UI elements should attempt to load styles from the CSS store /// by calling 'StyleElement'. It will default to false for old applications. Newer apps /// can enable it manually. static bool LoadStyles; public: static bool DefaultColours; LHashTbl, char*> LanguageNames; /// This is all the CSS loaded from the lr8 file (and possibly other sources as well) LCss::Store CssStore; /// Get the load styles setting (enable 'StyleElement' to do something) static bool GetLoadStyles() { return LoadStyles; } /// Sets the loading of styles for all UI elements. static void SetLoadStyles(bool ls) { LoadStyles = ls; } /// This is called by UI elements to load styles if necessary. static bool StyleElement(LViewI *v); const char *GetThemeFolder(); void SetThemeFolder(const char *f); /// The constructor LResources ( /// [optional] The filename to use. const char *FileName = NULL, /// [optional] Warn if the file is not found. bool Warn = false, /// [optional] Folder for theming info const char *ThemeFolder = NULL ); virtual ~LResources(); /// Loads a dialog from the resource into the UI. /// \return true on success. bool LoadDialog ( /// The ID of the resource int Resource, /// The view to contain all the new controls. LViewI *Parent, /// [Optional] The size of the dialog if needed LRect *Pos = NULL, /// [Optional] The name of the window. LAutoString *Name = NULL, /// [Optional] A scripting engine interface LEventsI *ScriptEngine = NULL, /// [Optional] The current tags list for optional /// inclusion / exclusion of controls. char *Tags = 0 ); /// Get a string resource object using it's reference. LStringRes *StrFromRef(int Ref); /// Gets the value of a string resource from it's Ref. char *StringFromRef(int Ref); /// Gets the value of a string resource from it's Id. char *StringFromId(int Ref); /// \returns true if the object loaded ok. bool IsOk(); /// Gets the file format. ResFileFormat GetFormat(); /// Load a specific file bool Load(const char *FileName); /// Gets the filename used to load the object. char *GetFileName(); /// \returns the languages in the file. LArray *GetLanguages() { return &Languages; } /// \returns an iterator for all the dialogs in the resource collection. List::I GetDialogs() { return Dialogs.begin(); } /// Create a resource object /// \private ResObject *CreateObject(LXmlTag *Tag, ResObject *Parent); /// Sets the position of an object /// \private void Res_SetPos(ResObject *Obj, int x1, int y1, int x2, int y2); /// Create a resource object from a string /// \private void Res_SetPos(ResObject *Obj, char *s); /// Gets the position /// \private LRect Res_GetPos(ResObject *Obj); /// Gets the string ref number /// \private int Res_GetStrRef(ResObject *Obj); /// Sets the string ref associated with a control /// \private bool Res_SetStrRef(ResObject *Obj, int Ref, ResReadCtx *Ctx); /// Attach an object to another (create a parent / child relationship) /// \private void Res_Attach(ResObject *Obj, ResObject *Parent); /// Gets all the child objects /// \private bool Res_GetChildren(ResObject *Obj, List *l, bool Deep); /// Appends an object to a parent /// \private void Res_Append(ResObject *Obj, ResObject *Parent); /// ? /// \private bool Res_GetItems(ResObject *Obj, List *l); /// Gets a dom object of properties /// \private bool Res_GetProperties(ResObject *Obj, LDom *Props); /// Sets a dom object of properties /// \private bool Res_SetProperties(ResObject *Obj, LDom *Props); /// Gets the current dom object of properties /// \private LDom* Res_GetDom(ResObject *Obj); }; /// Loads a resource and returns a pointer to it. /// \ingroup Resources LgiExtern LResources *LgiGetResObj(bool Warn = false, const char *filename = 0, bool LoadOnDemand = true); /// This class is used to style LView controls with CSS class LgiClass LViewCssCb : public LCss::ElementCallback { public: const char *GetElement(LViewI *obj) { return obj->GetClass(); } const char *GetAttr(LViewI *obj, const char *Attr) { return NULL; } bool GetClasses(LString::Array &Classes, LViewI *obj) { LString::Array *a = obj->CssClasses(); if (!a) return false; Classes = *a; return true; } LViewI *GetParent(LViewI *obj) { return obj->GetParent(); } LArray GetChildren(LViewI *obj) { return obj->IterateViews(); } }; /// Loads a string from the resource file /// \ingroup Resources -LgiFunc const char *LLoadString(int Res, const char *Default = 0); +LgiFunc const char *LLoadString(int Res, const char *Default = NULL); #endif \ No newline at end of file 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,1572 +1,1564 @@ /*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/common/Lgi.h" #include "lgi/common/Palette.h" #ifdef __CYGWIN__ #include "png.h" #endif #include #include #include #include "lgi/common/LibraryUtils.h" #ifdef FILTER_UI #include "TransparentDlg.h" #endif #include "lgi/common/Variant.h" // Pixel formats typedef uint8_t Png8; typedef LRgb24 Png24; typedef LRgba32 Png32; typedef LRgb48 Png48; typedef LRgba64 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 #ifdef LINUX const char *LinuxLibName() { static char lib[64]; sprintf_s(lib, sizeof(lib), "libpng%i", PNG_LIBPNG_VER_SONUM); // printf("png lib name = '%s'\n", lib); return lib; } #endif #if LIBPNG_SHARED #define LIBPNG Lib-> const char *sLibrary = #if defined(MAC) || defined(HAIKU) "libpng16" #elif defined(LINUX) LinuxLibName() #else #if defined(__CYGWIN__) "cygpng12" #else "libpng16" #ifdef _MSC_VER_STR "_" #if _MSC_VER >= _MSC_VER_VS2019 _MSC_YEAR_STR #else _MSC_VER_STR #endif #if defined(LGI_64BIT) "x64" #else "x32" #endif #ifdef _DEBUG "d" #endif #endif #endif #endif ; // Library interface class LibPng : public LLibrary { public: LibPng() : LLibrary(sLibrary) { static bool First = true; if (First) { First = false; auto Loaded = IsLoaded(); if (Loaded) { LgiTrace("%s:%i - PNG: %s\n", _FL, GetFullPath().Get()); } else { #if defined(WINDOWS) && defined(_DEBUG) auto ReleaseLib = LString(sLibrary)(0, -2); if (!Load(ReleaseLib)) #endif LgiTrace("%s:%i - Failed to load '%s'.\n", _FL, sLibrary); } } } 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 { LAutoPtr Png; public: LibPng *Get() { if (Lock(_FL)) { if (!Png) Png.Reset(new LibPng); Unlock(); } return Png; } InitLibPng() : LMutex("InitLibPng") { } } CurrentLibPng; #else #define LIBPNG #endif class GdcPng : public LFilter { 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; LSurface *pDC; LMemQueue DataPipe; jmp_buf Here; public: GdcPng ( #if LIBPNG_SHARED LibPng *lib #endif ); ~GdcPng(); const char *GetComponentName() override { return "libpng"; } Format GetFormat() override { return FmtPng; } void SetMeter(int i) { if (Meter) Meter->Value(i); } int GetCapabilites() override { return FILTER_CAP_READ | FILTER_CAP_WRITE; } IoStatus ReadImage(LSurface *pDC, LStream *In) override; IoStatus WriteImage(LStream *Out, LSurface *pDC) override; bool GetVariant(const char *n, LVariant &v, const char *a = NULL) override { 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 LFilterFactory { 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; } } LFilter *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 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) { LVariant 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) { LStream *s = (LStream*) #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); LAssert(0); } } struct PngWriteInfo { LStream *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__); LAssert(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++; } } LFilter::IoStatus GdcPng::ReadImage(LSurface *pDeviceContext, LStream *In) { LFilter::IoStatus Status = IoError; Pos = 0; pDC = pDeviceContext; DeleteArray(PrevScanLine); if (!pDC) { LAssert(!"No DC."); return Status; } LVariant v; #if LIBPNG_SHARED if (!Lib->IsLoaded() && !Lib->Load(sLibrary)) { LString 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 LFilter::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); LAssert(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; LColourSpace InCs = ColourType == PNG_COLOR_TYPE_GRAY_ALPHA ? CsIndex8 : LBitsToColourSpace(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, LSurface::SurfaceRequireExactCs)) { printf("%s:%i - LMemDC::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", LColourSpaceToString(InCs), LColourSpaceToString(pDC->GetColourSpace())); */ #endif // Copy in the scanlines int ActualBits = pDC->GetBits(); int ScanLen = LIBPNG png_get_image_width(png_ptr, info_ptr) * ActualBits / 8; LColourSpace OutCs = pDC->GetColourSpace(); for (int y=0; yY() && !Error; y++) { uchar *Scan = (*pDC)[y]; LAssert(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 2: { uchar *i = Scan0[y]; uchar *o = Scan; for (int x=0; xX(); x++) { switch (x & 3) { case 0: *o++ = (*i >> 6) & 0x3; break; case 1: *o++ = (*i >> 4) & 0x3; break; case 2: *o++ = (*i >> 2) & 0x3; break; case 3: *o++ = (*i++ >> 0) & 0x3; break; } } 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_t *grey = Scan; uint8_t *alpha = (*(pDC->AlphaDC()))[y]; LAssert(grey && alpha); 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((L##name*)Scan, (Png48*)Scan0[y], pDC->X()); \ else \ Read32_##bits((L##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(), LColourSpaceToString(pDC->GetColourSpace())); LAssert(!"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((L##name*)Scan, (Png64*)Scan0[y], pDC->X()); \ else \ ReadAlpha32_##bits((L##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(), LColourSpaceToString(pDC->GetColourSpace())); LAssert(!"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 { LAssert(!"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) { LPalette *Pal = new LPalette(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); LSurface *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; } LFilter::IoStatus GdcPng::WriteImage(LStream *Out, LSurface *pDC) { LFilter::IoStatus Status = IoError; LVariant Transparent; bool HasTransparency = false; COLOUR Back = 0; LVariant v; if (!pDC) return LFilter::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 LFilter::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()) { LSurface *a = pDC->AlphaDC(); if (a) { for (int y=0; yY() && !HasTransparency; y++) { uint8_t *p = (*a)[y]; if (!p) break; uint8_t *e = p + a->X(); while (p < e) { if (*p < 255) { HasTransparency = true; break; } p++; } } } } if (Props) { if (Props->GetValue(LGI_FILTER_BACKGROUND, v)) Back = v.CastInt32(); - Props->GetValue(LGI_FILTER_TRANSPARENT, Transparent); } - - if (Transparent.IsNull()) - { - // LTransparentDlg needs to set this in the caller - LAssert(!"Move this to the parent app."); - return IoError; - } - + if (setjmp(Here) == 0 && pDC && Out) { LVariant 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; LMemDC *pTemp = 0; if (pDC->AlphaDC() && HasTransparency) { pTemp = new LMemDC(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", 0, (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->SetRange(pDC->Y()); switch (pDC->GetBits()) { case 8: { // Output the palette LPalette *Pal = pDC->Palette(); if (Pal) { int Colours = Pal->GetSize(); LAutoPtr 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; } } LArray row; if (row.Length(pDC->Y())) { for (int y=0; yY(); y++) { row[y] = TempBits + (TempLine * y); } LIBPNG png_set_rows(png_ptr, info_ptr, row.AddressOf()); LIBPNG png_write_png(png_ptr, info_ptr, 0, 0); Status = IoSuccess; } 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/Widgets/RadioGroup.cpp b/src/common/Widgets/RadioGroup.cpp --- a/src/common/Widgets/RadioGroup.cpp +++ b/src/common/Widgets/RadioGroup.cpp @@ -1,861 +1,861 @@ #if !defined(_WIN32) || (XP_BUTTON != 0) #include #include #include "lgi/common/Lgi.h" #include "lgi/common/SkinEngine.h" #include "lgi/common/RadioGroup.h" #include "lgi/common/CheckBox.h" #include "lgi/common/DisplayString.h" #include "lgi/common/LgiRes.h" #include "lgi/common/StringLayout.h" #define RADIO_GRID 4 /////////////////////////////////////////////////////////////////////////////////////////// // Radio group static int MinYSize = 16; class LRadioGroupPrivate : public LMutex, public LStringLayout { LRadioGroup *Ctrl; public: static int NextId; int Val; int MaxLayoutWidth; LHashTbl,LViewLayoutInfo*> Info; - LArray GroupIDs; LRadioGroupPrivate(LRadioGroup *g) : LMutex("LRadioGroupPrivate"), LStringLayout(LAppInst->GetFontCache()) { Ctrl = g; Val = 0; MaxLayoutWidth = 0; AmpersandToUnderline = true; } ~LRadioGroupPrivate() { Info.DeleteObjects(); } bool PreLayout(int32 &Min, int32 &Max) { if (Lock(_FL)) { DoPreLayout(Min, Max); Unlock(); } else return false; return true; } bool Layout(int Px) { if (Lock(_FL)) { DoLayout(Px, MinYSize); Unlock(); } else return false; return true; } }; int LRadioGroupPrivate::NextId = 10000; LRadioGroup::LRadioGroup(int id, int x, int y, int cx, int cy, const char *name, int Init) : ResObject(Res_Group) { d = new LRadioGroupPrivate(this); Name(name); LRect r(x, y, x+cx, y+cy); SetPos(r); SetId(id); d->Val = Init; LResources::StyleElement(this); } LRadioGroup::~LRadioGroup() { DeleteObj(d); } void LRadioGroup::OnStyleChange() { if (d->Lock(_FL)) { d->Empty(); d->Add(LView::Name(), GetCss()); d->DoLayout(X()); d->Unlock(); Invalidate(); } } bool LRadioGroup::OnLayout(LViewLayoutInfo &Inf) { auto children = IterateViews(); const int BORDER_PX = 2; int MinPx = (RADIO_GRID + BORDER_PX) * 2; if (!Inf.Width.Max) { // Work out the width... d->PreLayout(Inf.Width.Min, Inf.Width.Max); Inf.Width.Min += MinPx; Inf.Width.Max += MinPx; d->Info.DeleteObjects(); // Inf.Width.Min = 16 + TextPx; // Inf.Width.Max = RADIO_GRID + BORDER_PX * 2; for (LViewI *w: children) { LAutoPtr c(new LViewLayoutInfo); if (w->OnLayout(*c)) { // Layout enabled control Inf.Width.Min = MAX(Inf.Width.Min, c->Width.Min + MinPx); Inf.Width.Max += c->Width.Max + RADIO_GRID; d->Info.Add(w, c.Release()); } else { // Non layout enabled control Inf.Width.Min = MAX(Inf.Width.Min, w->X() + (RADIO_GRID << 1)); Inf.Width.Max += w->X() + RADIO_GRID; } } if (Inf.Width.Max < Inf.Width.Min) Inf.Width.Max = Inf.Width.Min; d->MaxLayoutWidth = Inf.Width.Max; } else { d->Layout(Inf.Width.Max); Inf.Height.Min = d->GetMin().y + MinPx; Inf.Height.Max = d->GetMax().y + MinPx; // Working out the height, and positioning the controls // Inf.Height.Min = d->Txt ? d->Txt->Y() : 16; bool Horiz = d->MaxLayoutWidth <= Inf.Width.Max; int Cx = BORDER_PX + RADIO_GRID, Cy = d->GetMin().y; int LastY = 0; for (LViewI *w: children) { LViewLayoutInfo *c = d->Info.Find(w); if (c) { if (w->OnLayout(*c)) { LRect r(Cx, Cy, Cx + c->Width.Max - 1, Cy + c->Height.Max - 1); w->SetPos(r); if (Horiz) // Horizontal layout Cx += r.X() + RADIO_GRID; else // Vertical layout Cy += r.Y() + RADIO_GRID; LastY = MAX(LastY, r.y2); } else LAssert(!"This shouldn't fail."); } else { // Non layout control... just use existing size LRect r = w->GetPos(); r.Offset(Cx - r.x1, Cy - r.y1); w->SetPos(r); if (Horiz) // Horizontal layout Cx += r.X() + RADIO_GRID; else // Vertical layout Cy += r.Y() + RADIO_GRID; LastY = MAX(LastY, r.y2); } } Inf.Height.Min = Inf.Height.Max = LastY + RADIO_GRID * 2 + BORDER_PX; } return true; } void LRadioGroup::OnAttach() { LResources::StyleElement(this); OnStyleChange(); LView::OnAttach(); } LMessage::Result LRadioGroup::OnEvent(LMessage *m) { return LView::OnEvent(m); } bool LRadioGroup::Name(const char *n) { bool Status = false; if (d->Lock(_FL)) { Status = LView::Name(n); d->Empty(); d->Add(n, GetCss()); d->SetBaseFont(GetFont()); d->DoLayout(X()); d->Unlock(); } return Status; } bool LRadioGroup::NameW(const char16 *n) { bool Status = false; if (d->Lock(_FL)) { Status = LView::NameW(n); d->Empty(); d->Add(LBase::Name(), GetCss()); d->SetBaseFont(GetFont()); d->DoLayout(X()); d->Unlock(); } return Status; } void LRadioGroup::SetFont(LFont *Fnt, bool OwnIt) { LAssert(Fnt && Fnt->Handle()); if (d->Lock(_FL)) { LView::SetFont(Fnt, OwnIt); d->Unlock(); } d->Layout(X()); Invalidate(); } void LRadioGroup::OnCreate() { AttachChildren(); Value(d->Val); } int64 LRadioGroup::Value() { int i=0; for (auto w: Children) { LRadioButton *But = dynamic_cast(w); if (But) { if (But->Value()) { d->Val = i; break; } i++; } } return d->Val; } void LRadioGroup::Value(int64 Which) { d->Val = (int)Which; int i=0; for (auto w: Children) { LRadioButton *But = dynamic_cast(w); if (But) { if (i == Which) { But->Value(true); break; } i++; } } } int LRadioGroup::OnNotify(LViewI *Ctrl, LNotification n) { LViewI *v = GetNotify() ? GetNotify() : GetParent(); if (v) { if (dynamic_cast(Ctrl)) { return v->OnNotify(this, n); } else { return v->OnNotify(Ctrl, n); } } return 0; } void LRadioGroup::OnPaint(LSurface *pDC) { if (LApp::SkinEngine && TestFlag(LApp::SkinEngine->GetFeatures(), GSKIN_GROUP)) { LSkinState State; State.pScreen = pDC; State.MouseOver = false; State.aText = d->GetStrs(); LApp::SkinEngine->OnPaint_LRadioGroup(this, &State); } else { // LColour Fore = StyleColour(LCss::PropColor, LC_TEXT); LColour Back = StyleColour(LCss::PropBackgroundColor, LColour(L_MED)); if (!Back.IsTransparent()) { pDC->Colour(Back); pDC->Rectangle(); } int y = d->GetMin().y; LRect b(0, y/2, X()-1, Y()-1); LWideBorder(pDC, b, EdgeXpChisel); LPoint TxtPt(6, 0); LRect TxtRc = d->GetBounds(); TxtRc.Offset(TxtPt.x, TxtPt.y); d->Paint(pDC, TxtPt, Back, TxtRc, Enabled(), false); } } LRadioButton *LRadioGroup::Append(int x, int y, const char *name) { LRadioButton *But = new LRadioButton(d->NextId++, x, y, -1, -1, name); if (But) { Children.Insert(But); } return But; } /////////////////////////////////////////////////////////////////////////////////////////// // Radio button class LRadioButtonPrivate : public LMutex, public LStringLayout { public: LRadioButton *Ctrl; bool Val; bool Over; LRect Btn; LColour BackCol; + LArray GroupIDs; LRadioButtonPrivate(LRadioButton *c) : LMutex("LRadioButtonPrivate"), LStringLayout(LAppInst->GetFontCache()) { Btn.ZOff(-1,-1); Ctrl = c; Val = 0; Over = 0; AmpersandToUnderline = true; } ~LRadioButtonPrivate() { } LRect GetBtn() { auto CtrlHt = Ctrl->Y(); auto Fnt = Ctrl->GetFont(); int Px = (int) (Fnt->Ascent() + 0.5); if (Px > CtrlHt) Px = CtrlHt; Btn.ZOff(Px-1, Px-1); Btn.Offset(0, (CtrlHt-Btn.Y())>>1); return Btn; } bool PreLayout(int32 &Min, int32 &Max) { if (Lock(_FL)) { DoPreLayout(Min, Max); Unlock(); } else return false; return true; } bool Layout(int Px) { if (Lock(_FL)) { DoLayout(Px); Unlock(); /* if (Min.y < MinYSize) Min.y = MinYSize; if (Max.y < MinYSize) Max.y = MinYSize; */ } else return false; return true; } }; static int PadXPx = 24; // 13px for circle, 11px padding to text. #ifdef MAC static int PadYPx = 6; #else static int PadYPx = 4; #endif LRadioButton::LRadioButton(int id, int x, int y, int cx, int cy, const char *name) : ResObject(Res_RadioBox) { d = new LRadioButtonPrivate(this); Name(name); if (cx < 0) cx = d->GetBounds().X() + PadXPx; if (cy < 0) cy = d->GetBounds().Y() + PadYPx; LRect r(x, y, x+cx, y+cy); SetPos(r); SetId(id); d->Val = false; d->Over = false; SetTabStop(true); #if WINNATIVE SetDlgCode(GetDlgCode() | DLGC_WANTARROWS); #endif } LRadioButton::~LRadioButton() { DeleteObj(d); } bool LRadioButton::SetGroup(LArray CtrlIds) { auto w = GetWindow(); if (!w) return false; // This ctrl should be in the ID list. auto id = GetId(); if (!CtrlIds.HasItem(id)) CtrlIds.Add(id); for (auto i: CtrlIds) { LRadioButton *button; if (!w->GetViewById(i, button)) return false; button->d->GroupIDs = CtrlIds; } return true; } void LRadioButton::OnAttach() { LResources::StyleElement(this); OnStyleChange(); LView::OnAttach(); } void LRadioButton::OnStyleChange() { if (d->Lock(_FL)) { d->Empty(); d->Add(LView::Name(), GetCss()); d->DoLayout(X()); d->Unlock(); Invalidate(); } } bool LRadioButton::Name(const char *n) { bool Status = false; if (d->Lock(_FL)) { Status = LView::Name(n); d->Empty(); d->Add(n, GetCss()); d->SetBaseFont(GetFont()); d->DoLayout(X()); d->Unlock(); } return Status; } bool LRadioButton::NameW(const char16 *n) { bool Status = false; if (d->Lock(_FL)) { Status = LView::NameW(n); d->Empty(); d->Add(LBase::Name(), GetCss()); d->SetBaseFont(GetFont()); d->DoLayout(X()); d->Unlock(); } return Status; } void LRadioButton::SetFont(LFont *Fnt, bool OwnIt) { LAssert(Fnt && Fnt->Handle()); if (d->Lock(_FL)) { LView::SetFont(Fnt, OwnIt); d->Unlock(); } d->Layout(X()); Invalidate(); } bool LRadioButton::OnLayout(LViewLayoutInfo &Inf) { auto Btn = d->GetBtn(); int Pad = Btn.X() + 4; if (!Inf.Width.Max) { d->PreLayout(Inf.Width.Min, Inf.Width.Max); // FIXME: Wrapping labels not supported yet. So use the max width. Inf.Width.Min = Inf.Width.Max; Inf.Width.Min += Pad; Inf.Width.Max += Pad; } else { d->Layout(Inf.Width.Max); Inf.Height.Min = Inf.Height.Max = MAX(d->GetMin().y, Btn.Y()); } return true; } int LRadioButton::OnNotify(LViewI *Ctrl, LNotification n) { if (Ctrl == (LViewI*)this && n.Type == LNotifyActivate) { Value(true); } return 0; } int64 LRadioButton::Value() { return d->Val; } void LRadioButton::Value(int64 i) { if (d->Val != (i != 0)) { if (i) { // remove the value from the currently selected radio value if (d->GroupIDs.Length()) { if (auto w = GetWindow()) { for (auto id: d->GroupIDs) { if (id == GetId()) continue; LRadioButton *button; if (w->GetViewById(id, button)) { if (button->Value()) button->Value(false); } } } } else { // Use the automatic mode... iterate through sibling views. if (auto p = GetParent()) { for (auto c: p->IterateViews()) { LRadioButton *b = dynamic_cast(c); if (b && b != this && b->d->Val) { b->d->Val = false; b->Invalidate(); } } } } } d->Val = i != 0; Invalidate(); if (i) SendNotify(); } } void LRadioButton::OnMouseClick(LMouse &m) { if (Enabled()) { bool WasCapturing = IsCapturing(); if (m.Down()) Focus(true); Capture(m.Down()); d->Over = m.Down(); LRect r(0, 0, X()-1, Y()-1); if (!m.Down() && r.Overlap(m.x, m.y) && WasCapturing) { Value(true); } else { Invalidate(); } } } void LRadioButton::OnMouseEnter(LMouse &m) { if (Enabled() && IsCapturing()) { d->Over = true; Invalidate(); } } void LRadioButton::OnMouseExit(LMouse &m) { if (Enabled() && IsCapturing()) { d->Over = false; Invalidate(); } } bool LRadioButton::OnKey(LKey &k) { bool Status = false; int Move = 0; switch (k.vkey) { case LK_UP: case LK_LEFT: { if (k.Down()) { Move = -1; } Status = true; break; } case LK_RIGHT: case LK_DOWN: { if (k.Down()) { Move = 1; } Status = true; break; } case LK_SPACE: { if (k.Down()) { Value(1); } return true; } } if (Move) { List Btns; for (LViewI *c: GetParent()->IterateViews()) { LRadioButton *b = dynamic_cast(c); if (b) Btns.Insert(b); } if (Btns.Length() > 1) { ssize_t Index = Btns.IndexOf(this); if (Index >= 0) { LRadioButton *n = Btns[(Index + Move + Btns.Length()) % Btns.Length()]; if (n) { n->Focus(true); } } } } return Status; } void LRadioButton::OnFocus(bool f) { Invalidate(); } void LRadioButton::OnPaint(LSurface *pDC) { if (LApp::SkinEngine && TestFlag(LApp::SkinEngine->GetFeatures(), GSKIN_RADIO)) { LSkinState State; State.pScreen = pDC; State.MouseOver = d->Over; State.aText = d->GetStrs(); State.View = this; State.Rect = d->GetBtn(); LColour Back = StyleColour(LCss::PropBackgroundColor, LColour(L_MED)); State.ForceUpdate = d->BackCol != Back; d->BackCol = Back; LApp::SkinEngine->OnPaint_LRadioButton(this, &State); } else { LRect r(0, 0, X()-1, Y()-1); LRect c(0, 0, 12, 12); // LColour Fore = StyleColour(LCss::PropColor, LC_TEXT, 4); LColour Back = StyleColour(LCss::PropBackgroundColor, LColour(L_MED)); // bool e = Enabled(); LRect fill(c.x2 + 1, r.y1, r.x2, r.x2); LPoint TxtPt(c.x2 + 11, (r.Y() - d->GetBounds().Y()) >> 1); d->Paint(pDC, TxtPt, Back, fill, Enabled(), false); #if defined LGI_CARBON LRect cli = GetClient(); for (LViewI *v = this; v && !v->Handle(); v = v->GetParent()) { LRect p = v->GetPos(); cli.Offset(p.x1, p.y1); } pDC->Colour(Back); pDC->Rectangle(cli.x1, cli.y1, c.x2, cli.y2); LRect rc(c.x1, c.y1 + 4, c.x2 - 1, c.y2 - 1); HIRect Bounds = rc; HIThemeButtonDrawInfo Info; HIRect LabelRect; Info.version = 0; Info.state = d->Val ? kThemeStatePressed : (Enabled() ? kThemeStateActive : kThemeStateInactive); Info.kind = kThemeRadioButton; Info.value = d->Val ? kThemeButtonOn : kThemeButtonOff; Info.adornment = Focus() ? kThemeAdornmentFocus : kThemeAdornmentNone; OSStatus err = HIThemeDrawButton(&Bounds, &Info, pDC->Handle(), kHIThemeOrientationNormal, &LabelRect); if (err) printf("%s:%i - HIThemeDrawButton failed %li\n", _FL, err); #else // Draw border pDC->Colour(L_LOW); pDC->Line(c.x1+1, c.y1+9, c.x1+1, c.y1+10); pDC->Line(c.x1, c.y1+4, c.x1, c.y1+8); pDC->Line(c.x1+1, c.y1+2, c.x1+1, c.y1+3); pDC->Line(c.x1+2, c.y1+1, c.x1+3, c.y1+1); pDC->Line(c.x1+4, c.y1, c.x1+8, c.y1); pDC->Line(c.x1+9, c.y1+1, c.x1+10, c.y1+1); pDC->Colour(L_SHADOW); pDC->Set(c.x1+2, c.y1+9); pDC->Line(c.x1+1, c.y1+4, c.x1+1, c.y1+8); pDC->Line(c.x1+2, c.y1+2, c.x1+2, c.y1+3); pDC->Set(c.x1+3, c.y1+2); pDC->Line(c.x1+4, c.y1+1, c.x1+8, c.y1+1); pDC->Set(c.x1+9, c.y1+2); pDC->Colour(L_LIGHT); pDC->Line(c.x1+11, c.y1+2, c.x1+11, c.y1+3); pDC->Line(c.x1+12, c.y1+4, c.x1+12, c.y1+8); pDC->Line(c.x1+11, c.y1+9, c.x1+11, c.y1+10); pDC->Line(c.x1+9, c.y1+11, c.x1+10, c.y1+11); pDC->Line(c.x1+4, c.y1+12, c.x1+8, c.y1+12); pDC->Line(c.x1+2, c.y1+11, c.x1+3, c.y1+11); /// Draw center bool e = Enabled(); pDC->Colour(d->Over || !e ? L_MED : L_WORKSPACE); pDC->Rectangle(c.x1+2, c.y1+4, c.x1+10, c.y1+8); pDC->Box(c.x1+3, c.y1+3, c.x1+9, c.y1+9); pDC->Box(c.x1+4, c.y1+2, c.x1+8, c.y1+10); // Draw value if (d->Val) { pDC->Colour(e ? L_TEXT : L_LOW); pDC->Rectangle(c.x1+4, c.y1+5, c.x1+8, c.y1+7); pDC->Rectangle(c.x1+5, c.y1+4, c.x1+7, c.y1+8); } #endif } } #endif