/* * LaGUI: A graphical application framework. * Copyright (C) 2022-2023 Wu Yiming * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #define _CRT_SECURE_NO_WARNINGS #define _GNU_SOURCE #include #ifdef LA_USE_GLES #include #else #include #include #endif #include "ft2build.h" #include "freetype/freetype.h" #include "la_icon.h" #include #include #include #ifdef __linux__ #include #include #include #include #define SYSWINDOW Window #ifdef LA_USE_GLES #define SYSGLCONTEXT EGLContext #else #define SYSGLCONTEXT GLXContext #endif #define SYSTEMDC DC #define SYSTEMRC RC #define SYSTEMDISPLAY Display #define SYSLOCK pthread_spinlock_t #endif #ifdef _WIN32 #include #include "Shlwapi.h" #define SYSWINDOW HWND #define SYSGLCONTEXT HGLRC #define SYSTEMDC HDC #define SYSTEMRC HRC #define SYSTEMDISPLAY int #define PATH_MAX 4096 #define SYSLOCK CRITICAL_SECTION #endif #define luajit_c #include "lua.h" #include "lauxlib.h" #include "lualib.h" #include "luajit.h" void tnsLuaInit(lua_State* L); #define NEED_STRUCTURE(a) \ typedef struct _##a a; #define STRUCTURE(a) \ typedef struct _##a a;\ struct _##a #define lengthof(a) \ (sizeof(a)/sizeof(a[0])) #ifdef _WIN32 #define LA_PATH_SEP '\\' #define LA_PATH_SEPSTR "\\" #define realpath(N,R) _fullpath((R),(N),PATH_MAX) #else #define LA_PATH_SEP '/' #define LA_PATH_SEPSTR "/" #endif #define DBL_TRIANGLE_LIM 1e-11 #define DBL_EDGE_LIM 1e-9 #ifndef LAGUI_GIT_BRANCH #define LAGUI_GIT_BRANCH "Release 1" #endif // No need to show hash when not compiled from git repo. //#ifndef LAGUI_GIT_HASH //#define LAGUI_GIT_HASH "?" //#endif #define LA_HYPER_CREATED_TIME(hi) \ hi->TimeCreated.Year,hi->TimeCreated.Month,hi->TimeCreated.Day,hi->TimeCreated.Hour,hi->TimeCreated.Minute,hi->TimeCreated.Second typedef double real; typedef unsigned long long u64bit; typedef unsigned int u32bit; typedef unsigned short u16bit; typedef unsigned short ushort; typedef unsigned char u8bit; typedef struct _laListSingle laListSingle; struct _laListSingle { void* pNext; }; typedef struct _laListHandle laListHandle; struct _laListHandle { void* pFirst; void* pLast; }; typedef struct _laListWithPivot laListWithPivot; struct _laListWithPivot { void* pFirst; void* pLast; void* Pivot; }; typedef struct _laListItem laListItem; struct _laListItem { void* pPrev; void* pNext; }; typedef struct _laListItem2 laListItem2; struct _laListItem2 { void* O1; void* O2; void* pPrev; void* pNext; }; typedef struct _laListItem3 laListItem3; struct _laListItem3 { void* O1; void* O2; void* O3; void* O4; void* pPrev; void* pNext; }; NEED_STRUCTURE(laSafeString); STRUCTURE(laAuthorInfo) { laListItem Item; laSafeString* Name; laSafeString* CopyrightString; }; STRUCTURE(laTimeInfo) { u16bit Year;//Also Used As Timer [ms] counter u8bit Month; u8bit Day; u8bit Hour; u8bit Minute; u8bit Second; }; NEED_STRUCTURE(laPropContainer); typedef struct _laUID laUID; struct _laUID { char String[32];//a simplified uuid, example: 0E3F9BA4802FDDC2-20160601123546 [\0] }; typedef struct _laListItemPointer laListItemPointer; struct _laListItemPointer { void* pPrev; void* pNext; void* p; }; typedef struct _laItemUserLinker laItemUserLinker; typedef void(*laUserRemoveFunc)(void* This, laItemUserLinker* iul); NEED_STRUCTURE(laProp); struct _laItemUserLinker { laListItemPointer Pointer; laUserRemoveFunc Remove; laProp* Which; void* Additional; unsigned int FrameDistinguish; int ForceRecalc; }; typedef struct _laItemUserLinkerLocal laItemUserLinkerLocal; struct _laItemUserLinkerLocal { laItemUserLinker Link; void* Instance; }; typedef struct _laElementListItem laElementListItem; struct _laElementListItem { laListItem Item; void* Ext; }; typedef struct _laListNonRecursiveRoot laListNonRecursiveRoot; struct _laListNonRecursiveRoot { laListHandle NSItems; }; typedef int(*laCompareFunc)(void*, void*); typedef void(*laListDoFunc)(void*); typedef void(*laListNonRecursiveDoFunc)(laListNonRecursiveRoot*, void*, void*);//item,custom typedef void(*laListNonRecursiveCopyFunc)(laListNonRecursiveRoot*, void*, void*, void*);//old,new,custom typedef void(*laListDoFuncArgp)(void*, void*); typedef void(*laCopyListFunc)(void*, void*); typedef void(*laListCustomDataRemover)(void*); //typedef void(*ListMatcherFunc)(void*,void*);//gotten value,enumed curent lst item. typedef struct _laListNonRecursiveItem laListNonRecursiveItem; struct _laListNonRecursiveItem { laListItem Item; laListHandle handle; laListHandle *ToHandle;//This Is Pointer! laListNonRecursiveDoFunc func; laListNonRecursiveCopyFunc CopyFunc; laListCustomDataRemover remover; void* CustomData; int bFreeList; int SizeEachNode; }; typedef struct _laHash256 laHash256; struct _laHash256 { laListHandle Entries[256]; }; typedef struct _laHash65536 laHash65536; struct _laHash65536 { laListHandle Entries[65536]; }; typedef struct _laSafeString laSafeString; struct _laSafeString { laListItem Item; char * Ptr; }; typedef struct _laSafeStringCollection laSafeStringCollection; struct _laSafeStringCollection { laListHandle SafeStrings; }; typedef struct _laStringSplitor laStringSplitor; struct _laStringSplitor { int NumberParts; laListHandle parts; }; typedef struct _laStringPart laStringPart; struct _laStringPart { laListItem Item; char * Content; int IntValue; real FloatValue; char Type; }; STRUCTURE(laStringLine) { laListItem Item; uint32_t Buf[1024];//unicode }; STRUCTURE(laStringEdit) { laListHandle Lines; int CursorLine, CursorBefore, CursorPreferBefore; int BeginLine, BeginBefore; int EndLine, EndBefore; int _BeginLine, _BeginBefore; // selection order int _EndLine, _EndBefore; int TotalLines; int ViewStartLine, ViewStartCol; int ViewHeight, ViewWidth; int MouseSelecting; }; #define LA_SWAP(T,x,y) \ { T SWAP = x; x = y; y = SWAP; } #define LA_MEMORY_POOL_1MB 1048576 #define LA_MEMORY_POOL_128MB 134217728 #define LA_MEMORY_POOL_256MB 268435456 #define LA_MEMORY_POOL_512MB 536870912 STRUCTURE(laMemoryPool) { laListItem Item; int NodeSize; int NextCount; int UsableCount; int Hyperlevel; laListHandle Pools; }; STRUCTURE(laMemoryPoolPart) { laListItem Item; laListHandle FreeMemoryNodes; int UsedCount; laMemoryPool* PoolRoot; //<------Pool mem starts here }; NEED_STRUCTURE(laDBInst); STRUCTURE(laMemNode0){ laListItem Item; laMemoryPoolPart* InPool;//<---- Keep at the last //<------User mem starts here }; STRUCTURE(laMemNode) { laListItem Item; laListHandle Users; //<---- Keep at the second void* ReadInstance; laMemoryPoolPart* InPool; //<---- Keep at the last //<------User mem starts here }; NEED_STRUCTURE(laManagedUDF); STRUCTURE(laMemNodeHyper) { laListItem Item; laListHandle Users; //<---- Keep at the second laUID NUID; laTimeInfo TimeCreated; laManagedUDF* FromFile; int Modified; int UNUSEDUndoDirty; laMemoryPoolPart* InPool; //<---- Keep at the last //<------User mem starts here }; STRUCTURE(laStaticMemoryPoolNode) { laListItem Item; int UsedByte; //<------User mem starts here }; STRUCTURE(laStaticMemoryPool) { int EachSize; laListHandle Pools; //SYSLOCK csMem; }; STRUCTURE(laAVLNodeReal64) { laAVLNodeReal64* Parent; u64bit Index; real Value; //real SmallestValue; //real GreatestValue; laAVLNodeReal64* Smaller; laAVLNodeReal64* Greater; char Height; void* Pointer; }; STRUCTURE(laAVLTreeReal64) { laAVLNodeReal64* Root; u64bit ItemCount; laMemoryPool MemoryPool; }; STRUCTURE(laTimeRecorder) { #ifdef __linux__ struct timespec ts; #endif #ifdef _WIN32 LARGE_INTEGER tm; #endif }; STRUCTURE(laTranslationNode) { laListItem Item; laSafeString* LanguageName; laHash256 Matches; }; STRUCTURE(laTranslation) { int EnableTranslation; laListHandle Languages; laTranslationNode* CurrentLanguage; laHash256 MisMatches; }; STRUCTURE(laTranslationMatch) { laListItem Item; char * Target; char * Replacement; }; NEED_STRUCTURE(laRackPage); STRUCTURE(laNodeVisitInfo){ laListHandle* l; laListHandle* br; uint64_t Branch; int NextBranch; laRackPage* Page; }; NEED_STRUCTURE(laBaseNode); typedef void (*laBaseNodeInitF)(laBaseNode*, int NoCreate); typedef void (*laBaseNodeDestroyF)(laBaseNode*); typedef int (*laBaseNodeVisitF)(laBaseNode*, laNodeVisitInfo*); typedef int (*laBaseNodeEvalF)(laBaseNode*); typedef void (*laBaseNodeCopyF)(laBaseNode* _new, laBaseNode* _old, int DoRematch); STRUCTURE(laBaseNodeType){ laBaseNodeInitF Init; laBaseNodeDestroyF Destroy; laBaseNodeVisitF Visit; laBaseNodeEvalF Eval; laBaseNodeCopyF Copy; laPropContainer* pc; char* TypeName; char* Name; int Icon; int NodeSize; }; NEED_STRUCTURE(laNodeRack); STRUCTURE(laBaseNode){ laListItem Item; laSafeString* Name; laBaseNodeType* Type; laNodeRack* InRack; int Gap; int InitDone; uint64_t EvalMagic; uint64_t Branch; uint64_t BranchTemp; laBaseNode* Duplicated; }; #define CreateNew(Type) \ calloc(sizeof(Type),1) #define CreateNew_Size(size) \ calloc(size,1) #define CreateNewBuffer(Type,Num) \ calloc(sizeof(Type),Num); #define FreeMem(ptr) \ nutFreeMem((&ptr)) #define elif \ else if #define LA_UNAVAILABLE_NAME "- Unknown -" uint32_t laToUnicode(const unsigned char* ch, int* advance); int laToUTF8(const uint32_t ch, unsigned char* out, unsigned char** next); int strToUnicode(uint32_t* target, unsigned char* const src); int strToUTF8(unsigned char* target, uint32_t* const src); int strlenU(uint32_t* const str); int strcpyU(uint32_t* target, uint32_t* const source ); int strcatU(uint32_t* target, uint32_t* const source ); struct tm* laGetFullTime(); void laRecordTime(laTimeRecorder* tr); real laTimeElapsedSecondsf(laTimeRecorder* End, laTimeRecorder* Begin); int laTimeElapsedMilliseconds(laTimeRecorder* End, laTimeRecorder* Begin); void laSetAuthorInfo(char * Name, char * CopyrightString); void memCreateNUID(char* buf,laMemNodeHyper* Hyper); NEED_STRUCTURE(laPropPack); int nutHyperUserCount(void* instance, laProp* p_optional, int *p_count); void memHyperInfo(laPropPack* pp, char* buf); void memMakeHyperData(laMemNodeHyper* hi); void nutFreeMem(void** ptr); int nutFloatCompare(real l, real r); int nutSameAddress(void* l, void* r); void* arrElement(void* head, int i, int size); int arrEnsureLength(void** head, int next, int* max, size_t ElementSize); int arrInitLength(void** head, int max, int* pmax, size_t ElementSize); void arrFree(void** head, int* max); void lstPushSingle(void** Head, laListSingle* Item); void* lstPopSingle(void** Head, laListSingle* Item); void lstClearPrevNext(laListItem* li); int lstCountElements(laListHandle* Handle); void lstAppendItem(laListHandle* Handle, void* Item); void lstPushItem(laListHandle* Handle, void* Item); void* lstPopItem(laListHandle* Handle) ; void lstAppendItem2(laListHandle* Handle, void* Item); void* lstPopItem2(laListHandle* Handle); void lstPushItem2(laListHandle* Handle, void* Item); void lstAppendItem3(laListHandle* Handle, void* Item); void* lstPopItem3(laListHandle* Handle); void lstPushItem3(laListHandle* Handle, void* Item); int lstRemoveItem(laListHandle* Handle, laListItem* li) ; int lstRemoveItem2(laListHandle* Handle, laListItem2* li); int lstRemoveSegment(laListHandle* Handle, laListItem* Begin, laListItem* End); void lstInsertItemBefore(laListHandle* Handle, laListItem* toIns, laListItem* pivot); void lstInsertItemAfter(laListHandle* Handle, laListItem* toIns, laListItem* pivot); void lstInsertSegmentBefore(laListHandle* Handle, laListItem* Begin, laListItem* End, laListItem* pivot); void lstInsertSegmentAfter(laListHandle* Handle, laListItem* Begin, laListItem* End, laListItem* pivot); int lstHaveItemInList(laListHandle* Handle); /**/ void* lstGetTop(laListHandle* Handle); void lstPushSimpleItem(void** first, laItemUserLinker* iul); void* lstPushItemUser(void** first, void* p); void* lstPushItemUsing(void** first, void* p); void* lstAppendPointerOnly(laListHandle* h, void* p); void* lstAppendPointerSizedOnly(laListHandle* h, void* p, int size); void* lstPushPointerOnly(laListHandle* h, void* p); void* lstPushPointerSizedOnly(laListHandle* h, void* p, int size); void lstReverse(laListHandle* h); int lstHasPointer(laListHandle* h, void *p); void* lstAppendPointer(laListHandle* h, void* p); void* lstAppendPointerSized(laListHandle* h, void* p, int size); void* lstPushPointer(laListHandle* h, void* p); void* lstPushPointerSized(laListHandle* h, void* p, int size); void* lstAppendPointerStatic(laListHandle* h, laStaticMemoryPool* smp, void* p); void* lstAppendPointerStaticSized(laListHandle* h, laStaticMemoryPool* smp, void* p, int size); void* lstPushPointerStatic(laListHandle* h, laStaticMemoryPool* smp, void* p); void* lstPushPointerStaticSized(laListHandle* h, laStaticMemoryPool* smp, void* p, int size); void* lstPopPointerOnly(laListHandle* h); void lstRemovePointerItemOnly(laListHandle* h, laListItemPointer* lip); void lstRemovePointerOnly(laListHandle* h, void* p); void lstClearPointerOnly(laListHandle* h); void lstGeneratePointerListOnly(laListHandle* from1, laListHandle* from2, laListHandle* to); void* lstPopPointer(laListHandle* h); void lstRemovePointerItem(laListHandle* h, laListItemPointer* lip); void lstRemovePointer(laListHandle* h, void* p); void lstRemovePointerLeave(laListHandle *h, void *p); void lstClearPointer(laListHandle* h); void lstGeneratePointerList(laListHandle* from1, laListHandle* from2, laListHandle* to); void lstCopyHandle(laListHandle* target, laListHandle* src); void* lstAppendPointerStaticPool(laStaticMemoryPool* mph, laListHandle* h, void* p); void* lstPopPointerLeave(laListHandle* h); void lstRemovePointerItemNoFree(laListHandle* h, laListItemPointer* lip); void lstMoveUp(laListHandle* h, laListItem* li); void lstMoveDown(laListHandle* h, laListItem* li); void lstForAllItemsDo(laListDoFunc func, laListHandle* hList); void lstForAllItemsDoLNRR(laListNonRecursiveDoFunc func, laListHandle* hList); void lstForAllItemsDo_DirectFree(laListDoFunc func, laListHandle* hList); void lstForAllItemsDo_arg_ptr(laListDoFuncArgp func, laListHandle* hList, void* arg); void lstForAllItemsDo_NonRecursive_Root(laListHandle* FirstHandle, laListNonRecursiveDoFunc func, int bFreeItem, void* custom_data, laListCustomDataRemover remover); void lstCopy_NonRecursive_Root(laListHandle* FromHandle, laListHandle* ToHandle, int SizeEachNode, laListNonRecursiveCopyFunc func, void* custom_data, laListCustomDataRemover remover); void lstAddNonRecursiveListHandle(laListNonRecursiveRoot* root, laListHandle* newHandle, laListNonRecursiveDoFunc nrFunc, int bFreeList, void* custom_data, laListCustomDataRemover remover); void lstAddNonRecursiveListCopier(laListNonRecursiveRoot* root, laListHandle* oldHandle, laListHandle* newHandle, int sizeEach, laListNonRecursiveCopyFunc nrCpyFunc, void* custom_data, laListCustomDataRemover remover); void* lstFindItem(void* CmpData, laCompareFunc func, laListHandle* hList); void lstCombineLists(laListHandle* dest, laListHandle* src); void lstDestroyList(laListHandle* hlst); void* lstReMatch(laListHandle* SearchHandle, laListHandle* CurrentHandle, void* ItemToFind); typedef int(*MatcherFunc)(void*, void*); void* lstReMatchEx(laListHandle* SearchHandle, laListHandle* CurrentHandle, void* ItemToFind, MatcherFunc func); void lstAddElement(laListHandle* hlst, void* ext); void lstDestroyElementList(laListHandle* hlst); void hsh256InsertItemCSTR(laHash256* hash, laListItem* li, char * buckle); void hsh256InsertItem(laHash256* hash, laListItem* li, char buckle); void hsh65536InsertItem(laHash65536* hash, laListItem* li, long buckle); void hsh65536Init(laHash65536** h); void hshFree(laHash65536** h); laListHandle* hsh65536DoHashLongPtr(laHash65536* hash, unsigned long long buckle); laListHandle* hsh65536DoHashNUID(laHash65536* hash, char * NUID); laListItem* hsh256FindItemSTR(laHash256* hash, laCompareFunc func, char * buckle); unsigned char hsh256DoHashSTR(char * buckle); void memResetByteCount(); int memGetByteCount(); void* memGetHead(void* UserMem, int* HyperLevel); laListHandle* memGetUserList(void* UserMem); laMemoryPool *memInitPool(int NodeSize, int HyperLevel); void memInitPoolSmall(laMemoryPool* mph, int NodeSize); laMemoryPoolPart* memNewPoolPart(laMemoryPool* mph); void* memAcquireH(laMemoryPool* Handle); void* memAcquireSimple(int Size); void* memAcquireNoAppend(int Size); void* memAcquireHyperNoAppend(int Size); void* memAcquire(int Size); void* memAcquireHyper(int Size); void memFree(void* Data); void memDestroyPool(laMemoryPool* Handle); void memNoLonger(); void memMarkClean(void* HyperUserMem); void memLeave(void *Data); void memTake(void* Data); void memFreeRemainingLeftNodes(); laStaticMemoryPoolNode* memNewStaticPool(laStaticMemoryPool* smp); void* memStaticAcquire(laStaticMemoryPool*smp, int size); void* memStaticAcquireThread(laStaticMemoryPool*smp, int size); void* memStaticDestroy(laStaticMemoryPool*smp); NEED_STRUCTURE(laSubProp); void memAssignRef(void* This, void** ptr, void* instance); void memAssignRefSafe(laSubProp* sp, void* This, void** ptr, void* instance); char * strSub(char *input, char *substring, char *replace); int strGetStringTerminateBy(char * content, char terminator, char * Out); int strHeadOfStringMatch(char * Str, char * SubStr); int strSkipSegmet(char ** pivot, char * content); char * strGetLastSegment(char * Content, char Seperator); void strDiscardLastSegmentSeperateBy(char * Content, char Seperator); void strDiscardSameBeginningSeperatedBy(char * s1, char * s2, char ** Result1, char ** Result2, char Seperator); int strCountSegmentSeperateBy(char * Content, char Seperator); void strMakeDifferentName(char * Target); void strReplaceCharacter(char * Str, char Find, char Replace); void strToUpper(char * Str); void strToLower(char * Str); int tolowerGuarded(int a); laStringSplitor *strSplitPath(char *path,char terminator); int strMakeInstructions(laStringSplitor** result,char * content); laStringPart* strGetArgument(laStringSplitor* ss, char * content); char * strGetArgumentString(laStringSplitor* ss, char * content); int strArgumentMatch(laStringSplitor* ss, char * id, char * value); int strDestroyStringSplitor(laStringSplitor** ss); int strGetIntSimple(char * content); real strGetFloatSimple(char * content); void strConvInt_CString(int src, char * dest, int lenth); void strConvFloat_CString(real src, char * dest, int lenth); void strCopyFull(char * dest, char * src); void strCopySized(char * dest, int LenthLim, char * src); #define strAppend strcat void strPrintFloatAfter(char * dest, int LenthLim, int bits, real data); void strPrintIntAfter(char * dest, int LenthLim, int data); int strSame(char * src, char *dest); void strEscapePath(char* OutCanBeSame, char* path); void strSafeDestroy(laSafeString** ss); void strSafeSet(laSafeString** ss, char * Content); void strSafeAppend(laSafeString **ss, char *Content); void strSafePrint(laSafeString **ss, char *Format, ...); void strSafePrintV(laSafeString **ss, char *Format, va_list arg); void strSafeDump(); #define SSTR(str) (((str) && (str)->Ptr)?(str)->Ptr:"") void strBeginEdit(laStringEdit** se, char * FullStr); char* strGetEditString(laStringEdit *se, int SelectionOnly); char* strEndEdit(laStringEdit** se, int FreeString); void strSetEditViewRange(laStringEdit* se, int Lines, int Cols); void strEnsureCursorVisible(laStringEdit* se); void strRemoveLine(laStringEdit* se, laStringLine* sl); void strRemoveLineI(laStringEdit* se, int LineIndex); void strSetCursor(laStringEdit* se, int LineIndex, int BeforeIndex); void strMoveCursor(laStringEdit* se, int Left, int Select); void strMoveCursorLine(laStringEdit *se, int Up, int Select); int strHasSelection(laStringEdit* se); void strCancelSelect(laStringEdit *se); void strLazySelect(laStringEdit *se); void strEndSelect(laStringEdit *se); void strSelectLineAll(laStringEdit* se); void strDeselectAll(laStringEdit* se); void strPanFoward(uint32_t * str, int Before, int Offset); void strSquishBackward(uint32_t * str, int Before, int EndBefore); void strClearSelection(laStringEdit* se); laStringLine *strGetCursorLine(laStringEdit *se, int* IndexIfLast); laStringLine* strGetBeginLine(laStringEdit* se); void strInsertChar(laStringEdit* se, uint32_t a); void strBackspace(laStringEdit* se); void strMoveView(laStringEdit *se, int DownLines, int RightCharacters); int laCopyFile(char *to, char *from); void transNewLanguage(const char * LanguageID); void transSetLanguage(const char * LanguageID); void transDumpMissMatchRecord(const char * filename); void transNewEntry(const char * Target, const char * replacement); char * transLate(char * Target); void transState(void* UNUSED, int val); void transInitTranslation_zh_cn(); void laOpenInternetLink(char* url); #define SEND_PANIC_ERROR(msg) \ {printf(msg); exit(0);} #ifdef _WIN32 void usleep(unsigned int usec); #endif void laSpinInit(SYSLOCK* lock); void laSpinDestroy(SYSLOCK * lock); void laSpinLock(SYSLOCK* lock); void laSpinUnlock(SYSLOCK* lock); void la_luaPrintStatus(lua_State *L); void la_luaLoadLibs(lua_State *L); void la_luaDumpStack(lua_State *L); int terLoadLine(char* buf, int firstline); #ifdef __cplusplus extern "C"{ #endif extern const char* LA_LUA_LIB_COMMON; #ifdef __cplusplus } #endif