diff --git a/Ide/LgiIdeProj.xml b/Ide/LgiIdeProj.xml
--- a/Ide/LgiIdeProj.xml
+++ b/Ide/LgiIdeProj.xml
@@ -1,274 +1,290 @@
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
.\buildHaiku.py
- ./buildHaiku.py
+ ./Makefile.linux
./MacCocoa/LgiIde.xcodeproj
Makefile.haiku
./lgiide
./lgiide
LgiIde.exe
LgiIde.exe
WINDOWS
WINNATIVE
WINDOWS
WINNATIVE
POSIX
POSIX
LIBPNG_VERSION=\"1.2\"
LIBPNG_VERSION=\"1.2\"
MingW
/home/matthew/Code/Lgi/trunk/Ide/Code/IdeProjectSettings.cpp
+
+
./Code
./Resources
../include
./Code
./Resources
../include
..\include\lgi\win
/local/include
..\include\lgi\win
/local/include
../include/lgi/linux
../include/lgi/linux/Gtk
../../../../codelib/openssl/include
../include/lgi/linux
../include/lgi/linux/Gtk
../../../../codelib/openssl/include
../include/lgi/haiku
../include/lgi/haiku
../include/lgi/mac/cocoa
../include/lgi/mac/cocoa
imm32
imm32
magic
pthread
`pkg-config --libs gtk+-3.0`
-static-libgcc
magic
pthread
`pkg-config --libs gtk+-3.0`
-static-libgcc
-static-libgcc
gnu
network
be
-static-libgcc
gnu
network
be
+
..\Debug
..\Release
+
Executable
lgiide
lgiide
LgiIde.exe
LgiIde.exe
lgiide
lgiide
C:\Data\CodeLib\Gtk\include\gtk-2.0\gtk
C:\Data\CodeLib\Gtk\include\gtk-2.0\gdk
C:\Data\CodeLib\Gtk\include\gtk-2.0\gtk
C:\Data\CodeLib\Gtk\include\gtk-2.0\gdk
`pkg-config --cflags gtk+-3.0`
`pkg-config --cflags gtk+-3.0`
POSIX
_GNU_SOURCE
POSIX
_GNU_SOURCE
+
+
4
4
0
1
+
+
SOME_TEST=testing
/home/matthew/code/scribe/trunk_os/Linux/ScribeProj.xml
+
+
-
+
+
+
+
-
+
+
+
+
diff --git a/Ide/Makefile.linux b/Ide/Makefile.linux
--- a/Ide/Makefile.linux
+++ b/Ide/Makefile.linux
@@ -1,423 +1,421 @@
#!/usr/bin/make
#
# This makefile generated by LgiIde
# http://www.memecode.com/lgi.php
#
.SILENT :
CC = gcc
CPP = g++
Target = ./lgiide
ifndef Build
Build = Debug
endif
BuildDir = $(Build)
Flags = -fPIC -w -fno-inline -fpermissive
ifeq ($(Build),Debug)
Flags += -g -std=c++14
Tag = d
Defs = -D_DEBUG -DLINUX -D_REENTRANT -D_FILE_OFFSET_BITS=64 -DPOSIX
Libs = \
-L../Debug \
-lmagic \
-lpthread \
`pkg-config --libs gtk+-3.0` \
-static-libgcc \
-llgi-gtk3$(Tag) \
-L../$(BuildDir)
Inc = \
`pkg-config --cflags gtk+-3.0` \
-I./Resources \
-I./Code \
-I../include/lgi/linux/Gtk \
-I../include/lgi/linux \
-I../include \
-I../../../../codelib/openssl/include
else
Flags += -s -Os -std=c++14
Defs = -DLINUX -D_REENTRANT -D_FILE_OFFSET_BITS=64 -DPOSIX
Libs = \
-L../Release \
-lmagic \
-lpthread \
`pkg-config --libs gtk+-3.0` \
-static-libgcc \
-llgi-gtk3$(Tag) \
-L../$(BuildDir)
Inc = \
`pkg-config --cflags gtk+-3.0` \
-I./Resources \
-I./Code \
-I../include/lgi/linux/Gtk \
-I../include/lgi/linux \
-I../include \
-I../../../../codelib/openssl/include
endif
# Dependencies
Source = Code/WebFldDlg.cpp \
Code/SysCharSupport.cpp \
Code/SpaceTabConv.cpp \
Code/SimpleCppParser.cpp \
Code/PythonParser.cpp \
Code/ProjectNode.cpp \
Code/NewProjectFromTemplate.cpp \
Code/MissingFiles.cpp \
Code/MemDumpViewer.cpp \
Code/LgiUtils.cpp \
Code/LgiIde.cpp \
Code/levenshtein.c \
Code/JavascriptParser.cpp \
Code/IdeProjectSettings.cpp \
Code/IdeProject.cpp \
Code/IdeDoc.cpp \
Code/IdeCommon.cpp \
Code/History.cpp \
- Code/Debugger.cpp \
- Code/DebugContext.cpp \
Code/FtpThread.cpp \
Code/FindSymbol.cpp \
Code/FindInFiles.cpp \
Code/DocEditStyling.cpp \
Code/DocEdit.cpp \
Code/Debugger.cpp \
Code/DebugContext.cpp \
Code/AddFtpFile.cpp \
../src/common/Text/TextConvert.cpp \
../src/common/Text/HtmlParser.cpp \
../src/common/Text/HtmlCommon.cpp \
../src/common/Text/Html.cpp \
../src/common/Text/Homoglyphs/HomoglyphsTable.cpp \
../src/common/Text/Homoglyphs/Homoglyphs.cpp \
../src/common/Text/DocView.cpp \
../src/common/Net/OpenSSLSocket.cpp \
../src/common/Net/Http.cpp \
../src/common/Net/Ftp.cpp \
../src/common/Lgi/Mdi.cpp \
../src/common/Lgi/LgiMain.cpp \
../src/common/Lgi/About.cpp \
../src/common/Gdc2/Filters/Png.cpp \
../src/common/Coding/ParseCpp.cpp \
../src/common/Coding/LexCpp.cpp
SourceLst := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(Source)))
Objects := $(addprefix $(BuildDir)/,$(SourceLst))
Deps := $(patsubst %.o,%.d,$(Objects))
# Target
# Executable target
$(Target) : ../$(BuildDir)/liblgi-gtk3$(Tag).so $(Objects)
mkdir -p $(BuildDir)
@echo Linking $(Target) [$(Build)]...
$(CPP) -Wl,-export-dynamic,-R. -o \
$(Target) $(Objects) $(Libs)
@echo Done.
../$(BuildDir)/liblgi-gtk3$(Tag).so : ../include/lgi/common/App.h \
../include/lgi/common/Array.h \
../include/lgi/common/AutoPtr.h \
../include/lgi/common/Base64.h \
../include/lgi/common/Bitmap.h \
../include/lgi/common/Box.h \
../include/lgi/common/Button.h \
../include/lgi/common/CairoSurface.h \
../include/lgi/common/Cancel.h \
../include/lgi/common/Capabilities.h \
../include/lgi/common/Charset.h \
../include/lgi/common/CheckBox.h \
../include/lgi/common/ClipBoard.h \
../include/lgi/common/Colour.h \
../include/lgi/common/ColourSpace.h \
../include/lgi/common/Com.h \
../include/lgi/common/Combo.h \
../include/lgi/common/Containers.h \
../include/lgi/common/Core.h \
../include/lgi/common/Css.h \
../include/lgi/common/CssTools.h \
../include/lgi/common/CurrentTime.h \
../include/lgi/common/DataDlg.h \
../include/lgi/common/DateTime.h \
../include/lgi/common/Dialog.h \
../include/lgi/common/DisplayString.h \
../include/lgi/common/DocView.h \
../include/lgi/common/Dom.h \
../include/lgi/common/DomFields.h \
../include/lgi/common/DragAndDrop.h \
../include/lgi/common/DropFiles.h \
../include/lgi/common/Edit.h \
../include/lgi/common/Error.h \
../include/lgi/common/EventTargetThread.h \
../include/lgi/common/File.h \
../include/lgi/common/FileSelect.h \
../include/lgi/common/Filter.h \
../include/lgi/common/FindReplaceDlg.h \
../include/lgi/common/Font.h \
../include/lgi/common/FontCache.h \
../include/lgi/common/FontSelect.h \
../include/lgi/common/Gdc2.h \
../include/lgi/common/GdcTools.h \
../include/lgi/common/GdiLeak.h \
../include/lgi/common/HashTable.h \
../include/lgi/common/ImageList.h \
../include/lgi/common/Input.h \
../include/lgi/common/ItemContainer.h \
../include/lgi/common/Json.h \
../include/lgi/common/Layout.h \
../include/lgi/common/Lgi.h \
../include/lgi/common/LgiClasses.h \
../include/lgi/common/LgiCommon.h \
../include/lgi/common/LgiDefs.h \
../include/lgi/common/LgiInc.h \
../include/lgi/common/LgiInterfaces.h \
../include/lgi/common/LgiMsgs.h \
../include/lgi/common/LgiNetInc.h \
../include/lgi/common/LgiRes.h \
../include/lgi/common/LgiString.h \
../include/lgi/common/LgiUiBase.h \
../include/lgi/common/Library.h \
../include/lgi/common/LibraryUtils.h \
../include/lgi/common/List.h \
../include/lgi/common/ListItemCheckBox.h \
../include/lgi/common/ListItemRadioBtn.h \
../include/lgi/common/LMallocArray.h \
../include/lgi/common/Mail.h \
../include/lgi/common/Matrix.h \
../include/lgi/common/Mem.h \
../include/lgi/common/Menu.h \
../include/lgi/common/Message.h \
../include/lgi/common/Mime.h \
../include/lgi/common/Mru.h \
../include/lgi/common/Mutex.h \
../include/lgi/common/Net.h \
../include/lgi/common/NetTools.h \
../include/lgi/common/Notifications.h \
../include/lgi/common/OAuth2.h \
../include/lgi/common/OptionsFile.h \
../include/lgi/common/Palette.h \
../include/lgi/common/Panel.h \
../include/lgi/common/Password.h \
../include/lgi/common/Path.h \
../include/lgi/common/PixelRops.h \
../include/lgi/common/Point.h \
../include/lgi/common/Popup.h \
../include/lgi/common/PopupList.h \
../include/lgi/common/Printer.h \
../include/lgi/common/Profile.h \
../include/lgi/common/Progress.h \
../include/lgi/common/ProgressDlg.h \
../include/lgi/common/ProgressView.h \
../include/lgi/common/Properties.h \
../include/lgi/common/RadioGroup.h \
../include/lgi/common/Range.h \
../include/lgi/common/Rect.h \
../include/lgi/common/RectF.h \
../include/lgi/common/RefCount.h \
../include/lgi/common/RegKey.h \
../include/lgi/common/Res.h \
../include/lgi/common/Rops.h \
../include/lgi/common/ScrollBar.h \
../include/lgi/common/SkinEngine.h \
../include/lgi/common/Slider.h \
../include/lgi/common/Splitter.h \
../include/lgi/common/StatusBar.h \
../include/lgi/common/Store3Defs.h \
../include/lgi/common/Stream.h \
../include/lgi/common/StringClass.h \
../include/lgi/common/StringLayout.h \
../include/lgi/common/StructuredIo.h \
../include/lgi/common/StructuredLog.h \
../include/lgi/common/SubProcess.h \
../include/lgi/common/TableLayout.h \
../include/lgi/common/TabView.h \
../include/lgi/common/TextFile.h \
../include/lgi/common/TextLabel.h \
../include/lgi/common/TextLog.h \
../include/lgi/common/TextView3.h \
../include/lgi/common/Thread.h \
../include/lgi/common/ThreadEvent.h \
../include/lgi/common/Token.h \
../include/lgi/common/ToolBar.h \
../include/lgi/common/ToolTip.h \
../include/lgi/common/TrayIcon.h \
../include/lgi/common/Tree.h \
../include/lgi/common/Undo.h \
../include/lgi/common/Unicode.h \
../include/lgi/common/UnicodeString.h \
../include/lgi/common/UnrolledList.h \
../include/lgi/common/Variant.h \
../include/lgi/common/View.h \
../include/lgi/common/Widgets.h \
../include/lgi/common/Window.h \
../include/lgi/common/XmlTree.h \
../include/lgi/linux/Gtk/LgiOsClasses.h \
../include/lgi/linux/Gtk/LgiOsDefs.h \
../include/lgi/linux/Gtk/LgiWidget.h \
../include/lgi/linux/Gtk/LgiWinManGlue.h \
../include/lgi/linux/SymLookup.h \
../include/lgi/mac/cocoa/LCocoaView.h \
../include/lgi/mac/cocoa/LgiMac.h \
../include/lgi/mac/cocoa/LgiOs.h \
../include/lgi/mac/cocoa/LgiOsClasses.h \
../include/lgi/mac/cocoa/LgiOsDefs.h \
../include/lgi/mac/cocoa/ObjCWrapper.h \
../include/lgi/mac/cocoa/SymLookup.h \
../private/common/FontPriv.h \
../private/common/ViewPriv.h \
../private/linux/AppPriv.h \
../src/common/Gdc2/15Bit.cpp \
../src/common/Gdc2/16Bit.cpp \
../src/common/Gdc2/24Bit.cpp \
../src/common/Gdc2/32Bit.cpp \
../src/common/Gdc2/8Bit.cpp \
../src/common/Gdc2/Alpha.cpp \
../src/common/Gdc2/Colour.cpp \
../src/common/Gdc2/Filters/Filter.cpp \
../src/common/Gdc2/Font/Charset.cpp \
../src/common/Gdc2/Font/DisplayString.cpp \
../src/common/Gdc2/Font/Font.cpp \
../src/common/Gdc2/Font/FontSystem.cpp \
../src/common/Gdc2/Font/FontType.cpp \
../src/common/Gdc2/Font/StringLayout.cpp \
../src/common/Gdc2/Font/TypeFace.cpp \
../src/common/Gdc2/GdcCommon.cpp \
../src/common/Gdc2/Path/Path.cpp \
../src/common/Gdc2/Rect.cpp \
../src/common/Gdc2/RopsCases.cpp \
../src/common/Gdc2/Surface.cpp \
../src/common/Gdc2/Tools/ColourReduce.cpp \
../src/common/Gdc2/Tools/GdcTools.cpp \
../src/common/General/Containers.cpp \
../src/common/General/DateTime.cpp \
../src/common/General/ExeCheck.cpp \
../src/common/General/FileCommon.cpp \
../src/common/General/Password.cpp \
../src/common/General/Properties.cpp \
../src/common/Hash/md5/md5.c \
../src/common/Hash/md5/md5.h \
../src/common/Hash/sha1/sha1.c \
../src/common/Hash/sha1/sha1.h \
../src/common/Lgi/Alert.cpp \
../src/common/Lgi/AppCommon.cpp \
../src/common/Lgi/Css.cpp \
../src/common/Lgi/CssTools.cpp \
../src/common/Lgi/DataDlg.cpp \
../src/common/Lgi/DragAndDropCommon.cpp \
../src/common/Lgi/FileSelect.cpp \
../src/common/Lgi/FindReplace.cpp \
../src/common/Lgi/FontSelect.cpp \
../src/common/Lgi/GuiUtils.cpp \
../src/common/Lgi/Input.cpp \
../src/common/Lgi/LgiCommon.cpp \
../src/common/Lgi/Library.cpp \
../src/common/Lgi/LMsg.cpp \
../src/common/Lgi/MemStream.cpp \
../src/common/Lgi/MenuCommon.cpp \
../src/common/Lgi/Mru.cpp \
../src/common/Lgi/Mutex.cpp \
../src/common/Lgi/Object.cpp \
../src/common/Lgi/OptionsFile.cpp \
../src/common/Lgi/Rand.cpp \
../src/common/Lgi/Stream.cpp \
../src/common/Lgi/SubProcess.cpp \
../src/common/Lgi/ThreadCommon.cpp \
../src/common/Lgi/ThreadEvent.cpp \
../src/common/Lgi/ToolTip.cpp \
../src/common/Lgi/TrayIcon.cpp \
../src/common/Lgi/Variant.cpp \
../src/common/Lgi/ViewCommon.cpp \
../src/common/Lgi/WindowCommon.cpp \
../src/common/Net/Base64.cpp \
../src/common/Net/MDStringToDigest.cpp \
../src/common/Net/Net.cpp \
../src/common/Net/NetTools.cpp \
../src/common/Net/Uri.cpp \
../src/common/Resource/LgiRes.cpp \
../src/common/Resource/Res.cpp \
../src/common/Skins/Gel/Gel.cpp \
../src/common/Text/DocView.cpp \
../src/common/Text/String.cpp \
../src/common/Text/TextView3.cpp \
../src/common/Text/Token.cpp \
../src/common/Text/Unicode.cpp \
../src/common/Text/Utf8.cpp \
../src/common/Text/XmlTree.cpp \
../src/common/Widgets/Bitmap.cpp \
../src/common/Widgets/Box.cpp \
../src/common/Widgets/Button.cpp \
../src/common/Widgets/CheckBox.cpp \
../src/common/Widgets/Combo.cpp \
../src/common/Widgets/Edit.cpp \
../src/common/Widgets/ItemContainer.cpp \
../src/common/Widgets/List.cpp \
../src/common/Widgets/Panel.cpp \
../src/common/Widgets/Popup.cpp \
../src/common/Widgets/Progress.cpp \
../src/common/Widgets/ProgressDlg.cpp \
../src/common/Widgets/RadioGroup.cpp \
../src/common/Widgets/ScrollBar.cpp \
../src/common/Widgets/Slider.cpp \
../src/common/Widgets/Splitter.cpp \
../src/common/Widgets/StatusBar.cpp \
../src/common/Widgets/TableLayout.cpp \
../src/common/Widgets/TabView.cpp \
../src/common/Widgets/TextLabel.cpp \
../src/common/Widgets/ToolBar.cpp \
../src/common/Widgets/Tree.cpp \
../src/linux/General/File.cpp \
../src/linux/General/Mem.cpp \
../src/linux/General/ShowFileProp_Linux.cpp \
../src/linux/Gtk/Gdc2.cpp \
../src/linux/Gtk/LgiWidget.cpp \
../src/linux/Gtk/MemDC.cpp \
../src/linux/Gtk/PrintDC.cpp \
../src/linux/Gtk/ScreenDC.cpp \
../src/linux/Lgi/App.cpp \
../src/linux/Lgi/ClipBoard.cpp \
../src/linux/Lgi/DragAndDrop.cpp \
../src/linux/Lgi/General.cpp \
../src/linux/Lgi/Layout.cpp \
../src/linux/Lgi/Menu.cpp \
../src/linux/Lgi/Printer.cpp \
../src/linux/Lgi/Thread.cpp \
../src/linux/Lgi/View.cpp \
../src/linux/Lgi/Widgets.cpp \
../src/linux/Lgi/Window.cpp
export Build=$(Build); \
$(MAKE) -C .. -f Makefile.linux
.SECONDEXPANSION:
$(Objects): $(BuildDir)/%.o: $$(wildcard %.c*)
mkdir -p $(@D)
@echo $(
Makefile.linux
Makefile.win64
Makefile.macosx
gcc
0
-
+
./include
./private/common
./include
./private/common
./include/lgi/linux
./include/lgi/linux/Gtk
./private/linux
./include/lgi/linux
./include/lgi/linux/Gtk
./private/linux
./include/lgi/win
./private/win
./include/lgi/win
./private/win
./include/lgi/haiku
./private/haiku
./include/lgi/haiku
./private/haiku
/usr/include/libappindicator3-0.1
`pkg-config --cflags gtk+-3.0`
`pkg-config --cflags gstreamer-1.0`
/usr/include/libappindicator3-0.1
`pkg-config --cflags gtk+-3.0`
`pkg-config --cflags gstreamer-1.0`
-
magic
appindicator3
crypt
-static-libgcc
`pkg-config --libs gtk+-3.0`
magic
appindicator3
crypt
-static-libgcc
`pkg-config --libs gtk+-3.0`
-static-libgcc
gnu
network
be
-static-libgcc
gnu
network
be
-
lgi-gtk3
lgi-gtk3
-
DynamicLibrary
LGI_LIBRARY
LGI_LIBRARY
POSIX
_GNU_SOURCE
POSIX
_GNU_SOURCE
-
+
-
+
-
+
diff --git a/Lvc/Src/PatchViewer.cpp b/Lvc/Src/PatchViewer.cpp
--- a/Lvc/Src/PatchViewer.cpp
+++ b/Lvc/Src/PatchViewer.cpp
@@ -1,604 +1,610 @@
#include "lgi/common/Lgi.h"
#include "lgi/common/Box.h"
#include "lgi/common/TextLog.h"
#include "lgi/common/TableLayout.h"
#include "lgi/common/TextLabel.h"
#include "lgi/common/Edit.h"
#include "lgi/common/Button.h"
#include "lgi/common/FileSelect.h"
#include "lgi/common/OptionsFile.h"
#include "lgi/common/XmlTreeUi.h"
enum Ctrls
{
IDC_STATIC = -1,
IDC_BOX1 = 100,
IDC_BOX2,
IDC_IN,
IDC_OUT,
IDC_TBL,
IDC_PATCH_FILE,
IDC_BROWSE_PATCH,
IDC_BASE_DIR,
IDC_BROWSE_BASE,
IDC_APPLY,
IDC_SAVE_PATCH,
};
template
ssize_t lineLen(T *start)
{
auto s = start;
for (; *s && *s != '\n'; s++);
return s - start;
}
class PatchView : public LTextLog
{
public:
PatchView(int id) : LTextLog(id)
{
}
void PourStyle(size_t Start, ssize_t Length)
{
for (auto l: Line)
{
auto s = Text + l->Start;
if (*s == '+' && s[1] != '+')
{
l->c = LColour::Green;
}
else if (*s == '-' && s[1] != '-')
{
l->c = LColour::Red;
}
else if (*s == '@' && s[1] == '@')
{
l->c.Rgb(128, 128, 128);
l->Back.Rgb(222, 222, 222);
}
}
}
};
class FileView : public LTextLog
{
public:
LArray Fore, Back;
FileView(int id) : LTextLog(id)
{
}
bool Open(const char *Name, const char *Cs = NULL)
{
Fore.Length(0);
Back.Length(0);
return LTextView3::Open(Name, Cs);
}
void Reset(const char16 *t)
{
NameW(t);
Fore.Length(0);
Back.Length(0);
}
void Update()
{
PourStyle(0, Size);
Invalidate();
}
void PourStyle(size_t Start, ssize_t Length)
{
for (size_t i=0; iStart;
if (Fore.IdxCheck(i))
{
auto &c = Fore[i];
if (c.IsValid())
l->c = c;
}
if (Back.IdxCheck(i))
{
auto &c = Back[i];
if (c.IsValid())
l->Back = c;
}
}
}
};
class PatchViewer : public LWindow, public LXmlTreeUi
{
LBox *box1 = NULL;
LBox *box2 = NULL;
FileView *in = NULL;
LTextLog *out = NULL;
LTableLayout *tbl = NULL;
LOptionsFile *Opts = NULL;
public:
PatchViewer(LViewI *Parent, LOptionsFile *opts) : Opts(opts)
{
LRect r(0, 0, 1600, 1000);
SetPos(r);
Name("Patcher");
MoveToCenter();
Map("PatchFile", IDC_PATCH_FILE);
Map("PatchBaseDir", IDC_BASE_DIR);
if (!Attach(0))
return;
AddView(box1 = new LBox(IDC_BOX1, true));
box1->AddView(tbl = new LTableLayout(IDC_TBL));
tbl->GetCss(true)->Height("6em");
int y = 0;
auto c = tbl->GetCell(0, y);
c->Add(new LTextLabel(IDC_STATIC, 0, 0, -1, -1, "Patch file:"));
c->VerticalAlign(LCss::VerticalMiddle);
c->PaddingLeft("0.5em");
c->PaddingTop("0.5em");
c = tbl->GetCell(1, y);
c->Add(new LEdit(IDC_PATCH_FILE, 0, 0, 60, 20));
c->PaddingTop("0.5em");
c = tbl->GetCell(2, y++);
c->Add(new LButton(IDC_BROWSE_PATCH, 0, 0, -1, -1, "..."));
c->PaddingRight("0.5em");
c->PaddingTop("0.5em");
c = tbl->GetCell(0, y);
c->Add(new LTextLabel(IDC_STATIC, 0, 0, -1, -1, "Base folder:"));
c->VerticalAlign(LCss::VerticalMiddle);
c->PaddingLeft("0.5em");
c = tbl->GetCell(1, y);
c->Add(new LEdit(IDC_BASE_DIR, 0, 0, 60, 20));
c = tbl->GetCell(2, y++);
c->Add(new LButton(IDC_BROWSE_BASE, 0, 0, -1, -1, "..."));
c->PaddingRight("0.5em");
c = tbl->GetCell(0, y);
c = tbl->GetCell(1, y);
c->Add(new LButton(IDC_SAVE_PATCH, 0, 0, -1, -1, "Save Patch"));
c->TextAlign(LCss::AlignRight);
c = tbl->GetCell(2, y);
c->Add(new LButton(IDC_APPLY, 0, 0, -1, -1, "Apply"));
c->PaddingRight("0.5em");
box1->AddView(box2 = new LBox(IDC_BOX2));
box2->AddView(in = new FileView(IDC_IN));
box2->AddView(out = new PatchView(IDC_OUT));
Convert(Opts, this, true);
AttachChildren();
Visible(true);
in->Name("...select a patch blob...");
auto p = GetCtrlName(IDC_PATCH_FILE);
if (p)
Open(p);
}
~PatchViewer()
{
Convert(Opts, this, false);
}
bool Open(const char *Patch)
{
if (!out->LTextView3::Open(Patch))
return false;
SetCtrlName(IDC_PATCH_FILE, Patch);
return true;
}
template
LArray GetLines(T *txt)
{
LArray lines;
lines.Add(txt);
for (auto t = txt; *t; t++)
{
if (*t == '\n' && *t)
lines.Add(t + 1);
}
return lines;
}
struct FilePatch
{
LString File;
LArray Hunks;
};
bool Apply()
{
auto patch = out->NameW();
auto lines = GetLines(out->NameW());
LArray Patches;
auto error = [](const char *msg) -> bool
{
LgiTrace("Apply error: %s\n", msg);
return false;
};
auto scanLines = [&lines](const char16 *key, size_t from) -> ssize_t
{
auto len = Strlen(key);
for (size_t i = from; i LString
{
auto ptr = lines[idx];
LString s(ptr, lineLen(ptr));
return s;
};
ssize_t aFile = -1, bFile = -1;
for (size_t i=0; i 0 &&
bFile > 0 &&
aFile == bFile - 1)
{
FilePatch &fp = Patches.New();
auto s = lineAt(aFile);
fp.File = s(6,-1);
LgiTrace("File: %s\n", fp.File.Get());
// Collect blobs
LRange *cur = NULL;
size_t n = bFile + 1;
for (; n < lines.Length(); n++)
{
auto ln = lines[n];
if (*ln == ' ')
continue;
bool start = ln[0] == '@' && ln[1] == '@';
bool end = (ln[0] == '-' && ln[1] == '-');
bool finish = Strnicmp(ln, L"diff ", 5) == 0;
if ((start || end || finish) && cur)
{
cur->Len = n - cur->Start;
cur = NULL;
}
if (start && !cur)
{
cur = &fp.Hunks.New();
cur->Start = n;
}
if (finish)
break;
}
for (auto &b: fp.Hunks)
LgiTrace(" Blob: %i:%i\n", (int)b.Start, (int)b.Len);
i = n;
aFile = bFile = -1;
}
}
// Iterate all the patches and apply them...
for (auto &fp: Patches)
{
LFile::Path path(GetCtrlName(IDC_BASE_DIR), fp.File);
if (!path.Exists())
{
LgiTrace("%s:%i - File to patch '%s' doesn't exist.\n", _FL, path.GetFull().Get());
return false;
}
LFile f(path, O_READ);
auto utf = f.Read();
size_t crlf = 0, lf = 0;
for (const char *s = utf; *s; s++)
{
if (s[0] == '\r' && s[1] == '\n') { crlf++; s++; }
else if (s[0] == '\n') { lf++; }
}
auto eol = crlf > lf ? L"\r\n" : L"\n";
auto eolLen = Strlen(eol);
LAutoWString wide(Utf8ToWide(utf));
LArray docLines;
for (char16 *w = wide; *w; )
{
for (auto e = w; true; e++)
{
if (!Strncmp(e, eol, eolLen))
{
docLines.New().Reset(NewStrW(w, e - w));
w = e + eolLen;
break;
}
if (!*e)
{
docLines.New().Reset(NewStrW(w, e - w));
w = e;
break;
}
}
}
/*
for (size_t i=0; i (ssize_t)docLines.Length())
return error("docLine out of range.");
auto docTxt = docLines[docLine].Get();
auto docTxtLen = lineLen(docTxt);
auto inputLineLen = lineLen(inputTxt);
if (*inputTxt == '+')
{
// Process insert: add 'inputLine' at docLine
LAutoWString insert(NewStrW(inputTxt + 1, inputLineLen - 1));
docLines.AddAt(docLine, insert);
}
else
{
// Check content...
if (docTxtLen != inputLineLen - 1)
return error("doc and input line lengths don't match.");
auto cmp = Strncmp(docTxt, inputTxt + 1/*prefix char*/, docTxtLen);
if (cmp)
return error("doc and input line content don't match.");
if (*inputTxt == '-')
{
// Process delete...
docLines.DeleteAt(docLine--, true);
}
}
docLine++;
inputTxt = lines[hunk.Start + ++inputLine];
}
}
}
return true;
}
int OnNotify(LViewI *Ctrl, LNotification n)
{
switch (Ctrl->GetId())
{
case IDC_BROWSE_PATCH:
{
- LFileSelect s;
- s.Parent(this);
- s.InitialDir(GetCtrlName(IDC_PATCH_FILE));
- if (s.Open())
+ auto s = new LFileSelect;
+ s->Parent(this);
+ s->InitialDir(GetCtrlName(IDC_PATCH_FILE));
+ s->Open([this](auto s, auto status)
{
- SetCtrlName(IDC_PATCH_FILE, s.Name());
- Open(s.Name());
- }
+ if (status)
+ {
+ SetCtrlName(IDC_PATCH_FILE, s->Name());
+ Open(s->Name());
+ }
+ delete s;
+ });
break;
}
case IDC_BROWSE_BASE:
{
- LFileSelect s;
- s.Parent(this);
- if (s.OpenFolder())
+ auto s = new LFileSelect;
+ s->Parent(this);
+ s->OpenFolder([this](auto s, auto status)
{
- SetCtrlName(IDC_BASE_DIR, s.Name());
- }
+ if (status)
+ SetCtrlName(IDC_BASE_DIR, s->Name());
+ delete s;
+ });
break;
}
case IDC_SAVE_PATCH:
{
auto patchFile = GetCtrlName(IDC_PATCH_FILE);
out->Save(patchFile);
break;
}
case IDC_OUT:
{
if (n.Type != LNotifyCursorChanged)
break;
auto car = out->GetCaret();
auto txt = out->NameW();
if (!txt)
break;
ssize_t caratLine = -1;
LArray lines;
lines.Add(txt);
for (auto t = txt; *t; t++)
{
if (caratLine < 0 && (t - txt) >= car)
caratLine = lines.Length() - 1;
if (*t == '\n' && *t)
lines.Add(t + 1);
}
if (caratLine < 0)
{
LgiTrace("%s:%i - No line num for carat pos.\n", _FL);
break;
}
if (caratLine >= (ssize_t)lines.Length())
{
LgiTrace("%s:%i - carat line greater than lines.len.\n", _FL);
break;
}
LRange blob(-1, 0);
for (ssize_t s = caratLine; s >= 0; s--)
{
auto ln = lines[s];
if (ln[0] == '@' && ln[1] == '@')
{
blob.Start = s;
break;
}
}
for (ssize_t e = caratLine; e < (ssize_t)lines.Length(); e++)
{
auto ln = lines[e];
if ( (ln[0] == '@' && ln[1] == '@') ||
(ln[0] == '-' && ln[1] == '-') )
{
blob.Len = e - blob.Start;
break;
}
else if (e == lines.Length() - 1)
{
blob.Len = e - blob.Start + 1;
break;
}
}
if (!blob.Valid())
{
LgiTrace("%s:%i - invalid patch blob size.\n", _FL);
break;
}
auto filesIdx = blob.Start;
while (filesIdx >= 0)
{
auto t = lines[filesIdx];
if (Strnicmp(t, L"--- a/", 6) == 0)
break;
filesIdx--;
}
if (filesIdx == 0)
{
LgiTrace("%s:%i - couldn't find file names lines.\n", _FL);
break;
}
auto aFile = lines[filesIdx];
auto bFile = lines[filesIdx + 1];
LFile::Path p(GetCtrlName(IDC_BASE_DIR), LString(aFile, lineLen(aFile))(5,-1));
if (!p.Exists())
{
LgiTrace("%s:%i - file '%s' doesn't exist.\n", _FL, p.GetFull().Get());
break;
}
if (!in->Open(p))
{
LgiTrace("%s:%i - failed to open file '%s'.\n", _FL, p.GetFull().Get());
break;
}
auto sText = lines[blob.Start];
auto eText = lines[blob.End()];
auto ln = LString(sText, lineLen(sText));
auto hdr = ln.SplitDelimit();
auto before = hdr[1].SplitDelimit(",");
auto after = hdr[2].SplitDelimit(",");
auto beforeLn = -before[0].Int() - 1;
auto beforeEnd = beforeLn + before[1].Int();
size_t blobIdx = blob.Start + 1;
for (ssize_t i=beforeLn; iBack[i].Rgb(222, 222, 222);
auto patchLn = lines[blobIdx++];
while (*patchLn == '+')
patchLn = lines[blobIdx++];
if (*patchLn != '+')
{
auto inTxt = in->TextAtLine(i);
auto inLen = lineLen(inTxt);
if (Strnicmp(inTxt, patchLn + 1, inLen))
{
in->Fore[i] = LColour::Red;
}
else
{
if (*patchLn == '-')
in->Fore[i] = LColour::Green;
}
}
}
in->Update();
in->SetLine((int)beforeLn);
break;
}
case IDC_APPLY:
{
Apply();
break;
}
}
return LWindow::OnNotify(Ctrl, n);
}
};
void OpenPatchViewer(LViewI *Parent, LOptionsFile *Opts)
{
new PatchViewer(Parent, Opts);
}
diff --git a/Makefile.linux b/Makefile.linux
--- a/Makefile.linux
+++ b/Makefile.linux
@@ -1,217 +1,219 @@
#!/usr/bin/make
#
# This makefile generated by LgiIde
# http://www.memecode.com/lgi.php
#
.SILENT :
CC = gcc
CPP = g++
Target = lgi-gtk3
ifndef Build
Build = Debug
endif
BuildDir = $(Build)
Flags = -fPIC -w -fno-inline -fpermissive
ifeq ($(Build),Debug)
Flags += -g -std=c++14
Tag = d
- Defs = -D_DEBUG -DLINUX -D_REENTRANT -DLGI_LIBRARY
+ Defs = -D_DEBUG -DLINUX -D_REENTRANT -D_FILE_OFFSET_BITS=64 -DPOSIX -DLGI_LIBRARY
Libs = \
-lmagic \
-lappindicator3 \
-lcrypt \
-static-libgcc \
`pkg-config --libs gtk+-3.0`
Inc = \
`pkg-config --cflags gtk+-3.0` \
`pkg-config --cflags gstreamer-1.0` \
+ -I/usr/include/libappindicator3-0.1 \
-I./private/linux \
-I./private/common \
-I./include/lgi/linux/Gtk \
-I./include/lgi/linux \
-I./include
else
Flags += -s -Os -std=c++14
- Defs = -DLINUX -D_REENTRANT -DLGI_LIBRARY
+ Defs = -DLINUX -D_REENTRANT -D_FILE_OFFSET_BITS=64 -DPOSIX -DLGI_LIBRARY
Libs = \
-lmagic \
-lappindicator3 \
-lcrypt \
-static-libgcc \
`pkg-config --libs gtk+-3.0`
Inc = \
`pkg-config --cflags gtk+-3.0` \
`pkg-config --cflags gstreamer-1.0` \
+ -I/usr/include/libappindicator3-0.1 \
-I./private/linux \
-I./private/common \
-I./include/lgi/linux/Gtk \
-I./include/lgi/linux \
-I./include
endif
# Dependencies
Source = src/linux/Lgi/Window.cpp \
src/linux/Lgi/Widgets.cpp \
src/linux/Lgi/View.cpp \
src/linux/Lgi/Thread.cpp \
src/linux/Lgi/Printer.cpp \
src/linux/Lgi/Menu.cpp \
src/linux/Lgi/Layout.cpp \
src/linux/Lgi/General.cpp \
src/linux/Lgi/DragAndDrop.cpp \
src/linux/Lgi/ClipBoard.cpp \
src/linux/Lgi/App.cpp \
src/linux/Gtk/ScreenDC.cpp \
src/linux/Gtk/PrintDC.cpp \
src/linux/Gtk/MemDC.cpp \
src/linux/Gtk/LgiWidget.cpp \
src/linux/Gtk/Gdc2.cpp \
src/linux/General/ShowFileProp_Linux.cpp \
src/linux/General/Mem.cpp \
src/linux/General/File.cpp \
src/common/Widgets/Tree.cpp \
src/common/Widgets/ToolBar.cpp \
src/common/Widgets/TextLabel.cpp \
src/common/Widgets/TabView.cpp \
src/common/Widgets/TableLayout.cpp \
src/common/Widgets/StatusBar.cpp \
src/common/Widgets/Splitter.cpp \
src/common/Widgets/Slider.cpp \
src/common/Widgets/ScrollBar.cpp \
src/common/Widgets/RadioGroup.cpp \
src/common/Widgets/ProgressDlg.cpp \
src/common/Widgets/Progress.cpp \
src/common/Widgets/Popup.cpp \
src/common/Widgets/Panel.cpp \
src/common/Widgets/List.cpp \
src/common/Widgets/ItemContainer.cpp \
src/common/Widgets/Edit.cpp \
src/common/Widgets/Combo.cpp \
src/common/Widgets/CheckBox.cpp \
src/common/Widgets/Button.cpp \
src/common/Widgets/Box.cpp \
src/common/Widgets/Bitmap.cpp \
src/common/Text/XmlTree.cpp \
src/common/Text/Utf8.cpp \
src/common/Text/Unicode.cpp \
src/common/Text/Token.cpp \
src/common/Text/TextView3.cpp \
src/common/Text/String.cpp \
src/common/Text/DocView.cpp \
src/common/Skins/Gel/Gel.cpp \
src/common/Resource/Res.cpp \
src/common/Resource/LgiRes.cpp \
src/common/Net/Uri.cpp \
src/common/Net/NetTools.cpp \
src/common/Net/Net.cpp \
src/common/Net/MDStringToDigest.cpp \
src/common/Net/Base64.cpp \
src/common/Lgi/WindowCommon.cpp \
src/common/Lgi/ViewCommon.cpp \
src/common/Lgi/Variant.cpp \
src/common/Lgi/TrayIcon.cpp \
src/common/Lgi/ToolTip.cpp \
src/common/Lgi/ThreadEvent.cpp \
src/common/Lgi/ThreadCommon.cpp \
src/common/Lgi/SubProcess.cpp \
src/common/Lgi/Stream.cpp \
src/common/Lgi/Rand.cpp \
src/common/Lgi/OptionsFile.cpp \
src/common/Lgi/Object.cpp \
src/common/Lgi/Mutex.cpp \
src/common/Lgi/Mru.cpp \
src/common/Lgi/MenuCommon.cpp \
src/common/Lgi/MemStream.cpp \
src/common/Lgi/LMsg.cpp \
src/common/Lgi/Library.cpp \
src/common/Lgi/LgiCommon.cpp \
src/common/Lgi/Input.cpp \
src/common/Lgi/GuiUtils.cpp \
src/common/Lgi/FontSelect.cpp \
src/common/Lgi/FindReplace.cpp \
src/common/Lgi/FileSelect.cpp \
src/common/Lgi/DragAndDropCommon.cpp \
src/common/Lgi/DataDlg.cpp \
src/common/Lgi/CssTools.cpp \
src/common/Lgi/Css.cpp \
src/common/Lgi/AppCommon.cpp \
src/common/Lgi/Alert.cpp \
src/common/Hash/sha1/sha1.c \
src/common/Hash/md5/md5.c \
src/common/General/Properties.cpp \
src/common/General/Password.cpp \
src/common/General/FileCommon.cpp \
src/common/General/ExeCheck.cpp \
src/common/General/DateTime.cpp \
src/common/General/Containers.cpp \
src/common/Gdc2/Tools/GdcTools.cpp \
src/common/Gdc2/Tools/ColourReduce.cpp \
src/common/Gdc2/Surface.cpp \
src/common/Gdc2/Rect.cpp \
src/common/Gdc2/Path/Path.cpp \
src/common/Gdc2/GdcCommon.cpp \
src/common/Gdc2/Font/TypeFace.cpp \
src/common/Gdc2/Font/StringLayout.cpp \
src/common/Gdc2/Font/FontType.cpp \
src/common/Gdc2/Font/FontSystem.cpp \
src/common/Gdc2/Font/Font.cpp \
src/common/Gdc2/Font/DisplayString.cpp \
src/common/Gdc2/Font/Charset.cpp \
src/common/Gdc2/Filters/Filter.cpp \
src/common/Gdc2/Colour.cpp \
src/common/Gdc2/Alpha.cpp \
src/common/Gdc2/8Bit.cpp \
src/common/Gdc2/32Bit.cpp \
src/common/Gdc2/24Bit.cpp \
src/common/Gdc2/16Bit.cpp \
src/common/Gdc2/15Bit.cpp
SourceLst := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(Source)))
Objects := $(addprefix $(BuildDir)/,$(SourceLst))
Deps := $(patsubst %.o,%.d,$(Objects))
# Target
TargetFile = lib$(Target)$(Tag).so
$(TargetFile) : $(Objects)
mkdir -p $(BuildDir)
@echo Linking $(TargetFile) [$(Build)]...
$(CPP)$s -shared \
\
-o $(BuildDir)/$(TargetFile) \
$(Objects) \
$(Libs)
@echo Done.
.SECONDEXPANSION:
$(Objects): $(BuildDir)/%.o: $$(wildcard %.c*)
mkdir -p $(@D)
@echo $(WndStyle
#else
#define GViewFlags WndFlags
#endif
#if defined(__GTK_H__)
struct LCaptureThread : public LThread, public LCancel
{
int view = -1;
LString name;
public:
constexpr static int EventMs = 150;
LCaptureThread(LView *v);
~LCaptureThread();
int Main();
};
#elif defined(MAC)
extern OsThread LgiThreadInPaint;
#elif defined(HAIKU)
#include
extern void *IsAttached(BView *v);
#endif
#define PAINT_VIRTUAL_CHILDREN 0
extern bool In_SetWindowPos;
extern LMouse &lgi_adjust_click(LMouse &Info,
LViewI *Wnd,
bool Capturing = false,
bool Debug = false);
#ifdef __GTK_H__
extern LPoint GtkAbsPos(Gtk::GtkWidget *w);
extern LRect GtkGetPos(Gtk::GtkWidget *w);
#endif
#if !WINNATIVE
#include "lgi/common/ThreadEvent.h"
class LPulseThread : public LThread, public LCancel
{
LView *View = NULL;
LString ViewClass;
int Length = 0;
LThreadEvent Event;
uint64_t WarnTs = 0;
LString MakeName(LView *v, const char *Type)
{
LString s;
s.Printf("LPulseThread.%s.%s", v->GetClass(), Type);
return s;
}
public:
LPulseThread(LView *view, int len) :
View(view),
LThread(MakeName(view, "Thread")),
Event(MakeName(view, "Event"))
{
LAssert(View);
Length = len;
ViewClass = View->GetClass();
- // printf("PulseThread=%i, %s, %i\n", PulseThreadCount, View->GetClass(), Length);
-
Run();
}
~LPulseThread()
{
- Cancel();
View = NULL;
Cancel();
Event.Signal();
WaitForExit();
- PulseThreadCount--;
- LSleep(1);
}
int Main()
{
while (!IsCancelled() && LAppInst)
{
auto s = Event.Wait(Length);
- if (IsCancelled() || s == LThreadEvent::WaitError)
+ if (!View || IsCancelled() || s == LThreadEvent::WaitError)
break;
- if (View && !View->PostEvent(M_PULSE))
- Cancel();
- auto r = View->PostEvent(M_PULSE, 0, 0, 50/*milliseconds*/);
- #if 0
- if (!r)
+ if (!View->PostEvent(M_PULSE, 0, 0, 50/*milliseconds*/))
+ {
+ auto now = LCurrentTime();
+ if (now - WarnTs >= 5000)
{
- auto now = LCurrentTime();
- if (now - WarnTs >= 5000)
- {
- WarnTs = now;
- printf("%s:%i - PulseThread::PostEvent failed for %p/%s.\n", _FL, View, ViewClass.Get());
- }
+ WarnTs = now;
+ printf("%s:%i - PulseThread::PostEvent failed for %p/%s.\n", _FL, View, ViewClass.Get());
}
- #endif
+ }
}
return 0;
}
};
+
#endif
enum LViewFontType
{
/// The LView has a pointer to an externally owned font.
GV_FontPtr,
/// The LView owns the font object, and must free it.
GV_FontOwned,
/// The LApp's font cache owns the object. In this case,
/// calling GetCssStyle on the LView will invalidate the
/// font ptr causing it to be re-calculated.
GV_FontCached,
};
class LViewPrivate
{
public:
// General
LView *View = NULL; // Owning view
LDragDropSource *DropSource = NULL;
LDragDropTarget *DropTarget = NULL;
bool IsThemed = false;
int CtrlId = -1;
int WantsPulse = -1;
// Hierarchy
LViewI *ParentI = NULL;
LView *Parent = NULL;
LViewI *Notify = NULL;
// Size
LPoint MinimumSize;
// Font
LFont *Font = NULL;
LViewFontType FontOwnType = GV_FontPtr;
// Style
LAutoPtr Css;
bool CssDirty = false; // This is set when 'Styles' changes, the next call to GetCss(...) parses
// the styles into the 'Css' object.
LString Styles; // Somewhat temporary object to store unparsed styles particular to
// this view until runtime, where the view heirarchy is known.
LString::Array Classes;
// Event dispatch handle
int SinkHnd = -1;
// OS Specific
#if WINNATIVE
static OsView hPrevCapture;
int WndStyle = 0; // Windows hWnd Style
int WndExStyle = 0; // Windows hWnd ExStyle
int WndDlgCode = 0; // Windows Dialog Code (WM_GETDLGCODE)
LString WndClass;
UINT_PTR TimerId = 0;
HTHEME hTheme = NULL;
#else
// Cursor
LPulseThread *PulseThread = NULL;
LView *Popup = NULL;
bool TabStop = false;
bool WantsFocus = false;
#if defined __GTK_H__
bool InPaint = false;
bool GotOnCreate = false;
#elif defined(MAC) && !defined(LGI_COCOA)
static HIObjectClassRef BaseClass;
#endif
#endif
#if defined(__GTK_H__)
static LCaptureThread *CaptureThread;
LMouse PrevMouse;
#elif defined(MAC)
#ifdef LGI_COCOA
LString ClassName;
bool AttachEvent;
#elif defined LGI_CARBON
EventHandlerRef DndHandler;
LAutoString AcceptedDropFormat;
#endif
#elif defined(LGI_SDL)
SDL_TimerID PulseId;
int PulseLength = -1;
#elif defined(HAIKU)
BView *Hnd = NULL;
LArray MsgQue; // For before the window is attached...
#endif
LViewPrivate(LView *view);
~LViewPrivate();
LView *GetParent()
{
if (Parent)
return Parent;
if (ParentI)
return ParentI->GetGView();
return 0;
}
};
diff --git a/src/common/Lgi/ViewCommon.cpp b/src/common/Lgi/ViewCommon.cpp
--- a/src/common/Lgi/ViewCommon.cpp
+++ b/src/common/Lgi/ViewCommon.cpp
@@ -1,2711 +1,2707 @@
/// \file
/// \author Matthew Allen
#ifdef LINUX
#include
#endif
#include "lgi/common/Lgi.h"
#include "lgi/common/DragAndDrop.h"
#include "lgi/common/TableLayout.h"
#include "lgi/common/Button.h"
#include "lgi/common/Css.h"
#include "lgi/common/LgiRes.h"
#include "lgi/common/EventTargetThread.h"
#include "lgi/common/Popup.h"
#include "lgi/common/CssTools.h"
#include "ViewPriv.h"
#if 0
#define DEBUG_CAPTURE(...) printf(__VA_ARGS__)
#else
#define DEBUG_CAPTURE(...)
#endif
-#if !WINNATIVE
-int LPulseThread::PulseThreadCount = 0;
-#endif
-
//////////////////////////////////////////////////////////////////////////////////////
// Helper
LPoint lgi_view_offset(LViewI *v, bool Debug = false)
{
LPoint Offset;
for (LViewI *p = v; p; p = p->GetParent())
{
if (dynamic_cast(p))
break;
LRect pos = p->GetPos();
LRect cli = p->GetClient(false);
if (Debug)
{
const char *cls = p->GetClass();
LgiTrace(" Off[%s] += %i,%i = %i,%i (%s)\n",
cls,
pos.x1, pos.y1,
Offset.x + pos.x1, Offset.y + pos.y1,
cli.GetStr());
}
Offset.x += pos.x1 + cli.x1;
Offset.y += pos.y1 + cli.y1;
}
return Offset;
}
LMouse &lgi_adjust_click(LMouse &Info, LViewI *Wnd, bool Capturing, bool Debug)
{
static LMouse Temp;
Temp = Info;
if (Wnd)
{
if (Debug
#if 0
|| Capturing
#endif
)
LgiTrace("AdjustClick Target=%s -> Wnd=%s, Info=%i,%i\n",
Info.Target?Info.Target->GetClass():"",
Wnd?Wnd->GetClass():"",
Info.x, Info.y);
if (Temp.Target != Wnd)
{
if (Temp.Target)
{
auto *TargetWnd = Temp.Target->GetWindow();
auto *WndWnd = Wnd->GetWindow();
if (TargetWnd == WndWnd)
{
LPoint TargetOff = lgi_view_offset(Temp.Target, Debug);
if (Debug)
LgiTrace(" WndOffset:\n");
LPoint WndOffset = lgi_view_offset(Wnd, Debug);
Temp.x += TargetOff.x - WndOffset.x;
Temp.y += TargetOff.y - WndOffset.y;
#if 0
LRect c = Wnd->GetClient(false);
Temp.x -= c.x1;
Temp.y -= c.y1;
if (Debug)
LgiTrace(" CliOff -= %i,%i\n", c.x1, c.y1);
#endif
Temp.Target = Wnd;
}
}
else
{
LPoint Offset;
Temp.Target = Wnd;
if (Wnd->WindowVirtualOffset(&Offset))
{
LRect c = Wnd->GetClient(false);
Temp.x -= Offset.x + c.x1;
Temp.y -= Offset.y + c.y1;
}
}
}
}
LAssert(Temp.Target != NULL);
return Temp;
}
//////////////////////////////////////////////////////////////////////////////////////
// LView class methods
LViewI *LView::_Capturing = 0;
LViewI *LView::_Over = 0;
#if LGI_VIEW_HASH
struct ViewTbl : public LMutex
{
typedef LHashTbl, int> T;
private:
T Map;
public:
ViewTbl() : Map(2000), LMutex("ViewTbl")
{
}
T *Lock()
{
if (!LMutex::Lock(_FL))
return NULL;
return ⤅
}
} ViewTblInst;
bool LView::LockHandler(LViewI *v, LView::LockOp Op)
{
ViewTbl::T *m = ViewTblInst.Lock();
if (!m)
return false;
int Ref = m->Find(v);
bool Status = false;
switch (Op)
{
case OpCreate:
{
if (Ref == 0)
Status = m->Add(v, 1);
else
LAssert(!"Already exists?");
break;
}
case OpDelete:
{
if (Ref == 1)
Status = m->Delete(v);
else
LAssert(!"Either locked or missing.");
break;
}
case OpExists:
{
Status = Ref > 0;
break;
}
}
ViewTblInst.Unlock();
return Status;
}
#endif
LView::LView(OsView view)
{
#ifdef _DEBUG
_Debug = false;
#endif
d = new LViewPrivate(this);
#ifdef LGI_SDL
_View = this;
#elif LGI_VIEW_HANDLE && !defined(HAIKU)
_View = view;
#endif
Pos.ZOff(-1, -1);
WndFlags = GWF_VISIBLE;
#ifndef LGI_VIEW_HASH
#error "LGI_VIEW_HASH needs to be defined"
#elif LGI_VIEW_HASH
LockHandler(this, OpCreate);
// printf("Adding %p to hash\n", (LViewI*)this);
#endif
}
LView::~LView()
{
if (d->SinkHnd >= 0)
{
LEventSinkMap::Dispatch.RemoveSink(this);
d->SinkHnd = -1;
}
#if LGI_VIEW_HASH
LockHandler(this, OpDelete);
#endif
for (unsigned i=0; iOwner == this)
pu->Owner = NULL;
}
_Delete();
// printf("%p::~LView delete %p th=%u\n", this, d, GetCurrentThreadId());
DeleteObj(d);
// printf("%p::~LView\n", this);
}
int LView::AddDispatch()
{
if (d->SinkHnd < 0)
d->SinkHnd = LEventSinkMap::Dispatch.AddSink(this);
return d->SinkHnd;
}
LString LView::CssStyles(const char *Set)
{
if (Set)
{
d->Styles = Set;
d->CssDirty = true;
}
return d->Styles;
}
LString::Array *LView::CssClasses()
{
return &d->Classes;
}
LArray LView::IterateViews()
{
LArray a;
for (auto c: Children)
a.Add(c);
return a;
}
bool LView::AddView(LViewI *v, int Where)
{
LAssert(!Children.HasItem(v));
bool Add = Children.Insert(v, Where);
if (Add)
{
LView *gv = v->GetGView();
if (gv && gv->_Window != _Window)
{
LAssert(!_InLock);
gv->_Window = _Window;
}
v->SetParent(this);
v->OnAttach();
OnChildrenChanged(v, true);
}
return Add;
}
bool LView::DelView(LViewI *v)
{
bool Has = Children.HasItem(v);
bool b = Children.Delete(v);
if (Has)
OnChildrenChanged(v, false);
Has = Children.HasItem(v);
LAssert(!Has);
return b;
}
bool LView::HasView(LViewI *v)
{
return Children.HasItem(v);
}
OsWindow LView::WindowHandle()
{
auto w = GetWindow();
auto h = w ? w->WindowHandle() : NULL;
return h;
}
LWindow *LView::GetWindow()
{
if (!_Window)
{
// Walk up parent list and find someone who has a window
auto *w = d->GetParent();
for (; w; w = w->d ? w->d->GetParent() : NULL)
{
if (w->_Window)
{
LAssert(!_InLock);
_Window = w->_Window;
break;
}
}
}
return dynamic_cast(_Window);
}
bool LView::Lock(const char *file, int line, int TimeOut)
{
#ifdef HAIKU
bool Debug = !Stricmp("LList", GetClass());
if (!d || !d->Hnd)
{
if (Debug)
printf("%s:%i - no handle %p %p\n", _FL, d, d ? d->Hnd : NULL);
return false;
}
if (d->Hnd->Parent() == NULL)
{
if (Debug)
printf("%s:%p - Lock() no parent.\n", GetClass(), this);
return true;
}
if (TimeOut >= 0)
{
auto r = d->Hnd->LockLooperWithTimeout(TimeOut * 1000);
if (r == B_OK)
{
_InLock++;
if (Debug)
printf("%s:%p - Lock() cnt=%i par=%p.\n", GetClass(), this, _InLock, d->Hnd->Parent());
return true;
}
printf("%s:%i - Lock(%i) failed with %x.\n", _FL, TimeOut, r);
return false;
}
auto r = d->Hnd->LockLooper();
if (r)
{
_InLock++;
if (Debug)
{
auto w = WindowHandle();
printf("%s:%p - Lock() cnt=%i myThread=%i wndThread=%i.\n",
GetClass(),
this,
_InLock,
GetCurrentThreadId(),
w ? w->Thread() : -1);
}
return true;
}
if (Debug)
printf("%s:%i - Lock(%s:%i) failed.\n", _FL, file, line);
return false;
#else
if (!_Window)
GetWindow();
_InLock++;
// LgiTrace("%s::%p Lock._InLock=%i %s:%i\n", GetClass(), this, _InLock, file, line);
if (_Window && _Window->_Lock)
{
if (TimeOut < 0)
{
return _Window->_Lock->Lock(file, line);
}
else
{
return _Window->_Lock->LockWithTimeout(TimeOut, file, line);
}
}
return true;
#endif
}
void LView::Unlock()
{
#ifdef HAIKU
if (!d || !d->Hnd)
{
printf("%s:%i - Unlock() error, no hnd.\n", _FL);
return;
}
if (!d->Hnd->Parent())
{
// printf("%s:%p - Unlock() no parent.\n", GetClass(), this);
return;
}
if (_InLock > 0)
{
// printf("%s:%p - Calling UnlockLooper: %i.\n", GetClass(), this, _InLock);
d->Hnd->UnlockLooper();
_InLock--;
// printf("%s:%p - UnlockLooper done: %i.\n", GetClass(), this, _InLock);
}
else
{
printf("%s:%i - Unlock() without lock.\n", _FL);
}
#else
if (_Window &&
_Window->_Lock)
{
_Window->_Lock->Unlock();
}
_InLock--;
// LgiTrace("%s::%p Unlock._InLock=%i\n", GetClass(), this, _InLock);
#endif
}
void LView::OnMouseClick(LMouse &m)
{
}
void LView::OnMouseEnter(LMouse &m)
{
}
void LView::OnMouseExit(LMouse &m)
{
}
void LView::OnMouseMove(LMouse &m)
{
}
bool LView::OnMouseWheel(double Lines)
{
return false;
}
bool LView::OnKey(LKey &k)
{
return false;
}
void LView::OnAttach()
{
List::I it = Children.begin();
for (LViewI *v = *it; v; v = *++it)
{
if (!v->GetParent())
v->SetParent(this);
}
#if 0 // defined __GTK_H__
if (_View && !DropTarget())
{
// If one of our parents is drop capable we need to set a dest here
LViewI *p;
for (p = GetParent(); p; p = p->GetParent())
{
if (p->DropTarget())
{
break;
}
}
if (p)
{
Gtk::gtk_drag_dest_set( _View,
(Gtk::GtkDestDefaults)0,
NULL,
0,
Gtk::GDK_ACTION_DEFAULT);
// printf("%s:%i - Drop dest for '%s'\n", _FL, GetClass());
}
else
{
Gtk::gtk_drag_dest_unset(_View);
// printf("%s:%i - Not setting drop dest '%s'\n", _FL, GetClass());
}
}
#endif
}
void LView::OnCreate()
{
}
void LView::OnDestroy()
{
}
void LView::OnFocus(bool f)
{
// printf("%s::OnFocus(%i)\n", GetClass(), f);
}
void LView::OnPulse()
{
}
void LView::OnPosChange()
{
}
bool LView::OnRequestClose(bool OsShuttingDown)
{
return true;
}
int LView::OnHitTest(int x, int y)
{
return -1;
}
void LView::OnChildrenChanged(LViewI *Wnd, bool Attaching)
{
}
void LView::OnPaint(LSurface *pDC)
{
auto c = GetClient();
LCssTools Tools(this);
Tools.PaintContent(pDC, c);
}
int LView::OnNotify(LViewI *Ctrl, LNotification Data)
{
if (!Ctrl)
return 0;
if (Ctrl == (LViewI*)this && Data.Type == LNotifyActivate)
{
// Default activation is to focus the current control.
Focus(true);
}
else if (d && d->Parent)
{
// default behaviour is just to pass the
// notification up to the parent
// FIXME: eventually we need to call the 'LNotification' parent fn...
return d->Parent->OnNotify(Ctrl, Data);
}
return 0;
}
int LView::OnCommand(int Cmd, int Event, OsView Wnd)
{
return 0;
}
void LView::OnNcPaint(LSurface *pDC, LRect &r)
{
int Border = Sunken() || Raised() ? _BorderSize : 0;
if (Border == 2)
{
LEdge e;
if (Sunken())
e = Focus() ? EdgeWin7FocusSunken : DefaultSunkenEdge;
else
e = DefaultRaisedEdge;
#if WINNATIVE
if (d->IsThemed)
DrawThemeBorder(pDC, r);
else
#endif
LWideBorder(pDC, r, e);
}
else if (Border == 1)
{
LThinBorder(pDC, r, Sunken() ? DefaultSunkenEdge : DefaultRaisedEdge);
}
}
#if LGI_COCOA || defined(__GTK_H__)
/*
uint64 nPaint = 0;
uint64 PaintTime = 0;
*/
void LView::_Paint(LSurface *pDC, LPoint *Offset, LRect *Update)
{
/*
uint64 StartTs = Update ? LCurrentTime() : 0;
d->InPaint = true;
*/
// Create temp DC if needed...
LAutoPtr Local;
if (!pDC)
{
if (!Local.Reset(new LScreenDC(this)))
return;
pDC = Local;
}
#if 0
// This is useful for coverage checking
pDC->Colour(LColour(255, 0, 255));
pDC->Rectangle();
#endif
// Non-Client drawing
LRect r;
if (Offset)
{
r = Pos;
r.Offset(Offset);
}
else
{
r = GetClient().ZeroTranslate();
}
pDC->SetClient(&r);
LRect zr1 = r.ZeroTranslate(), zr2 = zr1;
OnNcPaint(pDC, zr1);
pDC->SetClient(NULL);
if (zr2 != zr1)
{
r.x1 -= zr2.x1 - zr1.x1;
r.y1 -= zr2.y1 - zr1.y1;
r.x2 -= zr2.x2 - zr1.x2;
r.y2 -= zr2.y2 - zr1.y2;
}
LPoint o(r.x1, r.y1); // Origin of client
// Paint this view's contents...
pDC->SetClient(&r);
#if 0
if (_Debug)
{
#if defined(__GTK_H__)
Gtk::cairo_matrix_t matrix;
cairo_get_matrix(pDC->Handle(), &matrix);
double ex[4];
cairo_clip_extents(pDC->Handle(), ex+0, ex+1, ex+2, ex+3);
ex[0] += matrix.x0; ex[1] += matrix.y0; ex[2] += matrix.x0; ex[3] += matrix.y0;
LgiTrace("%s::_Paint, r=%s, clip=%g,%g,%g,%g - %g,%g\n",
GetClass(), r.GetStr(),
ex[0], ex[1], ex[2], ex[3],
matrix.x0, matrix.y0);
#elif LGI_COCOA
auto Ctx = pDC->Handle();
CGAffineTransform t = CGContextGetCTM(Ctx);
LRect cr = CGContextGetClipBoundingBox(Ctx);
printf("%s::_Paint() pos=%s transform=%g,%g,%g,%g-%g,%g clip=%s r=%s\n",
GetClass(),
GetPos().GetStr(),
t.a, t.b, t.c, t.d, t.tx, t.ty,
cr.GetStr(),
r.GetStr());
#endif
}
#endif
OnPaint(pDC);
pDC->SetClient(NULL);
// Paint all the children...
for (auto i : Children)
{
LView *w = i->GetGView();
if (w && w->Visible())
{
if (!w->Pos.Valid())
continue;
#if 0
if (w->_Debug)
LgiTrace("%s::_Paint %i,%i\n", w->GetClass(), o.x, o.y);
#endif
w->_Paint(pDC, &o);
}
}
}
#else
void LView::_Paint(LSurface *pDC, LPoint *Offset, LRect *Update)
{
// Create temp DC if needed...
LAutoPtr Local;
if (!pDC)
{
Local.Reset(new LScreenDC(this));
pDC = Local;
}
if (!pDC)
{
printf("%s:%i - No context to draw in.\n", _FL);
return;
}
#if 0
// This is useful for coverage checking
pDC->Colour(LColour(255, 0, 255));
pDC->Rectangle();
#endif
bool HasClient = false;
LRect r(0, 0, Pos.X()-1, Pos.Y()-1), Client;
LPoint o;
if (Offset)
o = *Offset;
#if WINNATIVE
if (!_View)
#endif
{
// Non-Client drawing
Client = r;
OnNcPaint(pDC, Client);
HasClient = GetParent() && (Client != r);
if (HasClient)
{
Client.Offset(o.x, o.y);
pDC->SetClient(&Client);
}
}
r.Offset(o.x, o.y);
// Paint this view's contents
if (Update)
{
LRect OldClip = pDC->ClipRgn();
pDC->ClipRgn(Update);
OnPaint(pDC);
pDC->ClipRgn(OldClip.Valid() ? &OldClip : NULL);
}
else
{
OnPaint(pDC);
}
#if PAINT_VIRTUAL_CHILDREN
// Paint any virtual children
for (auto i : Children)
{
LView *w = i->GetGView();
if (w && w->Visible())
{
#if LGI_VIEW_HANDLE
if (!w->Handle())
#endif
{
LRect p = w->GetPos();
p.Offset(o.x, o.y);
if (HasClient)
p.Offset(Client.x1 - r.x1, Client.y1 - r.y1);
LPoint co(p.x1, p.y1);
// LgiTrace("%s::_Paint %i,%i\n", w->GetClass(), p.x1, p.y1);
pDC->SetClient(&p);
w->_Paint(pDC, &co);
pDC->SetClient(NULL);
}
}
}
#endif
if (HasClient)
pDC->SetClient(0);
}
#endif
LViewI *LView::GetParent()
{
ThreadCheck();
return d ? d->Parent : NULL;
}
void LView::SetParent(LViewI *p)
{
ThreadCheck();
d->Parent = p ? p->GetGView() : NULL;
d->ParentI = p;
}
void LView::SendNotify(LNotification note)
{
LViewI *n = d->Notify ? d->Notify : d->Parent;
if (n)
{
if (
#if LGI_VIEW_HANDLE && !defined(HAIKU)
!_View ||
#endif
InThread())
{
n->OnNotify(this, note);
}
else
{
// We are not in the same thread as the target window. So we post a message
// across to the view.
if (GetId() <= 0)
{
// We are going to generate a control Id to help the receiver of the
// M_CHANGE message find out view later on. The reason for doing this
// instead of sending a pointer to the object, is that the object
// _could_ be deleted between the message being sent and being received.
// Which would result in an invalid memory access on that object.
LViewI *p = GetWindow();
if (!p)
{
// No window? Find the top most parent we can...
p = this;
while (p->GetParent())
p = p->GetParent();
}
if (p)
{
// Give the control a valid ID
int i;
for (i=10; i<1000; i++)
{
if (!p->FindControl(i))
{
printf("Giving the ctrl '%s' the id '%i' for SendNotify\n",
GetClass(),
i);
SetId(i);
break;
}
}
}
else
{
// Ok this is really bad... go random (better than nothing)
SetId(5000 + LRand(2000));
}
}
LAssert(GetId() > 0); // We must have a valid ctrl ID at this point, otherwise
// the receiver will never be able to find our object.
// printf("Post M_CHANGE %i %i\n", GetId(), Data);
n->PostEvent(M_CHANGE, (LMessage::Param) GetId(), (LMessage::Param) new LNotification(note));
}
}
}
LViewI *LView::GetNotify()
{
ThreadCheck();
return d->Notify;
}
void LView::SetNotify(LViewI *p)
{
ThreadCheck();
d->Notify = p;
}
#define ADJ_LEFT 1
#define ADJ_RIGHT 2
#define ADJ_UP 3
#define ADJ_DOWN 4
int IsAdjacent(LRect &a, LRect &b)
{
if ( (a.x1 == b.x2 + 1) &&
!(a.y1 > b.y2 || a.y2 < b.y1))
{
return ADJ_LEFT;
}
if ( (a.x2 == b.x1 - 1) &&
!(a.y1 > b.y2 || a.y2 < b.y1))
{
return ADJ_RIGHT;
}
if ( (a.y1 == b.y2 + 1) &&
!(a.x1 > b.x2 || a.x2 < b.x1))
{
return ADJ_UP;
}
if ( (a.y2 == b.y1 - 1) &&
!(a.x1 > b.x2 || a.x2 < b.x1))
{
return ADJ_DOWN;
}
return 0;
}
LRect JoinAdjacent(LRect &a, LRect &b, int Adj)
{
LRect t;
switch (Adj)
{
case ADJ_LEFT:
case ADJ_RIGHT:
{
t.y1 = MAX(a.y1, b.y1);
t.y2 = MIN(a.y2, b.y2);
t.x1 = MIN(a.x1, b.x1);
t.x2 = MAX(a.x2, b.x2);
break;
}
case ADJ_UP:
case ADJ_DOWN:
{
t.y1 = MIN(a.y1, b.y1);
t.y2 = MAX(a.y2, b.y2);
t.x1 = MAX(a.x1, b.x1);
t.x2 = MIN(a.x2, b.x2);
break;
}
}
return t;
}
LRect *LView::FindLargest(LRegion &r)
{
ThreadCheck();
int Pixels = 0;
LRect *Best = 0;
static LRect Final;
// do initial run through the list to find largest single region
for (LRect *i = r.First(); i; i = r.Next())
{
int Pix = i->X() * i->Y();
if (Pix > Pixels)
{
Pixels = Pix;
Best = i;
}
}
if (Best)
{
Final = *Best;
Pixels = Final.X() * Final.Y();
int LastPixels = Pixels;
LRect LastRgn = Final;
int ThisPixels = Pixels;
LRect ThisRgn = Final;
LRegion TempList;
for (LRect *i = r.First(); i; i = r.Next())
{
TempList.Union(i);
}
TempList.Subtract(Best);
do
{
LastPixels = ThisPixels;
LastRgn = ThisRgn;
// search for adjoining rectangles that maybe we can add
for (LRect *i = TempList.First(); i; i = TempList.Next())
{
int Adj = IsAdjacent(ThisRgn, *i);
if (Adj)
{
LRect t = JoinAdjacent(ThisRgn, *i, Adj);
int Pix = t.X() * t.Y();
if (Pix > ThisPixels)
{
ThisPixels = Pix;
ThisRgn = t;
TempList.Subtract(i);
}
}
}
} while (LastPixels < ThisPixels);
Final = ThisRgn;
}
else return 0;
return &Final;
}
LRect *LView::FindSmallestFit(LRegion &r, int Sx, int Sy)
{
ThreadCheck();
int X = 1000000;
int Y = 1000000;
LRect *Best = 0;
for (LRect *i = r.First(); i; i = r.Next())
{
if ((i->X() >= Sx && i->Y() >= Sy) &&
(i->X() < X || i->Y() < Y))
{
X = i->X();
Y = i->Y();
Best = i;
}
}
return Best;
}
LRect *LView::FindLargestEdge(LRegion &r, int Edge)
{
LRect *Best = 0;
ThreadCheck();
for (LRect *i = r.First(); i; i = r.Next())
{
if (!Best)
{
Best = i;
}
if
(
((Edge & GV_EDGE_TOP) && (i->y1 < Best->y1))
||
((Edge & GV_EDGE_RIGHT) && (i->x2 > Best->x2))
||
((Edge & GV_EDGE_BOTTOM) && (i->y2 > Best->y2))
||
((Edge & GV_EDGE_LEFT) && (i->x1 < Best->x1))
)
{
Best = i;
}
if
(
(
((Edge & GV_EDGE_TOP) && (i->y1 == Best->y1))
||
((Edge & GV_EDGE_BOTTOM) && (i->y2 == Best->y2))
)
&&
(
i->X() > Best->X()
)
)
{
Best = i;
}
if
(
(
((Edge & GV_EDGE_RIGHT) && (i->x2 == Best->x2))
||
((Edge & GV_EDGE_LEFT) && (i->x1 == Best->x1))
)
&&
(
i->Y() > Best->Y()
)
)
{
Best = i;
}
}
return Best;
}
LViewI *LView::FindReal(LPoint *Offset)
{
ThreadCheck();
if (Offset)
{
Offset->x = 0;
Offset->y = 0;
}
#if !LGI_VIEW_HANDLE
LViewI *w = GetWindow();
#endif
LViewI *p = d->Parent;
while (p &&
#if !LGI_VIEW_HANDLE
p != w
#else
!p->Handle()
#endif
)
{
if (Offset)
{
Offset->x += Pos.x1;
Offset->y += Pos.y1;
}
p = p->GetParent();
}
if (p &&
#if !LGI_VIEW_HANDLE
p == w
#else
p->Handle()
#endif
)
{
return p;
}
return NULL;
}
bool LView::HandleCapture(LView *Wnd, bool c)
{
ThreadCheck();
DEBUG_CAPTURE("%s::HandleCapture(%i)=%i\n", GetClass(), c, (int)(_Capturing == Wnd));
if (c)
{
if (_Capturing == Wnd)
{
DEBUG_CAPTURE(" %s already has capture\n", _Capturing?_Capturing->GetClass():0);
}
else
{
DEBUG_CAPTURE(" _Capturing=%s -> %s\n", _Capturing?_Capturing->GetClass():0, Wnd?Wnd->GetClass():0);
_Capturing = Wnd;
#if defined(__GTK_H__)
if (d->CaptureThread)
d->CaptureThread->Cancel();
d->CaptureThread = new LCaptureThread(this);
#elif WINNATIVE
LPoint Offset;
LViewI *v = _Capturing->Handle() ? _Capturing : FindReal(&Offset);
HWND h = v ? v->Handle() : NULL;
if (h)
SetCapture(h);
else
LAssert(0);
#elif defined(LGI_SDL)
#if SDL_VERSION_ATLEAST(2, 0, 4)
SDL_CaptureMouse(SDL_TRUE);
#else
LAppInst->CaptureMouse(true);
#endif
#endif
}
}
else if (_Capturing)
{
DEBUG_CAPTURE(" _Capturing=%s -> NULL\n", _Capturing?_Capturing->GetClass():0);
_Capturing = NULL;
#if defined(__GTK_H__)
if (d->CaptureThread)
{
d->CaptureThread->Cancel();
d->CaptureThread = NULL; // It'll delete itself...
}
#elif WINNATIVE
ReleaseCapture();
#elif defined(LGI_SDL)
#if SDL_VERSION_ATLEAST(2, 0, 4)
SDL_CaptureMouse(SDL_FALSE);
#else
LAppInst->CaptureMouse(false);
#endif
#endif
}
return true;
}
bool LView::IsCapturing()
{
// DEBUG_CAPTURE("%s::IsCapturing()=%p==%p==%i\n", GetClass(), _Capturing, this, (int)(_Capturing == this));
return _Capturing == this;
}
bool LView::Capture(bool c)
{
ThreadCheck();
DEBUG_CAPTURE("%s::Capture(%i)\n", GetClass(), c);
return HandleCapture(this, c);
}
bool LView::Enabled()
{
ThreadCheck();
#if WINNATIVE
if (_View)
return IsWindowEnabled(_View) != 0;
#endif
return !TestFlag(GViewFlags, GWF_DISABLED);
}
void LView::Enabled(bool i)
{
ThreadCheck();
if (!i) SetFlag(GViewFlags, GWF_DISABLED);
else ClearFlag(GViewFlags, GWF_DISABLED);
#if LGI_VIEW_HANDLE && !defined(HAIKU)
if (_View)
{
#if WINNATIVE
EnableWindow(_View, i);
#elif defined LGI_CARBON
if (i)
{
OSStatus e = EnableControl(_View);
if (e) printf("%s:%i - Error enabling control (%i)\n", _FL, (int)e);
}
else
{
OSStatus e = DisableControl(_View);
if (e) printf("%s:%i - Error disabling control (%i)\n", _FL, (int)e);
}
#endif
}
#endif
Invalidate();
}
bool LView::Visible()
{
// This is a read only operation... which is kinda thread-safe...
// ThreadCheck();
#if WINNATIVE
if (_View)
/* This takes into account all the parent windows as well...
Which is kinda not what I want. I want this to reflect just
this window.
return IsWindowVisible(_View);
*/
return (GetWindowLong(_View, GWL_STYLE) & WS_VISIBLE) != 0;
#endif
return TestFlag(GViewFlags, GWF_VISIBLE);
}
void LView::Visible(bool v)
{
ThreadCheck();
if (v) SetFlag(GViewFlags, GWF_VISIBLE);
else ClearFlag(GViewFlags, GWF_VISIBLE);
#if defined(HAIKU)
LLocker lck(d->Hnd, _FL);
if (!IsAttached() || lck.Lock())
{
const int attempts = 3;
// printf("%s/%p:Visible(%i) hidden=%i\n", GetClass(), this, v, d->Hnd->IsHidden());
if (v)
{
bool parentHidden = false;
for (auto p = d->Hnd->Parent(); p; p = p->Parent())
{
if (p->IsHidden())
{
parentHidden = true;
break;
}
}
if (!parentHidden) // Don't try and show if one of the parent's is hidden.
{
for (int i=0; iHnd->IsHidden(); i++)
{
// printf("\t%p Show\n", this);
d->Hnd->Show();
}
if (d->Hnd->IsHidden())
{
printf("%s:%i - Failed to show %s.\n", _FL, GetClass());
for (auto p = d->Hnd->Parent(); p; p = p->Parent())
printf("\tparent: %s/%p ishidden=%i\n", p->Name(), p, p->IsHidden());
}
}
}
else
{
for (int i=0; iHnd->IsHidden(); i++)
{
// printf("\t%p Hide\n", this);
d->Hnd->Hide();
}
if (!d->Hnd->IsHidden())
{
printf("%s:%i - Failed to hide %s.\n", _FL, GetClass());
for (auto p = d->Hnd->Parent(); p; p = p->Parent())
printf("\tparent: %s/%p ishidden=%i\n", p->Name(), p, p->IsHidden());
}
}
// printf("\t%s/%p:Visible(%i) hidden=%i\n", GetClass(), this, v, d->Hnd->IsHidden());
}
else LgiTrace("%s:%i - Can't lock.\n", _FL);
#elif LGI_VIEW_HANDLE
if (_View)
{
#if WINNATIVE
ShowWindow(_View, (v) ? SW_SHOWNORMAL : SW_HIDE);
#elif LGI_COCOA
LAutoPool Pool;
[_View.p setHidden:!v];
#elif LGI_CARBON
Boolean is = HIViewIsVisible(_View);
if (v != is)
{
OSErr e = HIViewSetVisible(_View, v);
if (e) printf("%s:%i - HIViewSetVisible(%p,%i) failed with %i (class=%s)\n",
_FL, _View, v, e, GetClass());
}
#endif
}
else
#endif
{
Invalidate();
}
}
bool LView::Focus()
{
ThreadCheck();
bool Has = false;
#if defined(__GTK_H__)
LWindow *w = GetWindow();
if (w)
{
bool Active = w->IsActive();
if (Active)
Has = w->GetFocus() == static_cast(this);
}
#elif defined(HAIKU)
LLocker lck(d->Hnd, _FL);
if (lck.Lock())
{
Has = d->Hnd->IsFocus();
lck.Unlock();
}
#elif defined(WINNATIVE)
if (_View)
{
HWND hFocus = GetFocus();
Has = hFocus == _View;
}
#elif LGI_COCOA
Has = TestFlag(WndFlags, GWF_FOCUS);
#elif LGI_CARBON
LWindow *w = GetWindow();
if (w)
{
ControlRef Cur;
OSErr e = GetKeyboardFocus(w->WindowHandle(), &Cur);
if (e)
LgiTrace("%s:%i - GetKeyboardFocus failed with %i\n", _FL, e);
else
Has = (Cur == _View);
}
#endif
#if !LGI_CARBON
if (Has)
SetFlag(WndFlags, GWF_FOCUS);
else
ClearFlag(WndFlags, GWF_FOCUS);
#endif
return Has;
}
void LView::Focus(bool i)
{
ThreadCheck();
if (i)
SetFlag(WndFlags, GWF_FOCUS);
else
ClearFlag(WndFlags, GWF_FOCUS);
auto *Wnd = GetWindow();
if (Wnd)
{
Wnd->SetFocus(this, i ? LWindow::GainFocus : LWindow::LoseFocus);
}
#if LGI_VIEW_HANDLE && !defined(HAIKU)
if (_View)
#endif
{
#if defined(HAIKU)
_Focus(i);
#elif defined(LGI_SDL) || defined(__GTK_H__)
// Nop: Focus is all handled by Lgi's LWindow class.
#elif WINNATIVE
if (i)
{
HWND hCur = GetFocus();
if (hCur != _View)
{
if (In_SetWindowPos)
{
assert(0);
LgiTrace("%s:%i - SetFocus %p (%-30s)\n", _FL, Handle(), Name());
}
SetFocus(_View);
}
}
else
{
if (In_SetWindowPos)
{
assert(0);
LgiTrace("%s:%i - SetFocus(%p)\n", _FL, GetDesktopWindow());
}
SetFocus(GetDesktopWindow());
}
#elif defined LGI_CARBON
LViewI *Wnd = GetWindow();
if (Wnd && i)
{
OSErr e = SetKeyboardFocus(Wnd->WindowHandle(), _View, 1);
if (e)
{
// e = SetKeyboardFocus(Wnd->WindowHandle(), _View, kControlFocusNextPart);
// if (e)
{
HIViewRef p = HIViewGetSuperview(_View);
// errCouldntSetFocus
printf("%s:%i - SetKeyboardFocus failed: %i (%s, %p)\n", _FL, e, GetClass(), p);
}
}
// else printf("%s:%i - SetFocus v=%p(%s)\n", _FL, _View, GetClass());
}
else printf("%s:%i - no window?\n", _FL);
#endif
}
}
LDragDropSource *LView::DropSource(LDragDropSource *Set)
{
if (Set)
d->DropSource = Set;
return d->DropSource;
}
LDragDropTarget *LView::DropTarget(LDragDropTarget *Set)
{
if (Set)
d->DropTarget = Set;
return d->DropTarget;
}
#if defined LGI_CARBON
extern pascal OSStatus LgiViewDndHandler(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData);
#endif
#if defined __GTK_H__
// Recursively add drag dest to all view and all children
bool GtkAddDragDest(LViewI *v, bool IsTarget)
{
if (!v) return false;
LWindow *w = v->GetWindow();
if (!w) return false;
auto wid = GtkCast(w->WindowHandle(), gtk_widget, GtkWidget);
if (IsTarget)
{
Gtk::gtk_drag_dest_set( wid,
(Gtk::GtkDestDefaults)0,
NULL,
0,
Gtk::GDK_ACTION_DEFAULT);
}
else
{
Gtk::gtk_drag_dest_unset(wid);
}
for (LViewI *c: v->IterateViews())
GtkAddDragDest(c, IsTarget);
return true;
}
#endif
bool LView::DropTarget(bool t)
{
ThreadCheck();
bool Status = false;
if (t) SetFlag(GViewFlags, GWF_DROP_TARGET);
else ClearFlag(GViewFlags, GWF_DROP_TARGET);
#if WINNATIVE
if (_View)
{
if (t)
{
if (!d->DropTarget)
DragAcceptFiles(_View, t);
else
Status = RegisterDragDrop(_View, (IDropTarget*) d->DropTarget) == S_OK;
}
else
{
if (_View && d->DropTarget)
Status = RevokeDragDrop(_View) == S_OK;
}
}
#elif defined MAC && !defined(LGI_SDL)
LWindow *Wnd = dynamic_cast(GetWindow());
if (Wnd)
{
Wnd->SetDragHandlers(t);
if (!d->DropTarget)
d->DropTarget = t ? Wnd : 0;
}
#if LGI_COCOA
LWindow *w = GetWindow();
if (w)
{
OsWindow h = w->WindowHandle();
if (h)
{
NSMutableArray *a = [[NSMutableArray alloc] init];
if (a)
{
[a addObject:(NSString*)kUTTypeItem];
for (id item in NSFilePromiseReceiver.readableDraggedTypes)
[a addObject:item];
[h.p.contentView registerForDraggedTypes:a];
[a release];
}
}
}
#elif LGI_CARBON
if (t)
{
static EventTypeSpec DragEvents[] =
{
{ kEventClassControl, kEventControlDragEnter },
{ kEventClassControl, kEventControlDragWithin },
{ kEventClassControl, kEventControlDragLeave },
{ kEventClassControl, kEventControlDragReceive },
};
if (!d->DndHandler)
{
OSStatus e = ::InstallControlEventHandler( _View,
NewEventHandlerUPP(LgiViewDndHandler),
GetEventTypeCount(DragEvents),
DragEvents,
(void*)this,
&d->DndHandler);
if (e) LgiTrace("%s:%i - InstallEventHandler failed (%i)\n", _FL, e);
}
SetControlDragTrackingEnabled(_View, true);
}
else
{
SetControlDragTrackingEnabled(_View, false);
}
#endif
#elif defined __GTK_H__
Status = GtkAddDragDest(this, t);
if (Status && !d->DropTarget)
d->DropTarget = t ? GetWindow() : 0;
#endif
return Status;
}
bool LView::Sunken()
{
// ThreadCheck();
#if WINNATIVE
return TestFlag(d->WndExStyle, WS_EX_CLIENTEDGE);
#else
return TestFlag(GViewFlags, GWF_SUNKEN);
#endif
}
void LView::Sunken(bool i)
{
ThreadCheck();
#if WINNATIVE
if (i) SetFlag(d->WndExStyle, WS_EX_CLIENTEDGE);
else ClearFlag(d->WndExStyle, WS_EX_CLIENTEDGE);
if (_View)
SetWindowLong(_View, GWL_EXSTYLE, d->WndExStyle);
#else
if (i) SetFlag(GViewFlags, GWF_SUNKEN);
else ClearFlag(GViewFlags, GWF_SUNKEN);
#endif
if (i)
{
if (!_BorderSize)
_BorderSize = 2;
}
else _BorderSize = 0;
}
bool LView::Flat()
{
// ThreadCheck();
#if WINNATIVE
return !TestFlag(d->WndExStyle, WS_EX_CLIENTEDGE) &&
!TestFlag(d->WndExStyle, WS_EX_WINDOWEDGE);
#else
return !TestFlag(GViewFlags, GWF_SUNKEN) &&
!TestFlag(GViewFlags, GWF_RAISED);
#endif
}
void LView::Flat(bool i)
{
ThreadCheck();
#if WINNATIVE
ClearFlag(d->WndExStyle, (WS_EX_CLIENTEDGE|WS_EX_WINDOWEDGE));
#else
ClearFlag(GViewFlags, (GWF_RAISED|GWF_SUNKEN));
#endif
}
bool LView::Raised()
{
// ThreadCheck();
#if WINNATIVE
return TestFlag(d->WndExStyle, WS_EX_WINDOWEDGE);
#else
return TestFlag(GViewFlags, GWF_RAISED);
#endif
}
void LView::Raised(bool i)
{
ThreadCheck();
#if WINNATIVE
if (i) SetFlag(d->WndExStyle, WS_EX_WINDOWEDGE);
else ClearFlag(d->WndExStyle, WS_EX_WINDOWEDGE);
#else
if (i) SetFlag(GViewFlags, GWF_RAISED);
else ClearFlag(GViewFlags, GWF_RAISED);
#endif
if (i)
{
if (!!_BorderSize)
_BorderSize = 2;
}
else _BorderSize = 0;
}
int LView::GetId()
{
// This is needed by SendNotify function which is thread safe.
// So no thread safety check here.
return d->CtrlId;
}
void LView::SetId(int i)
{
// This is needed by SendNotify function which is thread safe.
// So no thread safety check here.
d->CtrlId = i;
#if WINNATIVE
if (_View)
SetWindowLong(_View, GWL_ID, d->CtrlId);
#elif defined __GTK_H__
#elif defined MAC
#endif
}
bool LView::GetTabStop()
{
ThreadCheck();
#if WINNATIVE
return TestFlag(d->WndStyle, WS_TABSTOP);
#else
return d->TabStop;
#endif
}
void LView::SetTabStop(bool b)
{
ThreadCheck();
#if WINNATIVE
if (b)
SetFlag(d->WndStyle, WS_TABSTOP);
else
ClearFlag(d->WndStyle, WS_TABSTOP);
#else
d->TabStop = b;
#endif
}
int64 LView::GetCtrlValue(int Id)
{
ThreadCheck();
LViewI *w = FindControl(Id);
return (w) ? w->Value() : 0;
}
void LView::SetCtrlValue(int Id, int64 i)
{
ThreadCheck();
LViewI *w = FindControl(Id);
if (w) w->Value(i);
}
const char *LView::GetCtrlName(int Id)
{
ThreadCheck();
LViewI *w = FindControl(Id);
return (w) ? w->Name() : 0;
}
void LView::SetCtrlName(int Id, const char *s)
{
if (!IsAttached() || InThread())
{
if (auto w = FindControl(Id))
w->Name(s);
}
else
{
PostEvent( M_SET_CTRL_NAME,
(LMessage::Param)Id,
(LMessage::Param)new LString(s));
}
}
bool LView::GetCtrlEnabled(int Id)
{
ThreadCheck();
LViewI *w = FindControl(Id);
return (w) ? w->Enabled() : 0;
}
void LView::SetCtrlEnabled(int Id, bool Enabled)
{
if (!IsAttached() || InThread())
{
if (auto w = FindControl(Id))
w->Enabled(Enabled);
}
else
{
PostEvent( M_SET_CTRL_ENABLE,
(LMessage::Param)Id,
(LMessage::Param)Enabled);
}
}
bool LView::GetCtrlVisible(int Id)
{
ThreadCheck();
LViewI *w = FindControl(Id);
if (!w)
LgiTrace("%s:%i - Ctrl %i not found.\n", _FL, Id);
return (w) ? w->Visible() : 0;
}
void LView::SetCtrlVisible(int Id, bool v)
{
if (!IsAttached() || InThread())
{
if (auto w = FindControl(Id))
w->Visible(v);
}
else
{
PostEvent( M_SET_CTRL_VISIBLE,
(LMessage::Param)Id,
(LMessage::Param)v);
}
}
bool LView::AttachChildren()
{
for (auto c: Children)
{
bool a = c->IsAttached();
if (!a)
{
if (!c->Attach(this))
{
LgiTrace("%s:%i - failed to attach %s\n", _FL, c->GetClass());
return false;
}
}
}
return true;
}
LFont *LView::GetFont()
{
if (!d->Font &&
d->Css &&
LResources::GetLoadStyles())
{
LFontCache *fc = LAppInst->GetFontCache();
if (fc)
{
LFont *f = fc->GetFont(d->Css);
if (f)
{
if (d->FontOwnType == GV_FontOwned)
DeleteObj(d->Font);
d->Font = f;
d->FontOwnType = GV_FontCached;
}
}
}
return d->Font ? d->Font : LSysFont;
}
void LView::SetFont(LFont *Font, bool OwnIt)
{
bool Change = d->Font != Font;
if (Change)
{
if (d->FontOwnType == GV_FontOwned)
{
LAssert(d->Font != LSysFont);
DeleteObj(d->Font);
}
d->FontOwnType = OwnIt ? GV_FontOwned : GV_FontPtr;
d->Font = Font;
#if WINNATIVE
if (_View)
SendMessage(_View, WM_SETFONT, (WPARAM) (Font ? Font->Handle() : 0), 0);
#endif
for (LViewI *p = GetParent(); p; p = p->GetParent())
{
LTableLayout *Tl = dynamic_cast(p);
if (Tl)
{
Tl->InvalidateLayout();
break;
}
}
Invalidate();
}
}
bool LView::IsOver(LMouse &m)
{
return (m.x >= 0) &&
(m.y >= 0) &&
(m.x < Pos.X()) &&
(m.y < Pos.Y());
}
bool LView::WindowVirtualOffset(LPoint *Offset)
{
bool Status = false;
if (Offset)
{
Offset->x = 0;
Offset->y = 0;
for (LViewI *Wnd = this; Wnd; Wnd = Wnd->GetParent())
{
#if !LGI_VIEW_HANDLE
auto IsWnd = dynamic_cast(Wnd);
if (!IsWnd)
#else
if (!Wnd->Handle())
#endif
{
LRect r = Wnd->GetPos();
LViewI *Par = Wnd->GetParent();
if (Par)
{
LRect c = Par->GetClient(false);
Offset->x += r.x1 + c.x1;
Offset->y += r.y1 + c.y1;
}
else
{
Offset->x += r.x1;
Offset->y += r.y1;
}
Status = true;
}
else break;
}
}
return Status;
}
LString _ViewDesc(LViewI *v)
{
LString s;
s.Printf("%s/%s/%i", v->GetClass(), v->Name(), v->GetId());
return s;
}
LViewI *LView::WindowFromPoint(int x, int y, int DebugDepth)
{
char Tabs[64];
if (DebugDepth)
{
memset(Tabs, 9, DebugDepth);
Tabs[DebugDepth] = 0;
LgiTrace("%s%s %i\n", Tabs, _ViewDesc(this).Get(), Children.Length());
}
// We iterate over the child in reverse order because if they overlap the
// end of the list is on "top". So they should get the click or whatever
// before the the lower windows.
auto it = Children.rbegin();
int n = (int)Children.Length() - 1;
for (LViewI *c = *it; c; c = *--it)
{
LRect CPos = c->GetPos();
if (CPos.Overlap(x, y) && c->Visible())
{
LRect CClient;
CClient = c->GetClient(false);
int Ox = CPos.x1 + CClient.x1;
int Oy = CPos.y1 + CClient.y1;
if (DebugDepth)
{
LgiTrace("%s[%i] %s Pos=%s Client=%s m(%i,%i)->(%i,%i)\n",
Tabs, n--,
_ViewDesc(c).Get(),
CPos.GetStr(),
CClient.GetStr(),
x, y,
x - Ox, y - Oy);
}
LViewI *Child = c->WindowFromPoint(x - Ox, y - Oy, DebugDepth ? DebugDepth + 1 : 0);
if (Child)
return Child;
}
else if (DebugDepth)
{
LgiTrace("%s[%i] MISSED %s Pos=%s m(%i,%i)\n", Tabs, n--, _ViewDesc(c).Get(), CPos.GetStr(), x, y);
}
}
if (x >= 0 && y >= 0 && x < Pos.X() && y < Pos.Y())
{
return this;
}
return NULL;
}
LColour LView::StyleColour(int CssPropType, LColour Default, int Depth)
{
LColour c = Default;
if ((CssPropType >> 8) == LCss::TypeColor)
{
LViewI *v = this;
for (int i=0; v && iGetParent())
{
auto Style = v->GetCss();
if (Style)
{
auto Colour = (LCss::ColorDef*) Style->PropAddress((LCss::PropType)CssPropType);
if (Colour)
{
if (Colour->Type == LCss::ColorRgb)
{
c.Set(Colour->Rgb32, 32);
break;
}
else if (Colour->Type == LCss::ColorTransparent)
{
c.Empty();
break;
}
}
}
if (dynamic_cast(v) ||
dynamic_cast(v))
break;
}
}
return c;
}
bool LView::InThread()
{
#if WINNATIVE
HWND Hnd = _View;
for (LViewI *p = GetParent(); p && !Hnd; p = p->GetParent())
{
Hnd = p->Handle();
}
auto CurThreadId = GetCurrentThreadId();
auto GuiThreadId = LAppInst->GetGuiThreadId();
DWORD ViewThread = Hnd ? GetWindowThreadProcessId(Hnd, NULL) : GuiThreadId;
return CurThreadId == ViewThread;
#elif defined(HAIKU)
return true;
#else
OsThreadId Me = GetCurrentThreadId();
OsThreadId Gui = LAppInst ? LAppInst->GetGuiThreadId() : 0;
#if 0
if (Gui != Me)
LgiTrace("%s:%i - Out of thread:"
#ifdef LGI_COCOA
"%llx, %llx"
#else
"%x, %x"
#endif
"\n", _FL, Gui, Me);
#endif
return Gui == Me;
#endif
}
bool LView::PostEvent(int Cmd, LMessage::Param a, LMessage::Param b, int64_t timeoutMs)
{
#ifdef LGI_SDL
return LPostEvent(this, Cmd, a, b);
#elif defined(HAIKU)
if (!d || !d->Hnd)
{
// printf("%s:%i - Bad pointers %p %p\n", _FL, d, d ? d->Hnd : NULL);
return false;
}
BWindow *bWnd = NULL;
LWindow *wnd = dynamic_cast(this);
if (wnd)
{
bWnd = wnd->WindowHandle();
}
else
{
// Look for an attached view to lock...
for (LViewI *v = this; v; v = v->GetParent())
{
auto vhnd = v->Handle();
if (vhnd && ::IsAttached(vhnd))
{
bWnd = vhnd->Window();
break;
}
}
}
BMessage m(Cmd);
auto r = m.AddInt64(LMessage::PropA, a);
if (r != B_OK)
printf("%s:%i - AddUInt64 failed.\n", _FL);
r = m.AddInt64(LMessage::PropB, b);
if (r != B_OK)
printf("%s:%i - AddUInt64 failed.\n", _FL);
r = m.AddPointer(LMessage::PropView, this);
if (r != B_OK)
printf("%s:%i - AddPointer failed.\n", _FL);
if (bWnd)
{
r = bWnd->PostMessage(&m);
if (r != B_OK)
printf("%s:%i - PostMessage failed.\n", _FL);
return r == B_OK;
}
// Not attached yet...
d->MsgQue.Add(new BMessage(m));
// printf("%s:%i - PostEvent.MsgQue=%i\n", _FL, (int)d->MsgQue.Length());
return true;
#elif WINNATIVE
if (!_View)
return false;
BOOL Res = ::PostMessage(_View, Cmd, a, b);
if (!Res)
{
auto Err = GetLastError();
int asd=0;
}
return Res != 0;
#elif !LGI_VIEW_HANDLE
return LAppInst->PostEvent(this, Cmd, a, b);
#else
if (_View)
return LPostEvent(_View, Cmd, a, b);
LAssert(0);
return false;
#endif
}
bool LView::Invalidate(LRegion *r, bool Repaint, bool NonClient)
{
if (r)
{
for (int i=0; iLength(); i++)
{
bool Last = i == r->Length()-1;
Invalidate((*r)[i], Last ? Repaint : false, NonClient);
}
return true;
}
return false;
}
LButton *FindDefault(LViewI *w)
{
LButton *But = 0;
for (auto c: w->IterateViews())
{
But = dynamic_cast(c);
if (But && But->Default())
{
break;
}
But = FindDefault(c);
if (But)
{
break;
}
}
return But;
}
bool LView::Name(const char *n)
{
LBase::Name(n);
#if LGI_VIEW_HANDLE && !defined(HAIKU)
if (_View)
{
#if WINNATIVE
auto Temp = LBase::NameW();
SetWindowTextW(_View, Temp ? Temp : L"");
#endif
}
#endif
Invalidate();
return true;
}
const char *LView::Name()
{
#if WINNATIVE
if (_View)
{
LView::NameW();
}
#endif
return LBase::Name();
}
bool LView::NameW(const char16 *n)
{
LBase::NameW(n);
#if WINNATIVE
if (_View && n)
{
auto Txt = LBase::NameW();
SetWindowTextW(_View, Txt);
}
#endif
Invalidate();
return true;
}
const char16 *LView::NameW()
{
#if WINNATIVE
if (_View)
{
int Length = GetWindowTextLengthW(_View);
if (Length > 0)
{
char16 *Buf = new char16[Length+1];
if (Buf)
{
Buf[0] = 0;
int Chars = GetWindowTextW(_View, Buf, Length+1);
Buf[Chars] = 0;
LBase::NameW(Buf);
}
DeleteArray(Buf);
}
else
{
LBase::NameW(0);
}
}
#endif
return LBase::NameW();
}
LViewI *LView::FindControl(int Id)
{
LAssert(Id != -1);
if (GetId() == Id)
{
return this;
}
for (auto c : Children)
{
LViewI *Ctrl = c->FindControl(Id);
if (Ctrl)
{
return Ctrl;
}
}
return 0;
}
LPoint LView::GetMinimumSize()
{
return d->MinimumSize;
}
void LView::SetMinimumSize(LPoint Size)
{
d->MinimumSize = Size;
bool Change = false;
LRect p = Pos;
if (X() < d->MinimumSize.x)
{
p.x2 = p.x1 + d->MinimumSize.x - 1;
Change = true;
}
if (Y() < d->MinimumSize.y)
{
p.y2 = p.y1 + d->MinimumSize.y - 1;
Change = true;
}
if (Change)
{
SetPos(p);
}
}
bool LView::SetColour(LColour &c, bool Fore)
{
LCss *css = GetCss(true);
if (!css)
return false;
if (Fore)
{
if (c.IsValid())
css->Color(LCss::ColorDef(LCss::ColorRgb, c.c32()));
else
css->DeleteProp(LCss::PropColor);
}
else
{
if (c.IsValid())
css->BackgroundColor(LCss::ColorDef(LCss::ColorRgb, c.c32()));
else
css->DeleteProp(LCss::PropBackgroundColor);
}
return true;
}
/*
bool LView::SetCssStyle(const char *CssStyle)
{
if (!d->Css && !d->Css.Reset(new LCss))
return false;
const char *Defs = CssStyle;
bool b = d->Css->Parse(Defs, LCss::ParseRelaxed);
if (b && d->FontOwnType == GV_FontCached)
{
d->Font = NULL;
d->FontOwnType = GV_FontPtr;
}
return b;
}
*/
void LView::SetCss(LCss *css)
{
d->Css.Reset(css);
}
LCss *LView::GetCss(bool Create)
{
if (Create && !d->Css)
d->Css.Reset(new LCss);
if (d->CssDirty && d->Css)
{
const char *Defs = d->Styles;
if (d->Css->Parse(Defs, LCss::ParseRelaxed))
d->CssDirty = false;
}
return d->Css;
}
LPoint &LView::GetWindowBorderSize()
{
static LPoint s;
ZeroObj(s);
#if WINNATIVE
if (_View)
{
RECT Wnd, Client;
GetWindowRect(Handle(), &Wnd);
GetClientRect(Handle(), &Client);
s.x = (Wnd.right-Wnd.left) - (Client.right-Client.left);
s.y = (Wnd.bottom-Wnd.top) - (Client.bottom-Client.top);
}
#elif defined __GTK_H__
#elif defined MAC
s.x = 0;
s.y = 22;
#endif
return s;
}
#ifdef _DEBUG
#if defined(LGI_CARBON)
void DumpHiview(HIViewRef v, int Depth = 0)
{
char Sp[256];
memset(Sp, ' ', Depth << 2);
Sp[Depth<<2] = 0;
printf("%sHIView=%p", Sp, v);
if (v)
{
Boolean vis = HIViewIsVisible(v);
Boolean en = HIViewIsEnabled(v, NULL);
HIRect pos;
HIViewGetFrame(v, &pos);
char cls[128];
ZeroObj(cls);
GetControlProperty(v, 'meme', 'clas', sizeof(cls), NULL, cls);
printf(" vis=%i en=%i pos=%g,%g-%g,%g cls=%s",
vis, en,
pos.origin.x, pos.origin.y, pos.size.width, pos.size.height,
cls);
}
printf("\n");
for (HIViewRef c = HIViewGetFirstSubview(v); c; c = HIViewGetNextView(c))
{
DumpHiview(c, Depth + 1);
}
}
#elif defined(__GTK_H__)
void DumpGtk(Gtk::GtkWidget *w, Gtk::gpointer Depth = NULL)
{
using namespace Gtk;
if (!w)
return;
char Sp[65] = {0};
if (Depth)
memset(Sp, ' ', *((int*)Depth)*2);
auto *Obj = G_OBJECT(w);
LViewI *View = (LViewI*) g_object_get_data(Obj, "LViewI");
GtkAllocation a;
gtk_widget_get_allocation(w, &a);
LgiTrace("%s%p(%s) = %i,%i-%i,%i\n", Sp, w, View?View->GetClass():G_OBJECT_TYPE_NAME(Obj), a.x, a.y, a.width, a.height);
if (GTK_IS_CONTAINER(w))
{
auto *c = GTK_CONTAINER(w);
if (c)
{
int Next = Depth ? *((int*)Depth)+1 : 1;
gtk_container_foreach(c, DumpGtk, &Next);
}
}
}
#elif defined(HAIKU) || defined(WINDOWS)
template
void _Dump(int Depth, T v)
{
LString Sp, Name;
Sp.Length(Depth<<1);
memset(Sp.Get(), ' ', Depth<<1);
Sp.Get()[Depth<<1] = 0;
#if defined(HAIKU)
LRect Frame = v->Frame();
Name = v->Name();
bool IsHidden = v->IsHidden();
#else
RECT rc; GetWindowRect(v, &rc);
LRect Frame = rc;
wchar_t txt[256];
GetWindowTextW(v, txt, CountOf(txt));
Name = txt;
bool IsHidden = !(GetWindowLong(v, GWL_STYLE) & WS_VISIBLE);
#endif
LgiTrace("%s%p::%s frame=%s vis=%i\n",
Sp.Get(), v, Name.Get(), Frame.GetStr(), !IsHidden);
#if defined(HAIKU)
for (int32 i=0; iCountChildren(); i++)
_Dump(Depth + 1, v->ChildAt(i));
#else
for (auto h = GetWindow(v, GW_CHILD); h; h = GetWindow(h, GW_HWNDNEXT))
_Dump(Depth + 1, h);
#endif
}
#endif
void LView::_Dump(int Depth)
{
char Sp[65] = {0};
memset(Sp, ' ', Depth*2);
#if 0
char s[256];
sprintf_s(s, sizeof(s), "%s%p::%s %s (_View=%p)\n", Sp, this, GetClass(), GetPos().GetStr(), _View);
LgiTrace(s);
List::I i = Children.Start();
for (LViewI *c = *i; c; c = *++i)
{
LView *v = c->GetGView();
if (v)
v->_Dump(Depth+1);
}
#elif defined(LGI_CARBON)
DumpHiview(_View);
#elif defined(__GTK_H__)
// DumpGtk(_View);
#else
#if defined(HAIKU)
LLocker lck(WindowHandle(), _FL);
if (lck.Lock())
#endif
::_Dump(0, WindowHandle());
#endif
}
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////
static LArray *AllFactories = NULL;
#if defined(WIN32)
static HANDLE FactoryEvent;
#else
static pthread_once_t FactoryOnce = PTHREAD_ONCE_INIT;
static void GFactoryInitFactories()
{
AllFactories = new LArray;
}
#endif
LViewFactory::LViewFactory()
{
#if defined(WIN32)
char16 Name[64];
swprintf_s(Name, CountOf(Name), L"LgiFactoryEvent.%i", GetCurrentProcessId());
HANDLE h = CreateEventW(NULL, false, false, Name);
DWORD err = GetLastError();
if (err != ERROR_ALREADY_EXISTS)
{
FactoryEvent = h;
AllFactories = new LArray;
}
else
{
LAssert(AllFactories != NULL);
}
#else
pthread_once(&FactoryOnce, GFactoryInitFactories);
#endif
if (AllFactories)
AllFactories->Add(this);
}
LViewFactory::~LViewFactory()
{
if (AllFactories)
{
AllFactories->Delete(this);
if (AllFactories->Length() == 0)
{
DeleteObj(AllFactories);
#if defined(WIN32)
CloseHandle(FactoryEvent);
#endif
}
}
}
LView *LViewFactory::Create(const char *Class, LRect *Pos, const char *Text)
{
if (ValidStr(Class) && AllFactories)
{
for (int i=0; iLength(); i++)
{
LView *v = (*AllFactories)[i]->NewView(Class, Pos, Text);
if (v)
{
return v;
}
}
}
return 0;
}
#ifdef _DEBUG
#if defined(__GTK_H__)
using namespace Gtk;
#include "LgiWidget.h"
#endif
void LView::Debug()
{
_Debug = true;
#if defined LGI_COCOA
d->ClassName = GetClass();
#endif
}
#endif
diff --git a/src/common/Net/Net.cpp b/src/common/Net/Net.cpp
--- a/src/common/Net/Net.cpp
+++ b/src/common/Net/Net.cpp
@@ -1,1856 +1,1856 @@
/*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
#if defined(LINUX)
#include
#include
#include
#include
#elif defined(MAC)
#include
#include
#include
#include
#include
#endif
#include
#include "lgi/common/File.h"
#include "lgi/common/Net.h"
#include "lgi/common/LgiString.h"
#include "lgi/common/LgiCommon.h"
#include "LgiOsClasses.h"
#include "lgi/common/RegKey.h"
#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 LPrintSock "%I64x"
#define MSG_NOSIGNAL 0
#ifndef EWOULDBLOCK
#define EWOULDBLOCK WSAEWOULDBLOCK
#endif
#ifndef EISCONN
#define EISCONN WSAEISCONN
#endif
#define OsAddr S_un.S_addr
#elif defined POSIX
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SOCKET_ERROR -1
#define LPrintSock "%x"
typedef hostent HostEnt;
typedef int option_t;
#define OsAddr s_addr
#endif
#include "lgi/common/LgiNetInc.h"
///////////////////////////////////////////////////////////////////////////////
#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 "..\..\Win\INet\MibAccess.h"
#include
#pragma comment(lib, "iphlpapi.lib")
#endif
bool LSocket::EnumInterfaces(LArray &Out)
{
bool Status = false;
StartNetworkStack();
#ifdef WIN32
#if 0
PMIB_IF_TABLE2 Tbl;
SecureZeroMemory(&Tbl, sizeof(Tbl));
auto r = GetIfTable2(&Tbl);
for (size_t i=0; iNumEntries; i++)
{
auto &e = Tbl->Table[i];
WCHAR w[256] = {0};
r = ConvertInterfaceLuidToNameW(&e.InterfaceLuid, w, 256);
MIB_UNICASTIPADDRESS_ROW Addr;
InitializeUnicastIpAddressEntry(&Addr);
Addr.Address.si_family = AF_INET;
Addr.InterfaceLuid = e.InterfaceLuid;
Addr.InterfaceIndex = e.InterfaceIndex;
r = GetUnicastIpAddressEntry(&Addr);
if (r == NO_ERROR)
{
int asd=0;
}
}
FreeMibTable(Tbl);
#else
MibII m;
m.Init();
MibInterface Intf[16] = {0};
int Count = m.GetInterfaces(Intf, CountOf(Intf));
if (Count)
{
for (int i=0; iifa_next)
{
if (a->ifa_addr &&
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 LSocketImplPrivate : public LCancel
{
public:
// Data
int Blocking : 1;
int NoDelay : 1;
int Udp : 1;
int Broadcast : 1;
int LogType = NET_LOG_NONE;
LString LogFile;
int Timeout = -1;
OsSocket Socket = INVALID_SOCKET;
int LastError = 0;
LCancel *Cancel = NULL;
LString ErrStr;
LSocketImplPrivate()
{
Cancel = this;
Blocking = true;
NoDelay = false;
Udp = false;
Broadcast = false;
}
~LSocketImplPrivate()
{
}
bool Select(int TimeoutMs, bool Read)
{
// 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 = Socket;
if (ValidSocket(s) && !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,
Read ? &set : NULL,
!Read ? &set : NULL,
NULL,
TimeoutMs >= 0 ? &t : NULL);
if (ret > 0 && FD_ISSET(s, &set))
{
return true;
}
}
return false;
}
// This is the timing granularity of the SelectWithCancel loop. Ie the
// number of milliseconds between checking the Cancel object.
constexpr static int CancelCheckMs = 50;
bool SelectWithCancel(int TimeoutMs, bool Read)
{
if (!Cancel)
// Regular select where we just wait the whole timeout...
return Select(TimeoutMs, false);
// Because select can't check out 'Cancel' value during the waiting we run the select
// call in a much smaller timeout and a loop so that we can respond to Cancel being set
// in a timely manner.
auto Now = LCurrentTime();
auto End = Now + TimeoutMs;
do
{
// Do the cancel check...
if (Cancel->IsCancelled())
break;
// How many ms to wait?
Now = LCurrentTime();
auto Remain = MIN(CancelCheckMs, (int)(End - Now));
if (Remain <= 0)
break;
if (Select(Remain, Read))
return true;
}
while (Now < End);
return false;
}
};
LSocket::LSocket(LStreamI *logger, void *unused_param)
{
StartNetworkStack();
BytesWritten = 0;
BytesRead = 0;
d = new LSocketImplPrivate;
}
LSocket::~LSocket()
{
Close();
DeleteObj(d);
}
bool LSocket::IsOK()
{
return
#ifndef __llvm__
this != 0 &&
#endif
d != 0;
}
LCancel *LSocket::GetCancel()
{
return d->Cancel;
}
void LSocket::SetCancel(LCancel *c)
{
d->Cancel = c;
}
void LSocket::OnDisconnect()
{
}
OsSocket LSocket::ReleaseHandle()
{
auto h = d->Socket;
d->Socket = INVALID_SOCKET;
return h;
}
OsSocket LSocket::Handle(OsSocket Set)
{
if (Set != INVALID_SOCKET)
{
d->Socket = Set;
}
return d->Socket;
}
bool LSocket::IsOpen()
{
if (ValidSocket(d->Socket) && !d->Cancel->IsCancelled())
{
return true;
}
return false;
}
int LSocket::GetTimeout()
{
return d->Timeout;
}
void LSocket::SetTimeout(int ms)
{
d->Timeout = ms;
}
bool LSocket::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 LSocket::IsWritable(int TimeoutMs)
{
return d->SelectWithCancel(TimeoutMs, false);
}
bool LSocket::CanAccept(int TimeoutMs)
{
return IsReadable(TimeoutMs);
}
bool LSocket::IsBlocking()
{
return d->Blocking != 0;
}
void LSocket::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);
#else
#error Impl me.
#endif
}
}
bool LSocket::IsDelayed()
{
return !d->NoDelay;
}
void LSocket::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 LSocket::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 LSocket::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 LSocket::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 LSocket::GetRemoteIp(char *IpAddr)
{
if (!IpAddr)
return false;
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 LSocket::GetRemotePort()
{
struct sockaddr_in a;
socklen_t addrlen = sizeof(a);
if (!getpeername(Handle(), (sockaddr*)&a, &addrlen))
{
return a.sin_port;
}
return 0;
}
int LSocket::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))
{
LArray Buf(512);
#if !defined(MAC)
option_t i;
socklen_t 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)
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);
+ auto ErrStr = LErrorCodeToString(Err);
printf("%s:%i - gethostbyname_r('%s') returned %i, %i, %s\n",
- _FL, HostAddr, Ret, Err, ErrStr);
+ _FL, HostAddr, Ret, Err, ErrStr.Get());
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
LgiTrace(LPrintSock " - Setting non blocking\n", d->Socket);
#endif
IsBlocking(false);
}
// Do initial connect to kick things off..
#if CONNECT_LOGGING
LgiTrace(LPrintSock " - Doing initial connect to %s:%i\n", d->Socket, HostAddr, Port);
#endif
Status = connect(d->Socket, (sockaddr*) &RemoteAddr, sizeof(sockaddr_in));
#if CONNECT_LOGGING
LgiTrace(LPrintSock " - 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 || d->LastError == WSAEWOULDBLOCK)
#else
#define IsWouldBlock() (d->LastError == EWOULDBLOCK || d->LastError == EINPROGRESS)
#endif
#if CONNECT_LOGGING
LgiTrace(LPrintSock " - IsWouldBlock()=%i d->LastError=%i\n", d->Socket, IsWouldBlock(), d->LastError);
#endif
int64 End = LCurrentTime() + (d->Timeout > 0 ? d->Timeout : 30000);
while ( !d->Cancel->IsCancelled() &&
ValidSocket(d->Socket) &&
IsWouldBlock())
{
int64 Remaining = End - LCurrentTime();
#if CONNECT_LOGGING
LgiTrace(LPrintSock " - Remaining " LPrintfInt64 "\n", d->Socket, Remaining);
#endif
if (Remaining < 0)
{
#if CONNECT_LOGGING
LgiTrace(LPrintSock " - Leaving loop\n", d->Socket);
#endif
break;
}
if (IsWritable((int)MIN(Remaining, 1000)))
{
// Should be ready to connect now...
#if CONNECT_LOGGING
LgiTrace(LPrintSock " - Secondary connect...\n", d->Socket);
#endif
Status = connect(d->Socket, (sockaddr*) &RemoteAddr, sizeof(sockaddr_in));
#if CONNECT_LOGGING
LgiTrace(LPrintSock " - Secondary connect=%i\n", d->Socket, Status);
#endif
if (Status != 0)
{
Error(Host);
if (d->LastError == EISCONN
#ifdef WIN32
|| d->LastError == WSAEISCONN // OMG windows, really?
#endif
)
{
Status = 0;
}
else
{
#if CONNECT_LOGGING
LgiTrace(LPrintSock " - Connect=%i Err=%i\n", d->Socket, Status, d->LastError);
#endif
if (IsWouldBlock())
continue;
}
break;
}
else
{
#if CONNECT_LOGGING
LgiTrace(LPrintSock " - Connected...\n", d->Socket);
#endif
break;
}
}
else
{
#if CONNECT_LOGGING
LgiTrace(LPrintSock " - 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 LSocket::Bind(int Port, bool reuseAddr)
{
if (!ValidSocket(d->Socket))
{
OnError(0, "Attempt to use invalid socket to bind.");
return false;
}
if (reuseAddr)
{
int so_reuseaddr = 1;
if (setsockopt(Handle(), SOL_SOCKET, SO_REUSEADDR, (const char *)&so_reuseaddr, sizeof so_reuseaddr))
OnError(0, "Attempt to set SO_REUSEADDR failed.");
// This might not be fatal... so continue on.
}
sockaddr_in add;
add.sin_family = AF_INET;
add.sin_addr.s_addr = htonl(INADDR_ANY);
add.sin_port = htons(Port);
int ret = bind(Handle(), (sockaddr*)&add, sizeof(add));
if (ret)
{
Error();
}
return ret == 0;
}
bool LSocket::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 LSocket::Accept(LSocketI *c)
{
if (!c)
{
LAssert(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 = LCurrentTime();
while ( !d->Cancel->IsCancelled() &&
ValidSocket(d->Socket))
{
if (IsReadable(100))
{
NewSocket = accept(d->Socket, &Address, &Length);
break;
}
else if (d->Timeout > 0)
{
uint64 Now = LCurrentTime();
if (Now - Start >= d->Timeout)
{
LString 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 LSocket::Close()
{
if (ValidSocket(d->Socket))
{
#if defined WIN32
closesocket(d->Socket);
#else
close(d->Socket);
#endif
d->Socket = INVALID_SOCKET;
OnDisconnect();
}
return true;
}
void LSocket::Log(const char *Msg, ssize_t Ret, const char *Buf, ssize_t Len)
{
if (d->LogFile)
{
LFile 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 LSocket::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 LSocket::OnError(int ErrorCode, const char *ErrorDescription)
{
d->ErrStr.Printf("Error(%i): %s", ErrorCode, ErrorDescription);
}
const char *LSocket::GetErrorString()
{
return d->ErrStr;
}
int LSocket::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;
static 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."},
{EDESTADDRREQ, "Destination address required."},
{EHOSTDOWN, "Host is down."},
#ifndef HAIKU
{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;
}
bool LSocket::GetUdp()
{
return d->Udp != 0;
}
void LSocket::SetUdp(bool isUdp)
{
if (d->Udp ^ isUdp)
{
d->Udp = isUdp;
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 LSocket::SetBroadcast(bool isBroadcast)
{
d->Broadcast = isBroadcast;
}
bool LSocket::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)
{
LgiTrace("AddMulticastMember(%s, %s)\n", LIpToStr(MulticastIp).Get(), LIpToStr(LocalInterface).Get());
return true;
}
Error();
return false;
}
bool LSocket::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 LSocket::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 LSocket::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
auto 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);
if (Port)
*Port = ntohs(a.sin_port);
}
return (int)b;
}
int LSocket::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_t LIpToInt(LString str)
{
auto p = str.Split(".");
if (p.Length() != 4)
return 0;
uint32_t ip = 0;
for (auto &s : p)
{
ip <<= 8;
auto n = s.Int();
if (n > 255)
{
LAssert(0);
return 0;
}
ip |= (uint8_t)s.Int();
}
return ip;
}
uint32_t LHostnameToIp(const char *Host)
{
if (!Host)
return 0;
// struct addrinfo hints = {};
struct addrinfo *res = NULL;
getaddrinfo(Host, NULL, NULL, &res);
if (!res)
return 0;
uint32_t ip = 0;
if (res->ai_addr)
{
auto fam = res->ai_addr->sa_family;
if (fam == AF_INET)
{
auto a = (sockaddr_in*)res->ai_addr;
ip = ntohl(a->sin_addr.s_addr);
}
}
freeaddrinfo(res);
return ip;
}
bool WhatsMyIp(LAutoString &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, sizeof(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));
}
#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
LSocks5Socket::LSocks5Socket()
{
Socks5Connected = false;
}
void LSocks5Socket::SetProxy(const LSocks5Socket *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 LSocks5Socket::SetProxy(char *proxy, int port, char *username, char *password)
{
Proxy.Reset(NewStr(proxy));
Port = port;
UserName.Reset(NewStr(username));
Password.Reset(NewStr(password));
}
int LSocks5Socket::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 = LSocket::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.");
LSocket::Write(Buf, 4, 0);
if (LSocket::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);
LAssert(NameLen < 0x80);
*b++ = (char)NameLen;
b += sprintf_s(b, NameLen+1, "%s", UserName.Get());
size_t PassLen = strlen(Password);
LAssert(PassLen < 0x80);
*b++ = (char)PassLen;
b += sprintf_s(b, PassLen+1, "%s", Password.Get());
LSocket::Write(Buf, (int)(3 + NameLen + PassLen));
if (LSocket::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);
LAssert(Len < 0x80);
*b++ = (char)Len;
strcpy_s(b, Buf+sizeof(Buf)-b, HostAddr);
b += Len;
}
// Port
memcpy(b, &HostPort, 2);
b += 2;
LSocket::Write(Buf, (int)(b - Buf), 0);
if (LSocket::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)
{
LSocket::Close();
OnInformation("[SOCKS5] Failure: Disconnecting.");
}
}
}
return Status;
}
diff --git a/src/linux/Lgi/App.cpp b/src/linux/Lgi/App.cpp
--- a/src/linux/Lgi/App.cpp
+++ b/src/linux/Lgi/App.cpp
@@ -1,1476 +1,1476 @@
#include
#include
#include
#include
#ifndef WIN32
#include
#include
#endif
#include
#define _GNU_SOURCE
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/SkinEngine.h"
#include "lgi/common/Array.h"
#include "lgi/common/Variant.h"
#include "lgi/common/Token.h"
#include "lgi/common/FontCache.h"
#include "AppPriv.h"
#define DEBUG_MSG_TYPES 0
#define DEBUG_HND_WARNINGS 0
#define IDLE_ALWAYS 0
using namespace Gtk;
bool GlibWidgetSearch(GtkWidget *p, GtkWidget *w, bool Debug, int depth = 0);
////////////////////////////////////////////////////////////////
struct OsAppArgumentsPriv
{
::LArray Ptr;
~OsAppArgumentsPriv()
{
Ptr.DeleteArrays();
}
};
OsAppArguments::OsAppArguments(int args, const char **arg)
{
d = new OsAppArgumentsPriv;
for (int i=0; iPtr.Add(NewStr(arg[i]));
}
Args = d->Ptr.Length();
Arg = &d->Ptr[0];
}
OsAppArguments::~OsAppArguments()
{
DeleteObj(d);
}
bool OsAppArguments::Get(const char *Name, const char **Val)
{
if (!Name)
return false;
for (int i=0; iPtr.DeleteArrays();
if (!CmdLine)
return;
for (char *s = CmdLine; *s; )
{
while (*s && strchr(WhiteSpace, *s)) s++;
if (*s == '\'' || *s == '\"')
{
char delim = *s++;
char *e = strchr(s, delim);
if (e)
d->Ptr.Add(NewStr(s, e - s));
else
break;
s = e + 1;
}
else
{
char *e = s;
while (*e && !strchr(WhiteSpace, *e))
e++;
d->Ptr.Add(NewStr(s, e-s));
s = e;
}
}
Args = d->Ptr.Length();
Arg = &d->Ptr[0];
}
OsAppArguments &OsAppArguments::operator =(OsAppArguments &a)
{
d->Ptr.DeleteArrays();
for (int i=0; iPtr.Add(NewStr(a.Arg[i]));
}
Args = d->Ptr.Length();
Arg = &d->Ptr[0];
return *this;
}
////////////////////////////////////////////////////////////////
#if HAS_SHARED_MIME
#include "GFilterUtils.h"
#include "mime-types.h"
class LSharedMime : public LLibrary
{
public:
LSharedMime() :
#ifdef _DEBUG
LLibrary("libsharedmime1d")
#else
LLibrary("libsharedmime1")
#endif
{
}
DynFunc0(int, mimetypes_init);
DynFunc1(const char*, mimetypes_set_default_type, const char *, default_type);
DynFunc2(const char*, mimetypes_get_file_type, const char*, pathname, mimetypes_flags, flags);
DynFunc2(const char*, mimetypes_get_data_type, const void*, data, int, length);
DynFunc3(bool, mimetypes_decode, const char *, type, char **, media_type, char **, sub_type);
DynFunc2(char *, mimetypes_convert_filename, const char *, pathname, const char *, mime_type);
DynFunc3(bool, mimetypes_add_mime_dir, const char *, path, bool, high_priority, bool, rescan);
DynFunc2(const char *, mimetypes_get_type_info, const char *, mime_type, const char *, lang);
};
#endif
/////////////////////////////////////////////////////////////////////////////
//
// Attempts to cleanup and call drkonqi to process the crash
//
static LString CrashHandlerApp;
void LgiCrashHandler(int Sig)
{
// Don't get into an infinite loop
signal(SIGSEGV, SIG_DFL);
#ifndef _MSC_VER
// Our pid
LString pid;
pid.Printf("%i", getpid());
LgiTrace("LgiCrashHandler trigger pid=%s\n", pid.Get());
auto child = fork();
if (!child)
{
LFile::Path workingDir = CrashHandlerApp;
workingDir--;
chdir(workingDir);
const char *args[] = { CrashHandlerApp, "--pid", pid, NULL };
execvp(CrashHandlerApp, args);
exit(0);
}
if (LAppInst->InThread())
{
LgiTrace("LgiCrashHandler showing dlg\n");
LgiMsg(NULL, "Application crashed... dumping details.", "Crash");
}
else
{
LgiTrace("LgiCrashHandler called from worker thread.\n");
LSleep(10000); // Wait for the crash handler to do it's thing...
}
#endif
exit(-1);
}
/////////////////////////////////////////////////////////////////////////////
#ifndef XK_Num_Lock
#define XK_Num_Lock 0xff7f
#endif
#ifndef XK_Shift_Lock
#define XK_Shift_Lock 0xffe6
#endif
#ifndef XK_Caps_Lock
#define XK_Caps_Lock 0xffe5
#endif
struct Msg
{
LViewI *v;
int m;
LMessage::Param a, b;
void Set(LViewI *V, int M, LMessage::Param A, LMessage::Param B)
{
v = V;
m = M;
a = A;
b = B;
}
};
// Out of thread messages... must lock before access.
class LMessageQue : public LMutex
{
public:
typedef LArray MsgArray;
LMessageQue() : LMutex("LMessageQue")
{
}
MsgArray *Lock(const char *file, int line)
{
if (!LMutex::Lock(file, line))
return NULL;
return &q;
}
operator bool()
{
return q.Length() > 0;
}
private:
MsgArray q;
} MsgQue;
/////////////////////////////////////////////////////////////////////////////
LSkinEngine *LApp::SkinEngine = 0;
LApp *TheApp = 0;
LMouseHook *LApp::MouseHook = 0;
LApp::LApp(OsAppArguments &AppArgs, const char *name, LAppArguments *Args) :
OsApplication(AppArgs.Args, AppArgs.Arg)
{
TheApp = this;
d = new LAppPrivate(this);
Name(name);
LgiArgsAppPath = AppArgs.Arg[0];
if (LIsRelativePath(LgiArgsAppPath))
{
char Cwd[MAX_PATH_LEN];
getcwd(Cwd, sizeof(Cwd));
LMakePath(Cwd, sizeof(Cwd), Cwd, LgiArgsAppPath);
LgiArgsAppPath = Cwd;
}
int WCharSz = sizeof(wchar_t);
#if defined(_MSC_VER)
LAssert(WCharSz == 2);
::LFile::Path Dlls(LgiArgsAppPath);
Dlls--;
SetDllDirectoryA(Dlls);
#else
LAssert(WCharSz == 4);
#endif
#ifdef _MSC_VER
SetEnvironmentVariable(_T("GTK_CSD"), _T("0"));
#else
setenv("GTK_CSD", "0", true);
#endif
// We want our printf's NOW!
setvbuf(stdout, (char*)NULL,_IONBF, 0); // print mesgs immediately.
// Setup the file and graphics sub-systems
d->FileSystem = new LFileSystem;
d->GdcSystem = new GdcDevice;
SetAppArgs(AppArgs);
srand(LCurrentTime());
LColour::OnChange();
Gtk::gchar id[256];
sprintf_s(id, sizeof(id), "com.memecode.%s", name);
d->App = Gtk::gtk_application_new(id, Gtk::G_APPLICATION_FLAGS_NONE);
LAssert(d->App != NULL);
MouseHook = new LMouseHook;
// Setup the SIGSEGV signal to call the crash handler
if (!GetOption("nch"))
{
auto programName = "crash-handler";
LFile::Path p(LSP_APP_INSTALL);
p += programName;
if (!p.Exists())
{
// Check alternate location for development builds
Dl_info dlInfo;
dladdr(LgiCrashHandler, &dlInfo);
if (dlInfo.dli_sname != NULL && dlInfo.dli_saddr != NULL)
{
p = dlInfo.dli_fname;
p += "../../src/linux/CrashHandler";
p += programName;
printf("Alternative path %s: %s\n",
p.Exists() ? "found" : "missing",
p.GetFull().Get());
}
}
if (p.Exists())
{
CrashHandlerApp = p;
signal(SIGSEGV, LgiCrashHandler);
LgiTrace("Crash handler: '%s' installed.\n", CrashHandlerApp.Get());
}
else
{
LgiTrace("Crash handler: No crash handler '%s' found, SIGSEGV handler not installed.\n",
p.GetFull().Get());
}
}
else
{
LgiTrace("Crash handler: disabled.\n");
}
d->GetConfig();
// System font setup
LFontType SysFontType;
Gtk::PangoFontMap *fm = Gtk::pango_cairo_font_map_get_default();
if (fm)
{
using namespace Gtk;
auto cfm = PANGO_CAIRO_FONT_MAP(fm);
#ifdef MAC
double Dpi = 80.0;
#else
double Dpi = 96.0;
#endif
::LFile::Path p(LSP_APP_ROOT);
p += "lgi-conf.json";
if (p.IsFile())
{
::LFile f(p, O_READ);
LJson j(f.Read());
auto sDpi = j.Get("DPI");
if (sDpi)
Dpi = sDpi.Float();
}
pango_cairo_font_map_set_resolution(cfm, Dpi);
}
if (SysFontType.GetSystemFont("System"))
{
SystemNormal = SysFontType.Create();
if (SystemNormal)
SystemNormal->Transparent(true);
SystemBold = SysFontType.Create();
if (SystemBold)
{
SystemBold->Bold(true);
SystemBold->Transparent(true);
SystemBold->Create();
}
}
else
{
printf("%s:%i - Couldn't get system font setting.\n", __FILE__, __LINE__);
}
if (!SystemNormal)
{
LgiMsg(0, "Error: Couldn't create system font.", "Lgi Error: LApp::LApp", MB_OK);
LExitApp();
}
if (!GetOption("noskin"))
{
extern LSkinEngine *CreateSkinEngine(LApp *App);
SkinEngine = CreateSkinEngine(this);
}
}
LApp::~LApp()
{
DeleteObj(AppWnd);
DeleteObj(SystemNormal);
DeleteObj(SystemBold);
DeleteObj(SkinEngine);
DeleteObj(MouseHook);
DeleteObj(d->FileSystem);
DeleteObj(d->GdcSystem);
DeleteObj(LFontSystem::Me);
DeleteObj(d);
TheApp = 0;
}
LApp *LApp::ObjInstance()
{
return TheApp;
}
bool LApp::IsOk()
{
bool Status =
#ifndef __clang__
(this != 0) &&
#endif
(d != 0);
LAssert(Status);
return Status;
}
LMouseHook *LApp::GetMouseHook()
{
return MouseHook;
}
int LApp::GetMetric(LSystemMetric Metric)
{
switch (Metric)
{
case LGI_MET_DECOR_X:
return 8;
case LGI_MET_DECOR_Y:
return 8 + 19;
case LGI_MET_DECOR_CAPTION:
return 19;
default:
break;
}
return 0;
}
LViewI *LApp::GetFocus()
{
// GtkWidget *w = gtk_window_get_focus(GtkWindow *window);
return NULL;
}
OsThread LApp::_GetGuiThread()
{
return d->GuiThread;
}
OsThreadId LApp::GetGuiThreadId()
{
return d->GuiThreadId;
}
OsProcessId LApp::GetProcessId()
{
#ifdef WIN32
return GetCurrentProcessId();
#else
return getpid();
#endif
}
OsAppArguments *LApp::GetAppArgs()
{
return IsOk() ? &d->Args : 0;
}
void LApp::SetAppArgs(OsAppArguments &AppArgs)
{
if (IsOk())
{
d->Args = AppArgs;
}
}
bool LApp::InThread()
{
OsThreadId Me = GetCurrentThreadId();
OsThreadId Gui = GetGuiThreadId();
// printf("Me=%i Gui=%i\n", Me, Gui);
return Gui == Me;
}
struct GtkIdle
{
LAppPrivate *d;
LAppI::OnIdleProc cb;
void *param;
};
Gtk::gboolean IdleWrapper(Gtk::gpointer data)
{
#if 0
static int64 ts = LCurrentTime();
static int count = 0;
int64 now = LCurrentTime();
if (now - ts > 300)
{
printf("IdleWrapper = %i\n", count);
count = 0;
ts = now;
}
else count++;
#endif
GtkIdle *i = (GtkIdle*) data;
if (i->cb)
i->cb(i->param);
LMessageQue::MsgArray *Msgs;
if (MsgQue &&
(Msgs = MsgQue.Lock(_FL)))
{
// printf("IdleWrapper start %i\n", (int)Msgs->Length());
// Copy the messages out of the locked structure..
// This allows new messages to arrive independent
// of us processing them here...
LMessageQue::MsgArray q = *Msgs;
Msgs->Empty();
MsgQue.Unlock();
for (auto m : q)
{
if (!LView::LockHandler(m.v, LView::OpExists))
{
// LgiTrace("%s:%i - Invalid view: %p.\n", _FL, m.v);
}
else
{
LMessage Msg(m.m, m.a, m.b);
// LgiTrace("%s::OnEvent %i,%i,%i\n", m.v->GetClass(), m.m, m.a, m.b);
m.v->OnEvent(&Msg);
}
}
}
else
{
// printf("IdleWrapper start no lock\n");
}
// printf("IdleWrapper end\n");
return i->cb != NULL;
// return false;
}
static GtkIdle idle = {0};
bool LApp::Run(OnIdleProc IdleCallback, void *IdleParam)
{
if (!InThread())
{
printf("%s:%i - Error: Out of thread.\n", _FL);
return false;
}
if (!idle.d)
{
idle.d = d;
idle.cb = IdleCallback;
idle.param = IdleParam;
}
static bool CmdLineDone = false;
if (!CmdLineDone)
{
CmdLineDone = true;
OnCommandLine();
}
Gtk::gtk_main();
return true;
}
bool LApp::Yield()
{
Gtk::gtk_main_iteration_do(false);
return true;
}
void LApp::Exit(int Code)
{
if (Code)
{
// hard exit
::exit(Code);
}
else
{
// soft exit
Gtk::gtk_main_quit();
if (d->IdleId.Length() > 0)
{
size_t Last = d->IdleId.Length() - 1;
Gtk::g_source_remove(d->IdleId[Last]);
d->IdleId.Length(Last);
}
}
}
bool LApp::PostEvent(LViewI *View, int Msg, LMessage::Param a, LMessage::Param b)
{
auto q = MsgQue.Lock(_FL);
if (!q)
{
printf("%s:%i - Couldn't lock app.\n", _FL);
return false;
}
q->New().Set(View, Msg, a, b);
#if 1 // defined(_DEBUG)
if (q->Length() > 200 && (q->Length()%20)==0)
{
static uint64 prev = 0;
auto now = LCurrentTime();
if (now - prev >= 1000)
{
prev = now;
#if defined(WIN32)
char s[256];
sprintf_s(s, sizeof(s),
#else
printf(
#endif
"PostEvent Que=" LPrintfSizeT " (msg=%i)\n", q->Length(), Msg);
#if defined(WIN32)
OutputDebugStringA(s);
#endif
#ifdef LINUX
LHashTbl, size_t> MsgCounts;
for (auto &msg: *q)
MsgCounts.Add(msg.m, MsgCounts.Find(msg.m) + 1);
for (auto c: MsgCounts)
printf(" %i->%i\n", c.key, c.value);
if (Msg == 916)
{
int asd=0;
}
#endif
}
}
#endif
MsgQue.Unlock();
// g_idle_add((GSourceFunc)IdleWrapper, &idle);
g_idle_add_full(G_PRIORITY_HIGH_IDLE, (GSourceFunc)IdleWrapper, &idle, NULL);
return true;
}
void LApp::OnUrl(const char *Url)
{
if (AppWnd)
AppWnd->OnUrl(Url);
}
void LApp::OnReceiveFiles(::LArray &Files)
{
if (AppWnd)
AppWnd->OnReceiveFiles(Files);
else
LAssert(!"You probably want to set 'AppWnd' before calling LApp::Run... maybe.");
}
const char *LApp::KeyModFlags::FlagName(int Flag)
{
#define CHECK(f) if (Flag & f) return #f;
CHECK(GDK_SHIFT_MASK)
CHECK(GDK_LOCK_MASK)
CHECK(GDK_CONTROL_MASK)
CHECK(GDK_MOD1_MASK)
CHECK(GDK_MOD2_MASK)
CHECK(GDK_MOD3_MASK)
CHECK(GDK_MOD4_MASK)
CHECK(GDK_MOD5_MASK)
CHECK(GDK_BUTTON1_MASK)
CHECK(GDK_BUTTON2_MASK)
CHECK(GDK_BUTTON3_MASK)
CHECK(GDK_BUTTON4_MASK)
CHECK(GDK_BUTTON5_MASK)
CHECK(GDK_SUPER_MASK)
CHECK(GDK_HYPER_MASK)
CHECK(GDK_META_MASK)
CHECK(GDK_RELEASE_MASK)
#undef CHECK
return NULL;
}
int LApp::KeyModFlags::FlagValue(const char *Name)
{
#define CHECK(f) if (!Stricmp(Name, #f)) return f;
CHECK(GDK_SHIFT_MASK)
CHECK(GDK_LOCK_MASK)
CHECK(GDK_CONTROL_MASK)
CHECK(GDK_MOD1_MASK)
CHECK(GDK_MOD2_MASK)
CHECK(GDK_MOD3_MASK)
CHECK(GDK_MOD4_MASK)
CHECK(GDK_MOD5_MASK)
CHECK(GDK_BUTTON1_MASK)
CHECK(GDK_BUTTON2_MASK)
CHECK(GDK_BUTTON3_MASK)
CHECK(GDK_BUTTON4_MASK)
CHECK(GDK_BUTTON5_MASK)
CHECK(GDK_SUPER_MASK)
CHECK(GDK_HYPER_MASK)
CHECK(GDK_META_MASK)
CHECK(GDK_RELEASE_MASK)
#undef CHECK
return 0;
}
::LString LApp::KeyModFlags::FlagsToString(int s)
{
::LString::Array a;
for (int i=0; i<32; i++)
{
if (((1 << i) & s) != 0)
a.New() = FlagName(1 << i);
}
return ::LString(",").Join(a);
}
LApp::KeyModFlags *LApp::GetKeyModFlags()
{
return d->GetModFlags();
}
const char *LApp::GetArgumentAt(int n)
{
return n >= 0 && n < d->Args.Args ? NewStr(d->Args.Arg[n]) : 0;
}
bool LApp::GetOption(const char *Option, char *Dest, int DestLen)
{
::LString Buf;
if (GetOption(Option, Buf))
{
if (Dest)
{
if (DestLen > 0)
{
strcpy_s(Dest, DestLen, Buf);
}
else return false;
}
return true;
}
return false;
}
bool LApp::GetOption(const char *Option, ::LString &Buf)
{
if (IsOk() && Option)
{
int OptLen = strlen(Option);
for (int i=1; iArgs.Args; i++)
{
char *a = d->Args.Arg[i];
if (!a)
continue;
if (strchr("-/\\", a[0]))
{
if (strnicmp(a+1, Option, OptLen) == 0)
{
char *Arg = 0;
if (strlen(a+1+OptLen) > 0)
{
Arg = a + 1 + OptLen;
}
else if (i < d->Args.Args - 1)
{
Arg = d->Args.Arg[i + 1];
}
if (Arg)
{
if (strchr("\'\"", *Arg))
{
char Delim = *Arg++;
char *End = strchr(Arg, Delim);
if (End)
{
auto Len = End-Arg;
if (Len > 0)
Buf.Set(Arg, Len);
else
return false;
}
else return false;
}
else
{
Buf = Arg;
}
}
return true;
}
}
}
}
return false;
}
void LApp::OnCommandLine()
{
::LArray Files;
for (int i=1; iArgs; i++)
{
char *a = GetAppArgs()->Arg[i];
if (LFileExists(a))
{
Files.Add(NewStr(a));
}
}
// call app
if (Files.Length() > 0)
{
OnReceiveFiles(Files);
}
// clear up
Files.DeleteArrays();
}
::LString LApp::GetFileMimeType(const char *File)
{
::LString Status;
char Full[MAX_PATH_LEN] = "";
if (!LFileExists(File))
{
// Look in the path
LToken p(getenv("PATH"), LGI_PATH_SEPARATOR);
for (int i=0; i 0)
Status = s.SplitDelimit(":").Last().Strip();
}
return Status;
}
}
#endif
#if HAS_LIB_MAGIC
static bool MagicError = false;
if (d->MagicLock.Lock(_FL))
{
if (!d->hMagic && !MagicError)
{
d->hMagic = magic_open(MAGIC_MIME_TYPE);
if (d->hMagic)
{
if (magic_load(d->hMagic, NULL) != 0)
{
printf("%s:%i - magic_load failed - %s\n", _FL, magic_error(d->hMagic));
magic_close(d->hMagic);
d->hMagic = NULL;
MagicError = true;
}
}
else
{
printf("%s:%i - magic_open failed.\n", _FL);
MagicError = true;
}
}
if (d->hMagic && !MagicError)
{
const char *mt = magic_file(d->hMagic, File);
if (mt)
{
Status = mt;
}
}
d->MagicLock.Unlock();
if (Status)
return Status;
}
#endif
#if HAS_SHARED_MIME
// freedesktop.org rocks...
if (!d->Sm)
{
// Not loaded, go and try to load it...
d->Sm = new LSharedMime;
if (d->Sm && d->Sm->IsLoaded())
{
d->Sm->mimetypes_init();
}
}
if (d->Sm && d->Sm->IsLoaded())
{
// Loaded...
char *m = (char*)d->Sm->mimetypes_get_file_type(File, MIMETYPES_CHECK_ALL);
if (m)
{
#if HAS_FILE_CMD
if (stricmp(m, "application/x-not-recognised") != 0)
#endif
{
strcpy(Mime, m);
return true;
}
}
else
{
printf("%s:%i - mimetypes_get_file_type failed for '%s'\n", __FILE__, __LINE__, File);
}
}
else
{
printf("%s:%i - Shared Mime not loaded!!!\n", __FILE__, __LINE__);
}
#endif
#if HAS_FILE_CMD
// doh! not installed... :(
LStringPipe Output;
char Args[256];
sprintf(Args, "-i \"%s\"", File);
LSubProcess p("file", Args);
if (p.Start())
{
p.Communicate(&Output);
char *Out = Output.NewStr();
if (Out)
{
char *s = strchr(Out, ':');
if (s && strchr(Out, '/'))
{
s += 2;
char *e = s;
while
(
*e &&
(
IsAlpha(*e) ||
IsDigit(*e) ||
strchr(".-_/", *e)
)
)
e++;
*e = 0;
Status.Reset(NewStr(s));
}
DeleteArray(Out);
}
}
#endif
return Status;
}
-bool LApp::GetAppsForMimeType(char *Mime, ::LArray<::LAppInfo*> &Apps)
+bool LApp::GetAppsForMimeType(const char *Mime, LArray<::LAppInfo> &Apps)
{
// Find alternative version of the MIME type (e.g. x-type and type).
char AltMime[256];
strcpy(AltMime, Mime);
char *s = strchr(AltMime, '/');
if (s)
{
s++;
int Len = strlen(s) + 1;
if (strnicmp(s, "x-", 2) == 0)
{
memmove(s, s+2, Len - 2);
}
else
{
memmove(s+2, s, Len);
s[0] = 'x';
s[1] = '-';
}
}
LGetAppsForMimeType(Mime, Apps);
LGetAppsForMimeType(AltMime, Apps);
return Apps.Length() > 0;
}
#if defined(LINUX)
LLibrary *LApp::GetWindowManagerLib()
{
if (this != NULL && !d->WmLib)
{
char Lib[32];
WindowManager Wm = LGetWindowManager();
switch (Wm)
{
case WM_Kde:
strcpy(Lib, "liblgikde3");
break;
case WM_Gnome:
strcpy(Lib, "liblgignome2");
break;
default:
strcpy(Lib, "liblgiother");
break;
}
#ifdef _DEBUG
strcat(Lib, "d");
#endif
d->WmLib = new LLibrary(Lib, true);
if (d->WmLib)
{
if (d->WmLib->IsLoaded())
{
Proc_LgiWmInit WmInit = (Proc_LgiWmInit) d->WmLib->GetAddress("LgiWmInit");
if (WmInit)
{
WmInitParams Params;
// Params.Dsp = XObject::XDisplay();
Params.Args = d->Args.Args;
Params.Arg = d->Args.Arg;
WmInit(&Params);
}
// else printf("%s:%i - Failed to find method 'LgiWmInit' in WmLib.\n", __FILE__, __LINE__);
}
// else printf("%s:%i - couldn't load '%s.so'\n", __FILE__, __LINE__, Lib);
}
// else printf("%s:%i - alloc error\n", __FILE__, __LINE__);
}
return d->WmLib && d->WmLib->IsLoaded() ? d->WmLib : 0;
}
void LApp::DeleteMeLater(LViewI *v)
{
d->DeleteLater.Add(v);
}
void LApp::SetClipBoardContent(OsView Hnd, ::LVariant &v)
{
// Store the clipboard data we will serve
d->ClipData = v;
}
bool LApp::GetClipBoardContent(OsView Hnd, ::LVariant &v, ::LArray &Types)
{
return false;
}
#endif
LSymLookup *LApp::GetSymLookup()
{
return d;
}
bool LApp::IsElevated()
{
#ifdef WIN32
LAssert(!"What API works here?");
return false;
#else
return geteuid() == 0;
#endif
}
int LApp::GetCpuCount()
{
return 1;
}
LFontCache *LApp::GetFontCache()
{
if (!d->FontCache)
d->FontCache.Reset(new LFontCache(SystemNormal));
return d->FontCache;
}
#ifdef LINUX
LApp::DesktopInfo::DesktopInfo(const char *file)
{
File = file;
Dirty = false;
if (File)
Serialize(false);
}
bool LApp::DesktopInfo::Serialize(bool Write)
{
::LFile f;
if (Write)
{
::LFile::Path p(File);
p--;
if (!p.Exists())
return false;
}
else if (!LFileExists(File))
return false;
if (!f.Open(File, Write?O_WRITE:O_READ))
{
LgiTrace("%s:%i - Failed to open '%s'\n", _FL, File.Get());
return false;
}
if (Write)
{
f.SetSize(0);
for (unsigned i=0; i= 0)
{
int e = l.Find("]", ++s);
if (e >= 0)
{
Cur = &Data.New();
Cur->Name = l(s, e - s + 1);
}
}
else if ((s = l.Find("=")) >= 0)
{
if (!Cur)
Cur = &Data.New();
KeyPair &kp = Cur->Values.New();
kp.Key = l(0, s).Strip();
kp.Value = l(++s, -1).Strip();
// printf("Read '%s': '%s'='%s'\n", Cur->Name.Get(), kp.Key.Get(), kp.Value.Get());
}
}
}
return true;
}
LApp::DesktopInfo::Section *LApp::DesktopInfo::GetSection(const char *Name, bool Create)
{
for (unsigned i=0; iGet(Field, false, Dirty);
if (kp)
{
return kp->Value;
}
}
}
return ::LString();
}
bool LApp::DesktopInfo::Set(const char *Field, const char *Value, const char *Sect)
{
if (!Field)
return false;
Section *s = GetSection(Sect ? Sect : DefaultSection, true);
if (!s)
return false;
KeyPair *kp = s->Get(Field, true, Dirty);
if (!kp)
return false;
if (kp->Value != Value)
{
kp->Value = Value;
Dirty = true;
}
return true;
}
LApp::DesktopInfo *LApp::GetDesktopInfo()
{
auto sExe = LGetExeFile();
::LFile::Path Exe(sExe);
::LFile::Path Desktop(LSP_HOME);
::LString Leaf;
Leaf.Printf("%s.desktop", Exe.Last().Get());
Desktop += ".local/share/applications";
Desktop += Leaf;
const char *Ex = Exe;
const char *Fn = Desktop;
if (d->DesktopInfo.Reset(new DesktopInfo(Desktop)))
{
// Do a sanity check...
::LString s = d->DesktopInfo->Get("Name");
if (!s && Name())
d->DesktopInfo->Set("Name", Name());
s = d->DesktopInfo->Get("Exec");
if (!s || s != (const char*)sExe)
d->DesktopInfo->Set("Exec", sExe);
s = d->DesktopInfo->Get("Type");
if (!s) d->DesktopInfo->Set("Type", "Application");
s = d->DesktopInfo->Get("Categories");
if (!s) d->DesktopInfo->Set("Categories", "Application;");
s = d->DesktopInfo->Get("Terminal");
if (!s) d->DesktopInfo->Set("Terminal", "false");
d->DesktopInfo->Update();
}
return d->DesktopInfo;
}
bool LApp::SetApplicationIcon(const char *FileName)
{
DesktopInfo *di = GetDesktopInfo();
if (!di)
return false;
::LString IcoPath = di->Get("Icon");
if (IcoPath == FileName)
return true;
di->Set("Icon", FileName);
return di->Update();
}
#endif
////////////////////////////////////////////////////////////////
OsApplication *OsApplication::Inst = 0;
class OsApplicationPriv
{
public:
OsApplicationPriv()
{
}
};
OsApplication::OsApplication(int Args, char **Arg)
{
Inst = this;
d = new OsApplicationPriv;
}
OsApplication::~OsApplication()
{
DeleteObj(d);
Inst = 0;
}
////////////////////////////////////////////////////////////////
void LMessage::Set(int Msg, Param ParamA, Param ParamB)
{
m = Msg;
a = ParamA;
b = ParamB;
}
struct GlibEventParams : public LMessage
{
GtkWidget *w;
};
bool GlibWidgetSearch(GtkWidget *p, GtkWidget *w, bool Debug, int depth)
{
char indent[256] = "";
if (Debug)
{
int ch = depth * 2;
memset(indent, ' ', ch);
indent[ch] = 0;
printf("%sGlibWidgetSearch: %p, %p\n", indent, p, w);
}
if (p == w)
return true;
if (GTK_IS_CONTAINER(p))
{
int n = 0;
Gtk::GList *top = gtk_container_get_children(GTK_CONTAINER(p));
Gtk::GList *i = top;
while (i)
{
if (Debug)
printf("%s[%i]=%s\n", indent, n, gtk_widget_get_name((GtkWidget*)i->data));
if (i->data == w)
return true;
else if (GlibWidgetSearch((GtkWidget*)i->data, w, Debug, depth + 1))
return true;
i = i->next;
n++;
}
g_list_free(top);
}
else if (GTK_IS_BIN(p))
{
GtkWidget *child = gtk_bin_get_child(GTK_BIN(p));
if (child)
{
if (Debug)
printf("%schild=%s\n", indent, gtk_widget_get_name(child));
if (child == w)
return true;
else if (GlibWidgetSearch(child, w, Debug, depth + 1))
return true;
}
}
else if (Debug)
{
printf("%sUnknown=%s\n", indent, gtk_widget_get_name(p));
}
return false;
}
void LApp::OnDetach(LViewI *View)
{
LMessageQue::MsgArray *q = MsgQue.Lock(_FL);
if (!q)
{
printf("%s:%i - Couldn't lock app.\n", _FL);
return;
}
MsgQue.Unlock();
}
bool LMessage::Send(LViewI *View)
{
if (!View)
{
LAssert(!"No view");
return false;
}
return LAppInst->PostEvent(View, m, a, b);
}
diff --git a/src/linux/Lgi/General.cpp b/src/linux/Lgi/General.cpp
--- a/src/linux/Lgi/General.cpp
+++ b/src/linux/Lgi/General.cpp
@@ -1,644 +1,652 @@
// Linux Implementation of General LGI functions
#include
#include
#include
#include
#include
#include
#define _POSIX_TIMERS
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/SubProcess.h"
#include "lgi/common/TextLabel.h"
#include "lgi/common/Button.h"
#ifndef LGI_SDL
#include "LgiWinManGlue.h"
#endif
#include "lgi/common/Token.h"
#include
#define DEBUG_GET_APPS_FOR_MIMETYPE 0
////////////////////////////////////////////////////////////////
// Local helper functions
bool _lgi_check_file(char *Path)
{
if (Path)
{
if (LFileExists(Path))
{
// file is there
return true;
}
else
{
// shortcut?
char *e = Path + strlen(Path);
strcpy(e, ".lnk");
if (LFileExists(Path))
{
// resolve shortcut
char Link[256];
if (LResolveShortcut(Path, Link, sizeof(Link)))
{
// check destination of link
if (LFileExists(Link))
{
strcpy(Path, Link);
return true;
}
}
}
*e = 0;
}
}
return false;
}
LString LCurrentUserName()
{
struct passwd *pw = getpwuid(geteuid());
if (pw)
return pw->pw_name;
return "";
}
void LSleep(uint32_t i)
{
struct timespec request, remain;
ZeroObj(request);
ZeroObj(remain);
request.tv_sec = i / 1000;
request.tv_nsec = (i % 1000) * 1000000;
//printf("%i LSleep(%i)\n", LGetCurrentThread(), i);
while (nanosleep(&request, &remain) == -1)
{
request = remain;
//printf("\t%i Resleeping=%i\n", LGetCurrentThread(), request.tv_sec*1000 + request.tv_nsec/1000);
}
}
int GtkAssertDlg(const char *File, int Line, const char *Msg)
{
Gtk::GtkWidget *dialog =
Gtk::gtk_message_dialog_new
(
NULL,
Gtk::GTK_DIALOG_DESTROY_WITH_PARENT,
Gtk::GTK_MESSAGE_ERROR,
Gtk::GTK_BUTTONS_NONE,
"%s:%i - Assert failed:\n%s",
File, Line,
Msg
);
Gtk::GtkDialog *dlg = GtkCast(dialog, gtk_dialog, GtkDialog);
Gtk::gtk_dialog_add_buttons(dlg, "Break", 1, "Quit", 2, "Ignore", 3, NULL);
int Result = Gtk::gtk_dialog_run(dlg);
Gtk::gtk_widget_destroy(dialog);
return Result;
}
void _lgi_assert(bool b, const char *test, const char *file, int line)
{
static bool Asserting = false;
if (!b && !Asserting)
{
Asserting = true;
printf("%s:%i - Assert failed:\n%s\n", file, line, test);
#ifdef LGI_SDL
exit(-1);
#else
Gtk::gint Result = Gtk::GTK_RESPONSE_NO;
#if 1
if (LAppInst->InThread())
Result = GtkAssertDlg(file, line, test);
else
Result = 1;
#endif
switch (Result)
{
case 1:
{
int *i = NULL;
*i = 0;
break;
}
case 2:
{
exit(-1);
break;
}
}
#endif
Asserting = false;
}
}
////////////////////////////////////////////////////////////////////////
// Implementations
LMessage CreateMsg(int m, int a, int b)
{
static LMessage Msg(0);
Msg.Set(m, a, b);
return Msg;
}
bool LGetMimeTypeExtensions(const char *Mime, LArray &Ext)
{
int Start = Ext.Length();
char *e;
#define HardCodeExtention(Mime, Ext1, Ext2) \
else if (!stricmp(Mime, Mime)) \
{ if (Ext1) Ext.Add(Ext1); \
if (Ext2) Ext.Add(Ext2); }
const char *Null = NULL;
if (!Mime);
HardCodeExtention("text/calendar", "ics", Null)
HardCodeExtention("text/x-vcard", "vcf", Null)
HardCodeExtention("text/mbox", "mbx", "mbox");
return Ext.Length() > Start;
}
LString LGetFileMimeType(const char *File)
{
return LAppInst->GetFileMimeType(File);
}
bool _GetSystemFont(char *FontType, char *Font, int FontBufSize, int &PointSize)
{
bool Status = false;
#ifndef LGI_SDL
LLibrary *WmLib = LAppInst->GetWindowManagerLib();
if (WmLib)
{
Proc_LgiWmGetSysFont GetSysFont = (Proc_LgiWmGetSysFont) WmLib->GetAddress("LgiWmGetSysFont");
if (GetSysFont)
{
Status = GetSysFont(FontType, Font, FontBufSize, PointSize);
if (!Status)
{
printf("%s:%i - GetSysFont failed\n", _FL);
}
}
else
{
printf("%s:%i - Entry point doesn't exist\n", _FL);
}
}
else
#endif
{
static bool Warn = true;
if (Warn)
{
printf("%s:%i - GetWindowManagerLib failed\n", _FL);
Warn = false;
}
}
return Status;
}
-bool LGetAppsForMimeType(const char *Mime, LArray &Apps, int Limit)
+bool LGetAppsForMimeType(const char *Mime, LArray &Apps, int Limit)
{
bool Status = false;
char Args[MAX_PATH_LEN];
sprintf(Args, "query default %s", Mime);
LStringPipe Output;
LLanguage *CurLang = LGetLanguageId();
char LangName[64];
sprintf_s(LangName, sizeof(LangName), "Name[%s]", CurLang ? CurLang->Id : "en");
#if DEBUG_GET_APPS_FOR_MIMETYPE
printf("LGetAppsForMimeType('%s', ..., %i)\nRunning 'xdg-mime %s'\n",
Mime, Limit, Args);
#endif
- LSubProcess p("xdg-mime", Args);
- if (p.Start())
+ LSubProcess proc("xdg-mime", Args);
+ if (proc.Start())
+ {
+ LgiTrace("%s:%i - Failed to execute xdg-mime\n", _FL);
+ return Status;
+ }
+
+ proc.Communicate(&Output);
+ LAutoString o(Output.NewStr());
+
+ #if DEBUG_GET_APPS_FOR_MIMETYPE
+ printf("Output:\n%s\n", o.Get());
+ #endif
+ if (o)
+ {
+ LgiTrace("%s:%i - No output from xdg-mime\n", _FL);
+ return Status;
+ }
+
+ char *e = o + strlen(o);
+ while (e > o && strchr(" \t\r\n", e[-1]))
+ *(--e) = 0;
+
+ char p[MAX_PATH_LEN];
+ if (LMakePath(p, sizeof(p), "/usr/share/applications", o))
{
- p.Communicate(&Output);
- LAutoString o(Output.NewStr());
+ LgiTrace("%s:%i - Failed to create path.\n", _FL);
+ return Status;
+ }
+
+ if (LFileExists(p))
+ {
+ LgiTrace("%s:%i - '%s' doesn't exist.", _FL, p);
+ return Status;
+ }
+
+ LAutoString txt(LReadTextFile(p));
+ LAutoString Section;
+
+ #if DEBUG_GET_APPS_FOR_MIMETYPE
+ printf("Reading '%s', got %i bytes\n", p, strlen(txt));
+ #endif
+
+ if (txt)
+ {
+ LgiTrace("%s:%i - Can't read from '%s'\n", _FL, p);
+ return Status;
+ }
+
+ auto &ai = Apps.New();
+
+ LToken t(txt, "\n");
+ for (int i=0; i o && strchr(" \t\r\n", e[-1]))
- *(--e) = 0;
-
- char p[MAX_PATH_LEN];
- if (LMakePath(p, sizeof(p), "/usr/share/applications", o))
+ Var++;
+ char *End = strchr(Var, ']');
+ if (End)
{
- if (LFileExists(p))
+ Section.Reset(NewStr(Var, End - Var));
+ }
+ }
+ else if (!Section || !stricmp(Section, "Desktop Entry"))
+ {
+ char *Value = strchr(Var, '=');
+ if (Value)
+ {
+ *Value++ = 0;
+ if (!stricmp(Var, "Exec"))
{
- LAutoString txt(LReadTextFile(p));
- LAutoString Section;
-
- #if DEBUG_GET_APPS_FOR_MIMETYPE
- printf("Reading '%s', got %i bytes\n", p, strlen(txt));
- #endif
-
- if (txt)
- {
- LAppInfo *ai = new LAppInfo;
- Apps.Add(ai);
-
- LToken t(txt, "\n");
- for (int i=0; iPath.Set(exe, sp-exe);
- else
- ai->Path = exe;
-
- Status = true;
- }
- else if (!stricmp(Var, "Name") ||
- !stricmp(Var, LangName))
- {
- ai->Name = Value;
- }
- }
- }
- }
-
- #if DEBUG_GET_APPS_FOR_MIMETYPE
- printf(" ai='%s' '%s'\n", ai->Name.Get(), ai->Path.Get());
- #endif
- }
- else LgiTrace("%s:%i - Can't read from '%s'\n", _FL, p);
- }
- else LgiTrace("%s:%i - '%s' doesn't exist.", _FL, p);
- }
- else LgiTrace("%s:%i - Failed to create path.\n", _FL);
- }
- else LgiTrace("%s:%i - No output from xdg-mime\n", _FL);
- }
- else LgiTrace("%s:%i - Failed to execute xdg-mime\n", _FL);
+ #if DEBUG_GET_APPS_FOR_MIMETYPE
+ printf(" ai='%s' '%s'\n", ai.Name.Get(), ai.Path.Get());
+ #endif
return Status;
}
LString LGetAppForMimeType(const char *Mime)
{
LString App;
- LArray Apps;
+ LArray Apps;
if (LGetAppsForMimeType(Mime, Apps, 1))
- App = Apps[0]->Path.Get();
- Apps.DeleteObjects();
+ App = Apps[0].Path.Get();
return App;
}
int LRand(int Limit)
{
return rand() % Limit;
}
LString LErrorCodeToString(uint32_t ErrorCode)
{
LString e = strerror(ErrorCode);
if (!e)
e.Printf("UnknownError(%i)", ErrorCode);
return e;
}
bool LExecute(const char *File, const char *Args, const char *Dir, LString *Error)
{
if (File)
{
bool IsUrl = false;
char App[MAX_PATH_LEN] = "";
if (strnicmp(File, "http://", 7) == 0 ||
strnicmp(File, "https://", 8) == 0)
{
IsUrl = true;
LGetAppForMimeType("text/html", App, sizeof(App));
}
else
{
struct stat f;
char Path[MAX_PATH_LEN];
// see if the file is executable
bool InPath = false;
bool Ok = stat(File, &f) == 0;
if (Ok)
{
strcpy_s(Path, sizeof(Path), File);
}
else
{
// look in the path
InPath = true;
LToken p(getenv("PATH"), LGI_PATH_SEPARATOR);
for (int i=0; iPrintf("'%s' doesn't exist.\n", File);
}
if (App[0])
{
bool FileAdded = false;
LString AppPath;
LString EscFile = LString::Escape(File, -1, " &");
LString a = App;
int Pos;
while ((Pos = a.Find("%")) >= 0)
{
char *s = a.Get() + Pos;
printf("a='%s'\n", a.Get());
switch (s[1])
{
case 'f':
case 'F':
{
a = a(0,Pos) + EscFile + a(Pos+2,-1);
FileAdded = true;
break;
}
case 'u':
case 'U':
{
if (IsUrl)
a = a(0,Pos) + EscFile + a(Pos+2,-1);
else
a = a(0,Pos) + LString("file:") + EscFile + a(Pos+2,-1);
FileAdded = true;
break;
}
default:
{
// we don't understand this command
a = a(0,Pos) + a(Pos+2,-1);
break;
}
}
printf("a='%s'\n", a.Get());
}
if (!FileAdded)
{
a += " ";
a += EscFile;
}
a += " > /dev/null 2> /dev/null &";
int e;
if (Dir) chdir(Dir);
printf("a=\n%s\n", a.Get());
if (e = system(a))
{
if (Error)
*Error = LErrorCodeToString(errno);
return false;
}
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
WindowManager LGetWindowManager()
{
static WindowManager Status = WM_Unknown;
if (Status == WM_Unknown)
{
LDirectory d;
for (bool b=d.First("/proc"); b && Status == WM_Unknown; b=d.Next())
{
if (d.IsDir() && isdigit(d.GetName()[0]))
{
char Path[256];
d.Path(Path, sizeof(Path));
LMakePath(Path, sizeof(Path), Path, "status");
LFile s;
if (s.Open(Path, O_READ))
{
char Buf[256];
Buf[sizeof(Buf)-1] = 0;
s.Read(Buf, sizeof(Buf)-1);
char *n = strchr(Buf, '\n');
if (n)
{
*n = 0;
if (stristr(Buf, "gnome-settings") != 0 ||
stristr(Buf, "gnome-session") != 0 ||
stristr(Buf, "gnome-panel") != 0)
{
Status = WM_Gnome;
}
else if (stristr(Buf, "startkde") != 0 ||
stristr(Buf, "kdesktop") != 0)
{
Status = WM_Kde;
}
}
}
else printf("%s:%i - error\n", __FILE__, __LINE__);
}
}
}
return Status;
}
void LFinishXWindowsStartup(LViewI *Wnd)
{
// Get the startup ID
const char *EnvStartId = "DESKTOP_STARTUP_ID";
char *DesktopStartupId = getenv(EnvStartId);
if (ValidStr(DesktopStartupId))
{
LStringPipe oss;
// Create remove string
oss.Push("remove: ID=");
// Quote the id string
for (char *c = DesktopStartupId; *c; c++)
{
if (*c == ' ' || *c == '"' || *c == '\\')
{
oss.Write((char*)"\\", 1);
}
oss.Write(c, 1);
}
char *Str = oss.NewStr();
// Get the window and display
/*
XWidget *View = Wnd->Handle();
if (!View)
return;
Display *display = View->XDisplay();
Window xroot_window = DefaultRootWindow(display);
XSetWindowAttributes attrs;
attrs.override_redirect = True;
attrs.event_mask = PropertyChangeMask | StructureNotifyMask;
// Get the atoms
Atom type_atom = XInternAtom(display, "_NET_STARTUP_INFO", false);
Atom type_atom_begin = XInternAtom(display, "_NET_STARTUP_INFO_BEGIN", false);
// Create the event we will send
XEvent xevent;
xevent.xclient.type = ClientMessage;
xevent.xclient.message_type = type_atom_begin;
xevent.xclient.display = display;
xevent.xclient.window = View->handle();
xevent.xclient.format = 8;
const char* src = Str;
const char* src_end = src + strlen(Str) + 1; // Include trailing NUL.
// Loop over the string and send it.
while (src != src_end)
{
char* dest = &xevent.xclient.data.b[0];
char* dest_end = dest + 20;
while (dest != dest_end && src != src_end)
{
*dest++ = *src++;
}
while (dest != dest_end)
{
*dest++ = 0;
}
printf("%s:%i - XSendEvent\n", __FILE__, __LINE__);
XSendEvent(display, xroot_window, False, PropertyChangeMask, &xevent);
xevent.xclient.message_type = type_atom;
}
// Clear the event ID so it's not inherited by child processes.
unsetenv(EnvStartId);
*/
}
}
#if HAS_GSTREAMER
// sudo apt-get install libgstreamer1.0-dev
using namespace Gtk;
#include
#endif
bool LPlaySound(const char *FileName, int ASync)
{
#if HAS_GSTREAMER
#else
return LExecute(FileName);
#endif
}