diff --git a/include/lgi/common/Net.h b/include/lgi/common/Net.h
--- a/include/lgi/common/Net.h
+++ b/include/lgi/common/Net.h
@@ -1,548 +1,553 @@
/**
\file
\author Matthew Allen
\date 28/5/1998
\brief Network sockets classes
Copyright (C) 1998, Matthew Allen
*/
#ifndef __INET_H
#define __INET_H
#include "lgi/common/LgiNetInc.h"
#include "lgi/common/LgiInterfaces.h"
#include "lgi/common/Mem.h"
#include "lgi/common/Containers.h"
#include "lgi/common/Stream.h"
#include "lgi/common/LgiString.h"
#include "lgi/common/Cancel.h"
#include "lgi/common/HashTable.h"
#if defined WIN32
#include "ws2ipdef.h"
#elif defined POSIX
#include
#elif defined BEOS
#include
#include
#else
typedef int SOCKET;
#endif
#define DEFAULT_TIMEOUT 30
// Standard ports
#define FTP_PORT 21
#define SSH_PORT 22
#define SMTP_PORT 25
#define HTTP_PORT 80
#define HTTPS_PORT 443
#define SMTP_SSL_PORT 465
#define POP3_PORT 110
#define POP3_SSL_PORT 995
#define IMAP_PORT 143
#define IMAP_SSL_PORT 993
// Parameters for passing to LSocket::SetVariant
/// Turn on/off logging. Used with LSocket::SetParameter.
#define LSocket_Log "Log"
/// Set the progress object. Used with LSocket::SetParameter. Value = (LProgressView*)Prog
#define LSocket_Progress "Progress"
/// Set the size of the transfer. Used with LSocket::SetParameter. Value = (int)Size
#define LSocket_TransferSize "TransferSize"
/// Set the type of protocol. Used with LSocket::SetParameter. Value = (char*)Protocol
#define LSocket_Protocol "Protocol"
#define LSocket_SetDelete "SetDelete"
// Functions
LgiNetFunc bool HaveNetConnection();
LgiNetFunc bool WhatsMyIp(LAutoString &Ip);
LgiExtern LString LIpStr(uint32_t ip);
LgiExtern uint32_t LIpHostInt(LString str);
/// Make md5 hash
LgiNetFunc void MDStringToDigest
(
/// Buffer to receive md5 hash
unsigned char digest[16],
/// Input string
char *Str,
/// Length of input string or -1 for null terminated
int Len = -1
);
/// Implementation of a network socket
class LgiNetClass LSocket :
public LSocketI,
public LStream
{
protected:
class LSocketImplPrivate *d;
// Methods
void Log(const char *Msg, ssize_t Ret, const char *Buf, ssize_t Len);
bool CreateUdpSocket();
public:
ssize_t BytesRead, BytesWritten;
/// Creates the class
LSocket(LStreamI *logger = 0, void *unused_param = 0);
/// Destroys the class
~LSocket();
/// Gets the active cancellation object
LCancel *GetCancel() override;
/// Sets the active cancellation object
void SetCancel(LCancel *c) override;
/// Returns the operating system handle to the socket.
OsSocket Handle(OsSocket Set = INVALID_SOCKET) override;
OsSocket ReleaseHandle();
/// Returns true if the internal state of the class is ok
bool IsOK();
/// Returns the IP address at this end of the socket.
bool GetLocalIp(char *IpAddr) override;
/// Returns the port at this end of the socket.
int GetLocalPort() override;
/// Gets the IP address at the remote end of the socket.
bool GetRemoteIp(uint32_t *IpAddr);
bool GetRemoteIp(char *IpAddr) override;
/// Gets the IP address at the remote end of the socket.
int GetRemotePort() override;
/// Gets the current timeout for operations in ms
int GetTimeout() override;
/// Sets the current timeout for operations in ms
void SetTimeout(int ms) override;
/// Returns whether there is data available for reading.
bool IsReadable(int TimeoutMs = 0) override;
/// Returns whether there is data available for reading.
bool IsWritable(int TimeoutMs = 0) override;
/// Returns if the socket is ready to accept a connection
bool CanAccept(int TimeoutMs = 0) override;
/// Returns if the socket is set to block
bool IsBlocking() override;
/// Set the socket to block
void IsBlocking(bool block) override;
/// Get the send delay setting
bool IsDelayed() override;
/// Set the send delay setting
void IsDelayed(bool Delay) override;
/// Opens a connection.
int Open
(
/// The name of the remote host.
const char *HostAddr,
/// The port on the remote host.
int Port
) override;
/// Returns true if the socket is connected.
bool IsOpen() override;
/// Closes the connection to the remote host.
int Close() override;
/// Binds on a given port.
bool Bind(int Port, bool reuseAddr = true);
/// Binds and listens on a given port for an incomming connection.
bool Listen(int Port = 0) override;
/// Accepts an incomming connection and connects the socket you pass in to the remote host.
bool Accept
(
/// The socket to handle the connection.
LSocketI *c
) override;
/// \brief Sends data to the remote host.
/// \return the number of bytes written or <= 0 on error.
ssize_t Write
(
/// Pointer to the data to write
const void *Data,
/// Numbers of bytes to write
ssize_t Len,
/// Flags to pass to send
int Flags = 0
) override;
inline ssize_t Write(const LString s) { return LStreamI::Write(s); }
/// \brief Reads data from the remote host.
/// \return the number of bytes read or <= 0 on error.
///
/// Generally the number of bytes returned is less than the buffer size. Depending on how much data
/// you are expecting you will need to keep reading until you get and end of field marker or the number
/// of bytes your looking for.
ssize_t Read
(
/// Pointer to the buffer to write output to
void *Data,
/// The length of the receive buffer.
ssize_t Len,
/// The flags to pass to recv
int Flags = 0
) override;
/// Returns the last error or 0.
int Error(void *Param = 0) override;
const char *GetErrorString() override;
/// Not supported
int64 GetSize() override { return -1; }
/// Not supported
int64 SetSize(int64 Size) override { return -1; }
/// Not supported
int64 GetPos() override { return -1; }
/// Not supported
int64 SetPos(int64 Pos) override { return -1; }
/// Gets called when the connection is disconnected
void OnDisconnect() override;
/// Gets called when data is received.
void OnRead(char *Data, ssize_t Len) override {}
/// Gets called when data is sent.
void OnWrite(const char *Data, ssize_t Len) override {}
/// Gets called when an error occurs.
void OnError(int ErrorCode, const char *ErrorDescription) override;
/// Gets called when some information is available.
void OnInformation(const char *Str) override {}
/// Parameter change handler.
int SetParameter
(
/// e.g. #LSocket_Log
int Param,
int Value
) { return false; }
/// Get UPD mode
bool GetUdp() override;
/// Set UPD mode
void SetUdp(bool isUdp = true) override;
/// Makes the socket able to broadcast
void SetBroadcast(bool isBroadcast = true);
/// Read UPD packet
int ReadUdp(void *Buffer, int Size, int Flags, uint32_t *Ip = 0, uint16_t *Port = 0) override;
/// Write UPD packet
int WriteUdp(void *Buffer, int Size, int Flags, uint32_t Ip, uint16_t Port) override;
bool AddMulticastMember(uint32_t MulticastIp, uint32_t LocalInterface);
bool SetMulticastInterface(uint32_t Interface);
// Impl
LStreamI *Clone() override
{
LSocket *s = new LSocket;
if (s)
s->SetCancel(GetCancel());
return s;
}
// Statics
/// Enumerates the current interfaces
struct Interface
{
LString Name;
uint32_t Ip4; // Host order...
uint32_t Netmask4;
bool IsLoopBack()
{
return Ip4 == 0x7f000001;
}
bool IsPrivate()
{
uint8_t h1 = (Ip4 >> 24) & 0xff;
if (h1 == 192)
{
uint8_t h2 = ((Ip4 >> 16) & 0xff);
return h2 == 168;
}
else if (h1 == 10)
{
return true;
}
else if (h1 == 172)
{
uint8_t h2 = ((Ip4 >> 16) & 0xff);
return h2 >= 16 && h2 <= 31;
}
return false;
}
bool IsLinkLocal()
{
uint8_t h1 = (Ip4 >> 24) & 0xff;
if (h1 == 169)
{
uint8_t h2 = ((Ip4 >> 16) & 0xff);
return h2 == 254;
}
return false;
}
};
static bool EnumInterfaces(LArray &Out);
};
class LgiNetClass LSocks5Socket : public LSocket
{
LAutoString Proxy;
int Port;
LAutoString UserName;
LAutoString Password;
protected:
bool Socks5Connected;
public:
LSocks5Socket();
LSocks5Socket &operator=(const LSocks5Socket &s)
{
Proxy.Reset(NewStr(s.Proxy));
UserName.Reset(NewStr(s.UserName));
Password.Reset(NewStr(s.Password));
Port = s.Port;
return *this;
}
// Connection
void SetProxy(char *proxy, int port, char *username, char *password);
void SetProxy(const LSocks5Socket *s);
int Open(const char *HostAddr, int port);
// Server
bool Listen(int Port) { return false; }
};
/// Uri parser
class LgiNetClass LUri
{
public:
LString sProtocol;
LString sUser;
LString sPass;
LString sHost;
int Port;
LString sPath;
LString sAnchor;
/// Parser for URI's.
LUri
(
/// Optional URI to start parsing
const char *uri = 0
);
~LUri();
bool IsProtocol(const char *p) { return sProtocol.Equals(p); }
bool IsHttp() { return sProtocol.Equals("http") || sProtocol.Equals("https"); }
bool IsFile() { return sProtocol.Equals("file"); }
void SetFile(LString Path) { Empty(); sProtocol = "file"; sPath = Path; }
const char *LocalPath();
operator bool();
/// Parse a URI into it's sub fields...
bool Set(const char *uri);
/// Re-constructs the URI
LString ToString();
/// Empty this object...
void Empty();
/// URL encode
LString EncodeStr
(
/// The string to encode
const char *s,
/// [Optional] Any extra characters you want encoded
const char *ExtraCharsToEncode = 0
);
/// URL decode
LString DecodeStr(const char *s);
/// Separate args into map
typedef LHashTbl,LString> StrMap;
StrMap Params();
LUri &operator =(const LUri &u);
LUri &operator =(const char *s) { Set(s); return *this; }
LUri &operator +=(const char *s);
};
/// Proxy settings lookup
class LgiNetClass LProxyUri : public LUri
{
public:
LProxyUri();
};
#define MAX_UDP_SIZE 512
class LUdpListener : public LSocket
{
LStream *Log;
LString Context;
public:
+ bool Status = false;
+
/*
If this isn't working on Linux, most likely it's a firewall issue.
For instance if you are running 'ufw' as your firewall you could allow packets through with:
sudo ufw allow ${port}/udp
*/
LUdpListener(LArray interface_ips, uint32_t mc_ip, uint16_t port, LStream *log = NULL) : Log(log)
{
SetUdp(true);
struct sockaddr_in addr;
ZeroObj(addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
#ifdef WINDOWS
- addr.sin_addr.S_un.S_addr = INADDR_ANY;
+ addr.sin_addr.S_un.S_addr = INADDR_ANY;
#elif defined(MAC)
- addr.sin_addr.s_addr = htonl(mc_ip);
+ addr.sin_addr.s_addr = htonl(mc_ip);
#else
- addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_addr.s_addr = INADDR_ANY;
#endif
- int r = bind(Handle(), (struct sockaddr*)&addr, sizeof(addr));
- if (r)
+ Status = bind(Handle(), (struct sockaddr*)&addr, sizeof(addr)) == 0;
+ if (!Status)
{
+ Context = "bind";
#ifdef WIN32
int err = WSAGetLastError();
- OnError(err, NULL);
+ #else
+ int err = errno;
#endif
+ OnError(err, GetErrorName(err));
- LgiTrace("Error: Bind on %s:%i = %i\n", LIpStr(ntohl(addr.sin_addr.s_addr)).Get(), port, r);
- }
- else
- {
- LgiTrace("Ok: Bind on %s:%i\n", LIpStr(ntohl(addr.sin_addr.s_addr)).Get(), port);
+ LgiTrace("Error: Bind on %s:%i\n", LIpStr(ntohl(addr.sin_addr.s_addr)).Get(), port);
}
if (mc_ip)
{
#if 0
AddMulticastMember(mc_ip, INADDR_ANY);
#else
for (auto ip: interface_ips)
AddMulticastMember(mc_ip, ip);
#endif
}
}
bool ReadPacket(LString &d, uint32_t &Ip, uint16_t &Port)
{
if (!IsReadable(10))
return false;
char Data[MAX_UDP_SIZE];
int Rd = ReadUdp(Data, sizeof(Data), 0, &Ip, &Port);
if (Rd <= 0)
return false;
d.Set(Data, Rd);
return true;
}
void OnError(int ErrorCode, const char *ErrorDescription)
{
+ LString s;
+ s.Printf("Error: %s - %i, %s\n", Context.Get(), ErrorCode, ErrorDescription);
if (Log)
- Log->Print("Error: %s - %i, %s\n", Context.Get(), ErrorCode, ErrorDescription);
+ Log->Print("%s", s.Get());
+ else
+ LgiTrace("%s", s.Get());
}
};
class LUdpBroadcast : public LSocket
{
// LArray Intf;
uint32_t SelectIf;
public:
LUdpBroadcast(uint32_t selectIf) : SelectIf(selectIf)
{
SetBroadcast();
SetUdp(true);
// EnumInterfaces(Intf);
}
bool BroadcastPacket(LString Data, uint32_t Ip, uint16_t Port)
{
return BroadcastPacket(Data.Get(), Data.Length(), Ip, Port);
}
bool BroadcastPacket(void *Ptr, size_t Size, uint32_t Ip, uint16_t Port)
{
if (Size > MAX_UDP_SIZE)
return false;
if (SelectIf)
{
struct in_addr addr;
addr.s_addr = htonl(SelectIf);
auto r = setsockopt(Handle(), IPPROTO_IP, IP_MULTICAST_IF, (char*)&addr, sizeof(addr));
if (r)
LgiTrace("%s:%i - set IP_MULTICAST_IF for '%s' failed: %i\n", _FL, LIpStr(SelectIf).Get(), r);
SelectIf = 0;
}
uint32_t BroadcastIp = Ip;
#if 0
LgiTrace("Broadcast %i.%i.%i.%i\n",
(BroadcastIp >> 24) & 0xff,
(BroadcastIp >> 16) & 0xff,
(BroadcastIp >> 8) & 0xff,
(BroadcastIp) & 0xff);
#endif
int wr = WriteUdp(Ptr, (int)Size, 0, BroadcastIp, Port);
return wr == Size;
}
};
#endif
diff --git a/include/lgi/win/LgiOsDefs.h b/include/lgi/win/LgiOsDefs.h
--- a/include/lgi/win/LgiOsDefs.h
+++ b/include/lgi/win/LgiOsDefs.h
@@ -1,309 +1,309 @@
//
// FILE: LgiOsDefs.h (Win32)
// AUTHOR: Matthew Allen
// DATE: 24/9/99
// DESCRIPTION: Lgi Win32 OS defines
//
// Copyright (C) 1999, Matthew Allen
// fret@memecode.com
//
#ifndef __LGI_OS_DEFS_H
#define __LGI_OS_DEFS_H
#pragma warning(disable : 4251 4275)
#define WIN32GTK 0
#define WINNATIVE 1
#define LGI_VIEW_HANDLE 1
#define LGI_VIEW_HASH 0
#ifndef WINDOWS
#error "Define WINDOWS in your project"
#endif
#ifdef _WIN64
#define LGI_64BIT 1
#ifndef WIN64
#define WIN64 1
#endif
#else
#define LGI_32BIT 1
#ifndef WIN32
#define WIN32 1
#endif
#endif
#include
#include
#include "lgi/common/LgiInc.h"
#include "lgi/common/LgiDefs.h"
#define _MSC_VER_VS2019 1920 // MSVC++ 16.0 (really all 192x values)
#define _MSC_VER_VS2017 1910 // MSVC++ 15.0
#define _MSC_VER_VS2015 1900 // MSVC++ 14.0
#define _MSC_VER_VS2013 1800 // MSVC++ 12.0
#define _MSC_VER_VS2012 1700 // MSVC++ 11.0
#define _MSC_VER_VS2010 1600 // MSVC++ 10.0
#define _MSC_VER_VS2008 1500 // MSVC++ 9.0
#define _MSC_VER_VS2005 1400 // MSVC++ 8.0
#define _MSC_VER_VS2003 1310 // MSVC++ 7.1
#define _MSC_VER_VC7 1300 // MSVC++ 7.0
#define _MSC_VER_VC6 1200 // MSVC++ 6.0
#define _MSC_VER_VC5 1100 // MSVC++ 5.0
#if _MSC_VER >= _MSC_VER_VS2019
#define _MSC_VER_STR "16"
#define _MSC_YEAR_STR "19"
#elif _MSC_VER >= _MSC_VER_VS2017
#define _MSC_VER_STR "15"
#define _MSC_YEAR_STR "17"
#elif _MSC_VER >= _MSC_VER_VS2015
#define _MSC_VER_STR "14"
#define _MSC_YEAR_STR "15"
#elif _MSC_VER >= _MSC_VER_VS2013
#define _MSC_VER_STR "12"
#define _MSC_YEAR_STR "13"
#elif _MSC_VER >= _MSC_VER_VS2012
#define _MSC_VER_STR "11"
#define _MSC_YEAR_STR "12"
#elif _MSC_VER >= _MSC_VER_VS2010
#define _MSC_VER_STR "10"
#define _MSC_YEAR_STR "10"
#else
#define _MSC_VER_STR "9"
#endif
//////////////////////////////////////////////////////////////////
// Includes
#define WIN32_LEAN_AND_MEAN
#include "winsock2.h"
#include "windows.h"
#include "SHELLAPI.H"
#include "COMMDLG.H"
#include
//////////////////////////////////////////////////////////////////
// Typedefs
typedef HWND OsWindow;
typedef HWND OsView;
typedef HANDLE OsThread;
typedef HANDLE OsProcess;
typedef char16 OsChar;
typedef HBITMAP OsBitmap;
typedef HDC OsPainter;
typedef HFONT OsFont;
#if _MSC_VER <= _MSC_VER_VC6
typedef unsigned long ULONG_PTR, *PULONG_PTR;
#define sprintf_s _snprintf
#endif
typedef BOOL (__stdcall *pSHGetSpecialFolderPathA)(HWND hwndOwner, LPSTR lpszPath, int nFolder, BOOL fCreate);
typedef BOOL (__stdcall *pSHGetSpecialFolderPathW)(HWND hwndOwner, LPWSTR lpszPath, int nFolder, BOOL fCreate);
typedef int (__stdcall *pSHFileOperationA)(LPSHFILEOPSTRUCTA lpFileOp);
typedef int (__stdcall *pSHFileOperationW)(LPSHFILEOPSTRUCTW lpFileOp);
typedef int (__stdcall *p_vscprintf)(const char *format, va_list argptr);
#include "lgi/common/Message.h"
#include "lgi/common/LgiUiBase.h"
class LgiClass OsAppArguments
{
LAutoWString CmdLine;
void _Default();
public:
HINSTANCE hInstance;
DWORD Pid;
char16 *lpCmdLine;
int nCmdShow;
OsAppArguments();
- OsAppArguments(int Args, char **Arg);
+ OsAppArguments(int Args, const char **Arg);
OsAppArguments &operator =(OsAppArguments &p);
void Set(char *Utf);
- void Set(int Args, char **Arg);
+ void Set(int Args, const char **Arg);
};
//////////////////////////////////////////////////////////////////
// Defines
#define IsWin9x (LApp::Win9x)
#define DefaultOsView(t) NULL
#define GWL_LGI_MAGIC 8
#define GWL_EXTRA_BYTES 12
// Key redefs
enum LgiKeys {
LK_TAB = VK_TAB,
LK_RETURN = VK_RETURN,
LK_SPACE = VK_SPACE,
LK_DELETE = VK_DELETE,
LK_ESCAPE = VK_ESCAPE,
LK_PAGEUP = VK_PRIOR,
LK_PAGEDOWN = VK_NEXT,
LK_BACKSPACE = VK_BACK,
LK_F1 = VK_F1,
LK_F2 = VK_F2,
LK_F3 = VK_F3,
LK_F4 = VK_F4,
LK_F5 = VK_F5,
LK_F6 = VK_F6,
LK_F7 = VK_F7,
LK_F8 = VK_F8,
LK_F9 = VK_F9,
LK_F10 = VK_F10,
LK_F11 = VK_F11,
LK_F12 = VK_F12,
LK_LEFT = VK_LEFT,
LK_RIGHT = VK_RIGHT,
LK_UP = VK_UP,
LK_DOWN = VK_DOWN,
LK_HOME = VK_HOME,
LK_END = VK_END,
LK_INSERT = VK_INSERT,
LK_SHIFT = VK_SHIFT,
LK_ALT = VK_MENU,
LK_CTRL = VK_CONTROL,
LK_DECIMAL = VK_DECIMAL,
LK_COMMA = VK_OEM_COMMA, // , .
LK_MINUS = VK_OEM_MINUS, // - _
LK_EQUALS = VK_OEM_PLUS, // = +
LK_SEMI_COLON = VK_OEM_1, // ; :
LK_SLASH = VK_OEM_2, // / ?
LK_TILDE = VK_OEM_3, // ~
LK_OPEN_BRACKET = VK_OEM_4, // [ {
LK_BACK_SLASH = VK_OEM_5, // \ |
LK_CLOSE_BRACKET = VK_OEM_6, // ] }
LK_SINGLE_QUOTE = VK_OEM_7, // ' "
};
// Sleep the current thread
LgiFunc void LSleep(DWORD i);
// Process
typedef DWORD OsProcessId;
LgiExtern HINSTANCE _lgi_app_instance;
#define LProcessInst() _lgi_app_instance
extern p_vscprintf lgi_vscprintf;
#define LGetCurrentProcess() GetCurrentProcessId()
// Threads
typedef DWORD OsThreadId;
typedef CRITICAL_SECTION OsSemaphore;
#define LGetCurrentThread() GetCurrentThread()
// #define GetCurrentThreadId() GetCurrentThreadId()
// Socket/Network
#define ValidSocket(s) ((s) != INVALID_SOCKET)
typedef SOCKET OsSocket;
#define LGI_GViewMagic 0x14412662
#define LGI_FileDropFormat "CF_HDROP"
#define LGI_StreamDropFormat CFSTR_FILEDESCRIPTORW
#define LGI_WideCharset "ucs-2"
#define LPrintfInt64 "%I64i"
#define LPrintfHex64 "%I64x"
#if LGI_64BIT
#define LPrintfSizeT "%I64u"
#define LPrintfSSizeT "%I64d"
#else
#define LPrintfSizeT "%u"
#define LPrintfSSizeT "%d"
#endif
#define LGI_IllegalFileNameChars "\t\r\n/\\:*?\"<>|"
#define MK_LEFT MK_LBUTTON
#define MK_RIGHT MK_RBUTTON
#define MK_MIDDLE MK_MBUTTON
#define MK_CTRL MK_CONTROL
// Stupid mouse wheel defines don't work. hmmm
#define WM_MOUSEWHEEL 0x020A
#define WHEEL_DELTA 120
#ifndef SPI_GETWHEELSCROLLLINES
#define SPI_GETWHEELSCROLLLINES 104
#endif
// Window flags
#define GWF_VISIBLE WS_VISIBLE
#define GWF_DISABLED WS_DISABLED
#define GWF_BORDER WS_BORDER
#define GWF_TABSTOP WS_TABSTOP
#define GWF_FOCUS 0x00000001
#define GWF_OVER 0x00000002
#define GWF_DROP_TARGET 0x00000004
#define GWF_SUNKEN 0x00000008
#define GWF_FLAT 0x00000010
#define GWF_RAISED 0x00000020
#define GWF_DIALOG 0x00000040
#define GWF_DESTRUCTOR 0x00000080
#define GWF_QUIT_WND 0x00000100
#define GWF_SYS_BORDER 0x00000200 // ask the system to draw the border
// Widgets
#define DialogToPixelX(i) (((i)*Bx)/4)
#define DialogToPixelY(i) (((i)*By)/8)
#define PixelToDialogX(i) (((i)*4)/Bx)
#define PixelToDialogY(i) (((i)*8)/By)
#define DIALOG_X 1.56
#define DIALOG_Y 1.85
#define CTRL_X 1.50
#define CTRL_Y 1.64
// Directories
#define DIR_CHAR '\\'
#define DIR_STR "\\"
#define EOL_SEQUENCE "\r\n"
#define LGI_PATH_SEPARATOR ";"
#define LGI_ALL_FILES "*.*"
#define LGI_LIBRARY_EXT "dll"
#define LGI_EXECUTABLE_EXT ".exe"
/////////////////////////////////////////////////////////////////////////////////////
// Typedefs
typedef HWND OsView;
/////////////////////////////////////////////////////////////////////////////////////
// Externs
LgiFunc class LViewI *LWindowFromHandle(OsView hWnd);
LgiFunc int GetMouseWheelLines();
LgiFunc int WinPointToHeight(int Pt, HDC hDC = NULL);
LgiFunc int WinHeightToPoint(int Ht, HDC hDC = NULL);
LgiExtern class LString WinGetSpecialFolderPath(int Id);
/// Convert a string d'n'd format to an OS dependant integer.
LgiFunc int FormatToInt(LString s);
/// Convert a Os dependant integer d'n'd format to a string.
LgiFunc char *FormatToStr(int f);
extern bool LgiToWindowsCursor(OsView Hnd, LCursor Cursor);
#ifdef _MSC_VER
#define snprintf _snprintf
//#define vsnprintf _vsnprintf
#define vsnwprintf _vsnwprintf
#define stricmp _stricmp
#define strlwr _strlwr
#define strnicmp _strnicmp
#define vsnprintf _vsnprintf_s
// #define getcwd _getcwd
#endif
#define atoi64 _atoi64
#ifdef __GNUC__
// #define stricmp strcasecmp
// #define strnicmp strncasecmp
#define vsnprintf_s vsnprintf
#define swprintf_s snwprintf
#define vsprintf_s vsnprintf
#if !defined(_TRUNCATE)
#define _TRUNCATE ((size_t)-1)
#endif
#endif
#endif
diff --git a/src/win/Lgi/App.cpp b/src/win/Lgi/App.cpp
--- a/src/win/Lgi/App.cpp
+++ b/src/win/Lgi/App.cpp
@@ -1,918 +1,918 @@
#define _WIN32_WINNT 0x600
#ifndef _MSC_VER
#include
#endif
#include
#include
#include
#include
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/SkinEngine.h"
#include "lgi/common/DocView.h"
#include "lgi/common/Token.h"
#include "lgi/common/Css.h"
#include "lgi/common/SpellCheck.h"
#include "lgi/common/Json.h"
#include "AppPriv.h"
// Don't have a better place to put this...
const char LSpellCheck::Delimiters[] =
{
' ', '\t', '\r', '\n', ',', ',', '.', ':', ';',
'{', '}', '[', ']', '!', '@', '#', '$', '%', '^', '&', '*',
'(', ')', '_', '-', '+', '=', '|', '\\', '/', '?', '\"',
0
};
HINSTANCE _lgi_app_instance = 0;
extern LPTOP_LEVEL_EXCEPTION_FILTER _PrevExceptionHandler;
OsAppArguments::OsAppArguments()
{
_Default();
}
-OsAppArguments::OsAppArguments(int Args, char **Arg)
+OsAppArguments::OsAppArguments(int Args, const char **Arg)
{
_Default();
Set(Args, Arg);
}
OsAppArguments &OsAppArguments::operator =(OsAppArguments &p)
{
hInstance = p.hInstance;
Pid = p.Pid;
nCmdShow = p.nCmdShow;
lpCmdLine = 0;
CmdLine.Reset(NewStrW(p.lpCmdLine));
lpCmdLine = CmdLine;
return *this;
}
void OsAppArguments::_Default()
{
hInstance = 0;
lpCmdLine = 0;
Pid = GetCurrentProcessId();
nCmdShow = SW_RESTORE;
}
-void OsAppArguments::Set(int Args, char **Arg)
+void OsAppArguments::Set(int Args, const char **Arg)
{
LStringPipe p;
for (int i=0; i_ExceptionFilter(e, LAppInst->d->ProductId);
else
LgiTrace("_ExceptionFilter_Redir error: No application ptr.\n");
return 0;
}
/////////////////////////////////////////////////////////////////////////////
LSkinEngine *LApp::SkinEngine = NULL;
LMouseHook *LApp::MouseHook = NULL;
LMouseHook *LApp::GetMouseHook()
{
return MouseHook;
}
static LAutoString ParseStr(LPointer &p, bool Pad = true)
{
char16 *Key = p.w;
// Skip 'key' string
while (*p.w)
p.w++;
// Skip NULL
p.w++;
if (Pad)
{
// Align to 32-bit boundry
while ((NativeInt)p.u8 & 3)
p.u8++;
}
return LAutoString(WideToUtf8(Key));
}
static LAutoString ParseVer(void *Resource, char *Part)
{
LToken Parts(Part, ".");
if (Parts.Length() == 3)
{
LPointer p;
p.u8 = (uint8_t*)Resource;
uint16 Len = *p.u16++;
uint16 ValueLen = *p.u16++;
uint16 Type = *p.u16++;
LAutoString Key = ParseStr(p);
// Read VS_FIXEDFILEINFO structure
DWORD dwSig = *p.u32++;
DWORD dwStrucVersion = *p.u32++;
DWORD dwFileVersionMS = *p.u32++;
DWORD dwFileVersionLS = *p.u32++;
DWORD dwProductVersionMS = *p.u32++;
DWORD dwProductVersionLS = *p.u32++;
DWORD dwFileFlagsMask = *p.u32++;
DWORD dwFileFlags = *p.u32++;
DWORD dwFileOS = *p.u32++;
DWORD dwFileType = *p.u32++;
DWORD dwFileSubtype = *p.u32++;
DWORD dwFileDateMS = *p.u32++;
DWORD dwFileDateLS = *p.u32++;
// Align to 32-bit boundry
while ((NativeInt)p.u8 & 3)
p.u8++;
// Children
while (p.u8 < (uint8_t*)Resource + Len)
{
// Read StringFileInfo structures...
uint8_t *fStart = p.u8;
uint16 fLength = *p.u16++;
uint16 fValueLength = *p.u16++;
uint16 fType = *p.u16++;
LAutoString fKey = ParseStr(p);
if (strcmp(fKey, "StringFileInfo"))
break;
while (p.u8 < fStart + fLength)
{
// Read StringTable entries
uint8_t *tStart = p.u8;
uint16 tLength = *p.u16++;
uint16 tValueLength = *p.u16++;
uint16 tType = *p.u16++;
LAutoString tKey = ParseStr(p);
while (p.u8 < tStart + tLength)
{
// Read String entries
uint8_t *sStart = p.u8;
uint16 sLength = *p.u16++;
uint16 sValueLength = *p.u16++;
uint16 sType = *p.u16++;
LAutoString sKey = ParseStr(p);
LAutoString sValue;
if (p.u8 < sStart + sLength)
sValue = ParseStr(p);
if (!stricmp(Parts[0], fKey) &&
!stricmp(Parts[1], tKey) &&
!stricmp(Parts[2], sKey))
{
return sValue;
}
}
}
}
}
return LAutoString();
}
void LInvalidParam(const wchar_t * expression,
const wchar_t * function,
const wchar_t * file,
unsigned int line,
uintptr_t pReserved)
{
LgiTrace("Invalid Parameter: %S (%S @ %S:%i)\n", expression, function, file, line);
}
/////////////////////////////////////////////////////////////////////////////
#include
#include
extern int MouseRollMsg;
typedef HRESULT (CALLBACK *fDllGetVersion)(DLLVERSIONINFO *);
LApp::LApp(OsAppArguments &AppArgs, const char *AppName, LAppArguments *Opts)
{
// LApp instance
LAssert(TheApp == 0);
TheApp = this;
LAssert(AppName != NULL);
Name(AppName);
int64 Time = LCurrentTime();
#define DumpTime(str) /* \
{ int64 n = LCurrentTime(); \
LgiTrace("%s=%ims\n", str, (int)(n-Time)); \
Time = n; } */
// Sanity Checks
LAssert(sizeof(int8) == 1);
LAssert(sizeof(uint8_t) == 1);
LAssert(sizeof(int16) == 2);
LAssert(sizeof(uint16) == 2);
LAssert(sizeof(int32) == 4);
LAssert(sizeof(uint32_t) == 4);
LAssert(sizeof(int64) == 8);
LAssert(sizeof(uint64) == 8);
LAssert(sizeof(char16) == 2);
LAssert(LDisplayString::FScale == (1 << LDisplayString::FShift));
DumpTime("start");
// Private data
d = new LAppPrivate(this);
char Mime[256];
sprintf_s(Mime, sizeof(Mime), "application/x-%s", AppName);
d->Mime.Reset(NewStr(Mime));
DumpTime("priv");
InitializeCriticalSection(&StackTraceSync);
if (!Opts || Opts->NoCrashHandler == false)
{
// Setup exception handler
HRSRC hRsrc = ::FindResource(NULL, MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION);
HGLOBAL hGlobal = ::LoadResource(NULL, hRsrc);
LPVOID pVersionResource = ::LockResource(hGlobal);
LAutoString ProductName, ProductVer;
// replace "040904e4" with the language ID of your resources
if (pVersionResource)
{
ProductName = ParseVer(pVersionResource, "StringFileInfo.0c0904b0.ProductName");
ProductVer = ParseVer(pVersionResource, "StringFileInfo.0c0904b0.ProductVersion");
}
if (ProductName && ProductVer)
{
char s[256];
sprintf_s(s, sizeof(s), "%s-%s", ProductName.Get(), ProductVer.Get());
d->ProductId.Reset(NewStr(s));
}
#if !defined(_DEBUG)
_PrevExceptionHandler = SetUnhandledExceptionFilter(_ExceptionFilter_Redir);
#endif
_set_invalid_parameter_handler(LInvalidParam);
}
DumpTime("exception handler");
// Initialize windows dll's
OleInitialize(NULL);
CoInitialize(NULL);
InitCommonControls();
{
/*
LLibrary ComCtl32("ComCtl32.dll");
DLLVERSIONINFO info;
ZeroObj(info);
info.cbSize = sizeof(info);
fDllGetVersion DllGetVersion = (fDllGetVersion)ComCtl32.GetAddress("DllGetVersion");
if (DllGetVersion)
{
HRESULT ret = DllGetVersion(&info);
d->ThemeAware = info.dwMajorVersion >= 6;
LgiTrace("ComCtl32.dll v%i.%i found (ret=%x)\n", info.dwMajorVersion, info.dwMinorVersion, ret);
)
*/
LArray Ver;
LGetOs(&Ver);
if (Ver.Length() > 1)
{
// LgiTrace("Windows v%i.%i\n", Ver[0], Ver[1]);
if (Ver[0] < 6)
{
d->ThemeAware = false;
}
}
#ifdef _MSC_VER
if (!d->ThemeAware)
{
SetThemeAppProperties(0);
}
#endif
}
DumpTime("init common ctrls");
// Setup LGI Sub-systems
LFontSystem::Inst();
DumpTime("font sys");
d->FileSystem = new LFileSystem;
DumpTime("file sys");
d->GdcSystem = new GdcDevice;
DumpTime("gdc");
// Vars...
AppWnd = 0;
SetAppArgs(AppArgs);
DumpTime("vars");
// System font
LFontType SysFontType;
if (SysFontType.GetSystemFont("System"))
{
SystemNormal = SysFontType.Create();
if (SystemNormal)
{
// Force load
SystemNormal->Create();
SystemNormal->WarnOnDelete(true);
}
else
{
LgiMsg(0, "Error: SysFontType.Create() failed.", "Lgi Error");
LExitApp();
}
SystemBold = SysFontType.Create();
if (SystemBold)
{
SystemBold->Bold(true);
SystemBold->Create();
}
}
else
{
LgiMsg(0, "Error: GetSystemFont failed.", "Lgi Error");
LExitApp();
}
DumpTime("fonts");
// Other vars and init
hNormalCursor = LoadCursor(NULL, IDC_ARROW);
LRandomize((uint) (LCurrentTime()*GetCurrentThreadId()));
MouseRollMsg = RegisterWindowMessage(L"MSWHEEL_ROLLMSG");
DumpTime("cursor/rand/msg");
LColour::OnChange();
DumpTime("colours");
// Setup mouse hook
MouseHook = new LMouseHook;
DumpTime("ms hook");
if
(
(!Opts || !Opts->NoSkin)
&&
!GetOption("noskin")
)
{
extern LSkinEngine *CreateSkinEngine(LApp *App);
SkinEngine = CreateSkinEngine(this);
}
DumpTime("skin");
}
LApp::~LApp()
{
DeleteObj(AppWnd);
SystemNormal->WarnOnDelete(false);
DeleteObj(SystemNormal);
DeleteObj(SystemBold);
DeleteObj(MouseHook);
TheApp = 0;
DeleteObj(SkinEngine);
DeleteObj(LFontSystem::Me);
DeleteObj(d->FileSystem);
DeleteObj(d->GdcSystem);
d->Classes.DeleteObjects();
CoUninitialize();
OleUninitialize();
DeleteObj(d);
DeleteCriticalSection(&StackTraceSync);
}
bool LApp::IsOk()
{
bool Status = (this != 0) &&
(d != 0) &&
(d->FileSystem != 0) &&
(d->GdcSystem != 0);
if (!Status)
LAssert(!"Hash table error");
return Status;
}
int LApp::GetCpuCount()
{
SYSTEM_INFO si;
ZeroObj(si);
GetSystemInfo(&si);
return si.dwNumberOfProcessors ? si.dwNumberOfProcessors : -1;
}
OsThread LApp::_GetGuiThread()
{
return d->GuiThread;
}
bool LApp::InThread()
{
auto GuiId = GetGuiThreadId();
auto MyId = GetCurrentThreadId();
return GuiId == MyId;
}
OsThreadId LApp::GetGuiThreadId()
{
return GetThreadId(d->GuiThread);
}
LApp::ClassContainer *LApp::GetClasses()
{
return IsOk() ? &d->Classes : 0;
}
OsAppArguments *LApp::GetAppArgs()
{
return IsOk() ? &d->Args : 0;
}
const char *LApp::GetArgumentAt(int n)
{
if (d->Args.lpCmdLine)
{
char16 *s = d->Args.lpCmdLine;
for (int i=0; i<=n; i++)
{
char16 *e = 0;
while (*s && strchr(WhiteSpace, *s)) s++;
if (*s == '\'' || *s == '\"')
{
char16 Delim = *s++;
e = StrchrW(s, Delim);
}
else
{
for (e = s; *e && !strchr(WhiteSpace, *e); e++)
;
}
if (i == n)
{
return WideToUtf8(s, e - s);
}
s = *e ? e + 1 : e;
}
}
return 0;
}
bool LApp::GetOption(const char *Option, LString &Buf)
{
if (!ValidStr(Option))
{
return false;
}
char16 *c = d->Args.lpCmdLine;
char16 *Opt = Utf8ToWide(Option);
auto OptLen = StrlenW(Opt);
while (c && *c)
{
if (*c == '/' || *c == '-')
{
c++;
char16 *e = c;
while (*e && (IsAlpha(*e)) || StrchrW(L"_-", *e))
e++;
if (e - c == OptLen &&
!StrnicmpW(c, Opt, OptLen))
{
c += OptLen;
if (*c)
{
// skip leading whitespace
while (*c && strchr(WhiteSpace, *c))
{
c++;
}
// write str out if they want it
char16 End = (*c == '\'' || *c == '\"') ? *c++ : ' ';
char16 *e = StrchrW(c, End);
if (!e) e = c + StrlenW(c);
Buf.SetW(c, e-c);
}
// yeah we got the option
DeleteArray(Opt);
return true;
}
}
c = StrchrW(c, ' ');
if (c) c++;
}
DeleteArray(Opt);
return false;
}
bool LApp::GetOption(const char *Option, char *Dest, int DestLen)
{
LString Buf;
if (GetOption(Option, Buf))
{
if (Dest)
strcpy_s(Dest, DestLen, &Buf[0]);
return true;
}
return false;
}
// #include "LInput.h"
void LApp::SetAppArgs(OsAppArguments &AppArgs)
{
d->Args = AppArgs;
}
void LApp::OnCommandLine()
{
char WhiteSpace[] = " \r\n\t";
char *CmdLine = WideToUtf8(d->Args.lpCmdLine);
if (ValidStr(CmdLine))
{
// LgiTrace("CmdLine='%s'\n", CmdLine);
LArray Files;
char *Delim = "\'\"";
char *s;
for (s = CmdLine; *s; )
{
// skip ws
while (*s && strchr(WhiteSpace, *s)) s++;
// read to end of token
char *e = s;
if (strchr(Delim, *s))
{
char Delim = *s++;
e = strchr(s, Delim);
if (!e)
e = s + strlen(s);
}
else
{
for (; *e && !strchr(WhiteSpace, *e); e++)
;
}
char *Arg = NewStr(s, e - s);
if (Arg)
{
if (LFileExists(Arg))
{
Files.Add(Arg);
}
else
{
DeleteArray(Arg);
}
}
// next
s = (*e) ? e + 1 : e;
}
// call app
if (Files.Length() > 0)
{
OnReceiveFiles(Files);
}
// clear up
Files.DeleteArrays();
}
DeleteArray(CmdLine);
}
void LApp::OnUrl(const char *Url)
{
if (AppWnd)
AppWnd->OnUrl(Url);
}
void LApp::OnReceiveFiles(LArray &Files)
{
if (AppWnd)
AppWnd->OnReceiveFiles(Files);
}
int32 LApp::GetMetric(LSystemMetric Metric)
{
int32 Status = 0;
switch (Metric)
{
case LGI_MET_DECOR_X:
{
Status = GetSystemMetrics(SM_CXFRAME) * 2;
break;
}
case LGI_MET_DECOR_Y:
{
Status = GetSystemMetrics(SM_CYFRAME) * 2;
Status += GetSystemMetrics(SM_CYCAPTION);
break;
}
case LGI_MET_DECOR_CAPTION:
{
Status = GetSystemMetrics(SM_CYCAPTION);
break;
}
case LGI_MET_MENU:
{
Status = GetSystemMetrics(SM_CYMENU);
break;
}
case LGI_MET_THEME_AWARE:
{
Status = d->ThemeAware;
break;
}
}
return Status;
}
LViewI *LApp::GetFocus()
{
HWND h = ::GetFocus();
if (h)
{
return LWindowFromHandle(h);
}
return 0;
}
HINSTANCE LApp::GetInstance()
{
if (this && IsOk())
{
return d->Args.hInstance;
}
return (HINSTANCE)GetCurrentProcess();
}
OsProcessId LApp::GetProcessId()
{
if (this)
{
return IsOk() ? d->Args.Pid : 0;
}
return GetCurrentProcessId();
}
int LApp::GetShow()
{
return IsOk() ? d->Args.nCmdShow : 0;
}
bool LApp::Run(OnIdleProc IdleCallback, void *IdleParam)
{
MSG Msg = {0};
bool status = true;
OnCommandLine();
if (IdleCallback)
{
bool DontWait = true;
while (!d->QuitReceived)
{
while (1)
{
bool Status;
if (DontWait)
{
Status = PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE) != 0;
}
else
{
Status = GetMessage(&Msg, NULL, 0, 0) > 0;
DontWait = true;
}
#if 0
char m[256];
sprintf_s(m, sizeof(m), "Msg=%i hwnd=%p %i,%i\n", Msg.message, Msg.hwnd, Msg.wParam, Msg.lParam);
OutputDebugStringA(m);
#endif
if (!Status || Msg.message == WM_QUIT)
break;
#ifdef _DEBUG
int64 Last = LCurrentTime();
#endif
TranslateMessage(&Msg);
DispatchMessage(&Msg);
#ifdef _DEBUG
int64 Now = LCurrentTime();
if (Now - Last > 10000)
{
LgiTrace("%s:%i - Msg Loop Blocked: %i ms (Msg: 0x%.4x)\n",
_FL, (int) (Now - Last), Msg.message);
}
#endif
}
if (Msg.message == WM_QUIT)
{
d->QuitReceived = true;
}
else
{
DontWait = IdleCallback(IdleParam);
}
}
}
else
{
while (!d->QuitReceived && GetMessage(&Msg, NULL, 0, 0) > 0)
{
#ifdef _DEBUG
int64 Last = LCurrentTime();
#endif
TranslateMessage(&Msg);
DispatchMessage(&Msg);
#ifdef _DEBUG
int64 Now = LCurrentTime();
if (Now - Last > 1000)
{
LgiTrace("%s:%i - Msg Loop Blocked: %i ms (Msg: 0x%.4x)\n",
_FL,
(int) (Now - Last), Msg.message);
}
#endif
}
}
return Msg.message != WM_QUIT;
}
bool LApp::Yield()
{
MSG Msg = {0};
while ( PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE) &&
Msg.message != WM_QUIT)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
if (Msg.message == WM_QUIT)
{
d->QuitReceived = true;
}
return Msg.message != WM_QUIT;
}
void LApp::Exit(int Code)
{
if (Code)
{
exit(0);
}
else
{
LAssert(InThread()); // Doesn't work for worker threads.
PostQuitMessage(Code);
}
}
LString LApp::GetFileMimeType(const char *File)
{
return LGetFileMimeType(File);
}
bool LApp::GetAppsForMimeType(char *Mime, LArray &Apps)
{
LAssert(!"Not impl.");
return false;
}
LSymLookup *LApp::GetSymLookup()
{
if (!this)
return 0;
if (!d->SymLookup)
d->SymLookup = new LSymLookup;
return d->SymLookup;
}
bool LApp::IsWine()
{
if (d->LinuxWine < 0)
{
HMODULE hntdll = GetModuleHandle(L"ntdll.dll");
if (hntdll)
{
typedef const char * (CDECL *pwine_get_version)(void);
pwine_get_version wine_get_version = (pwine_get_version)GetProcAddress(hntdll, "wine_get_version");
d->LinuxWine = wine_get_version != 0;
}
}
return d->LinuxWine > 0;
}
bool LApp::IsElevated()
{
bool fRet = false;
HANDLE hToken = NULL;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken ))
{
TOKEN_ELEVATION Elevation;
DWORD cbSize = sizeof(Elevation);
if (GetTokenInformation(hToken, TokenElevation, &Elevation, sizeof(Elevation), &cbSize))
fRet = Elevation.TokenIsElevated != 0;
}
if (hToken)
CloseHandle(hToken);
return fRet;
}
LFontCache *LApp::GetFontCache()
{
if (!d->FontCache)
d->FontCache.Reset(new LFontCache(SystemNormal));
return d->FontCache;
}