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; }