*/}}
Browse Source

luajit terminal initial support in lagui

YimingWu 1 year ago
parent
commit
f6d6f6acf9
11 changed files with 382 additions and 30 deletions
  1. 5 0
      CMakeLists.txt
  2. 53 0
      FindLuaJIT.cmake
  3. 12 0
      la_interface.h
  4. 14 5
      la_kernel.c
  5. 11 2
      la_tns.h
  6. 41 11
      la_tns_kernel.c
  7. 202 1
      la_util.c
  8. 5 1
      la_util.h
  9. 7 0
      lagui-config.cmake
  10. 19 6
      resources/la_properties.c
  11. 13 4
      resources/la_templates.c

+ 5 - 0
CMakeLists.txt

@@ -11,10 +11,13 @@ if (NOT DEFINED ${LAGUI_FONT_CUSTOM_PATH})
     set(LAGUI_FONT_CUSTOM_PATH $ENV{HOME}/.local/share/fonts/lagui)
 endif()
 
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR})
+
 find_package(OpenGL REQUIRED)
 find_package(X11 REQUIRED)
 find_package(Freetype REQUIRED)
 find_package(GLEW REQUIRED)
+find_package(LuaJIT REQUIRED)
 
 set(CMAKE_THREAD_PREFER_PTHREAD ON)
 set(THREADS_PREFER_PTHREAD_FLAG ON)
@@ -39,6 +42,7 @@ include_directories(
 	${GLEW_INCLUDE_PATH}
 	${GLM_INCLUDE_PATH}
 	${FREETYPE_INCLUDE_DIRS}
+    ${LUA_INCLUDE_DIR}
     lagui
 )
 
@@ -83,4 +87,5 @@ install(FILES ${HEADER_FILES} DESTINATION include/lagui)
 install(TARGETS lagui EXPORT lagui-targets DESTINATION lib/lagui)
 install(EXPORT lagui-targets DESTINATION lib/lagui)
 install(FILES lagui-config.cmake DESTINATION lib/lagui)
+install(FILES FindLuaJIT.cmake DESTINATION lib/lagui)
 install(FILES ${LAGUI_FONTS} DESTINATION ${LAGUI_FONT_CUSTOM_PATH})

+ 53 - 0
FindLuaJIT.cmake

@@ -0,0 +1,53 @@
+# Locate LuaJIT library
+# This module defines
+#  LUAJIT_FOUND, if false, do not try to link to Lua
+#  LUA_LIBRARY, where to find the lua library
+#  LUA_INCLUDE_DIR, where to find lua.h
+#
+# This module is similar to FindLua51.cmake except that it finds LuaJit instead.
+
+FIND_PATH(LUA_INCLUDE_DIR luajit.h
+	HINTS
+	$ENV{LUA_DIR}
+	PATH_SUFFIXES include/luajit-2.1 include/luajit-2.0 include/luajit-5_1-2.1 include/luajit-5_1-2.0 include luajit
+	PATHS
+	~/Library/Frameworks
+	/Library/Frameworks
+	/sw # Fink
+	/opt/local # DarwinPorts
+	/opt/csw # Blastwave
+	/opt
+)
+
+# Test if running on vcpkg toolchain
+if(DEFINED VCPKG_TARGET_TRIPLET AND DEFINED VCPKG_APPLOCAL_DEPS)
+	# On vcpkg luajit is 'lua51' and normal lua is 'lua'
+	FIND_LIBRARY(LUA_LIBRARY
+		NAMES lua51
+		HINTS
+		$ENV{LUA_DIR}
+		PATH_SUFFIXES lib
+	)
+else()
+	FIND_LIBRARY(LUA_LIBRARY
+		NAMES luajit-5.1
+		HINTS
+		$ENV{LUA_DIR}
+		PATH_SUFFIXES lib64 lib
+		PATHS
+		~/Library/Frameworks
+		/Library/Frameworks
+		/sw
+		/opt/local
+		/opt/csw
+		/opt
+	)
+endif()
+
+INCLUDE(FindPackageHandleStandardArgs)
+# handle the QUIETLY and REQUIRED arguments and set LUAJIT_FOUND to TRUE if
+# all listed variables exist
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(LuaJIT
+	REQUIRED_VARS LUA_LIBRARY LUA_INCLUDE_DIR)
+
+MARK_AS_ADVANCED(LUA_INCLUDE_DIR LUA_LIBRARY)

+ 12 - 0
la_interface.h

@@ -45,6 +45,12 @@
 //#include "llvm-c/Target.h"
 //#include <Windows.h>
 
+#define luajit_c
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+#include "luajit.h"
+
 #define LA_VERSION_MAIN 5
 #define LA_VERSION_SUB 2
 
@@ -305,6 +311,12 @@ STRUCTURE(LA){
     laListItem Hyper;
 
     laListHandle Logs;
+    char* TerminalInput[512];
+    laListHandle TerminalHistories;
+    int TerminalIncomplete;
+
+    lua_State *L;
+
     laListHandle Controllers; int NextControllerID;
 
     laListHandle Windows;

+ 14 - 5
la_kernel.c

@@ -1028,6 +1028,12 @@ int laGetReadyWith(laInitArguments* ia){
     MAIN.Animation=memAcquire(sizeof(laAnimation));
     MAIN.Audio=memAcquire(sizeof(laAudio));
 
+    // starting lua
+    logPrint("Starting luajit...\n");
+    MAIN.L = lua_open();
+    la_LuaJITLoadLibs();
+    
+    la_PrintLuaJITStatus();
     
     //interactions:
     MAIN.TopFramerate = 60;
@@ -1158,6 +1164,10 @@ void laShutoff(int SavePrefereces){
 
     hshFree(&MAIN.DBInstMemLeft);
     memNoLonger();
+
+    lua_close(MAIN.L);
+
+    printf("LaGUI has cleaned up.\n");
 }
 
 int laRestoreFactorySettings(){
@@ -2706,8 +2716,8 @@ laPanel* laTearOffPanel(laBlock* b, laPanel* p_if_set){
 
     p->Mode = LA_PANEL_FLOATING_TOP;
     p->TY-=LA_RH; p->TH+=LA_RH;
-    p->W=p->TW=p->PanelTemplate->DefaultW_RH*LA_RH;
-    p->W=p->TH=p->PanelTemplate->DefaultH_RH*LA_RH;
+    p->W=p->TW=(p->PanelTemplate?p->PanelTemplate->DefaultW_RH:20)*LA_RH;
+    p->W=p->TH=(p->PanelTemplate?p->PanelTemplate->DefaultH_RH:20)*LA_RH;
     p->Block = 0;
 
     laEnsurePanelInBound(p,&p->UI); laPlacePanelForCreation(p);
@@ -5312,7 +5322,7 @@ int la_UpdateUiListRecursive(laUiList *uil, int U, int L, int R, int B, int Fast
             if (ui->Type != _LA_UI_COLLECTION && (ui->Subs.pFirst || ui->Page) && ui->Type != _LA_UI_MENU_ROOT){
                 if (ui->Type != _LA_UI_CANVAS){
                     int scrollw=ui->Page->ScrollerShownV?bt->RP*2+LA_SCROLL_W:0;
-                    la_CalcUiTopInfluence(&uil->Columns, ui);
+                    la_CalcUiTopInfluence(&uil->Columns, ui); int first_in=(ui->Page->B==0);
                     SubB = la_UpdateUiListRecursive(ui->Page,
                         ui->TB + (ui->State == LA_UI_ACTIVE ? 0 : LA_RH)+(NoDecal?0:bt->TM), ui->TL+(NoDecal?0:bt->LM), ui->TR-(NoDecal?0:bt->RM)-scrollw, B, Fast, ParentPanel);
                     ui->TB = (ui->Page->HeightCoeff > 0 ? ui->TU + ui->Page->HeightCoeff * LA_RH :
@@ -5324,8 +5334,7 @@ int la_UpdateUiListRecursive(laUiList *uil, int U, int L, int R, int B, int Fast
                     }
                     if(ui->Page->AllowScale){ui->Page->ScrollerShownH=1;}
                     if(ui->Page->ScrollerShownH){subh-=LA_SCROLL_W-bt->BM;}
-                    if(GB && ui->TB >= GB){
-                        ui->Page->PanY=(SubB-ui->TB-bt->BM); if(ui->Page->PanY<0)ui->Page->PanY=0; }
+                    if((GB && ui->TB >= GB)||first_in){ ui->Page->PanY=(SubB-ui->TB-bt->BM); if(ui->Page->PanY<0)ui->Page->PanY=0; }
                     if(ui->Page->HeightCoeff){
                         if((subh<ui->Page->TB-ui->Page->TU && (!ui->Page->ScrollerShownV)) ||
                             (subh>=ui->Page->TB-ui->Page->TU && ui->Page->ScrollerShownV)){

+ 11 - 2
la_tns.h

@@ -458,8 +458,11 @@ STRUCTURE(tnsEvaluatedNode){
     tnsMatrix44d Mat;
     tnsObject* Target;
     laListHandle Children;
+    int LuaID;
 };
 STRUCTURE(tnsEvaluatedScene){
+    int NextLuaID;
+    laListHandle WastedEvaluateNodes;
     tnsEvaluatedNode* Root;
     tnsEvaluatedNode* CurrentParent;
     tnsEvaluatedNode* CurrentChild;
@@ -997,7 +1000,11 @@ void tnsSelfTransformValueChanged(tnsObject* o);
 void tnsDeltaTransformValueChanged(tnsObject* o);
 void tnsGlobalTransformValueChanged(tnsObject* o);
 
-void tnsExtrtnsEnsurePlayDuplicateactDeltaTransformValue(tnsObject *o);
+void tnsClearPlayDuplicate(tnsObject* o);
+void tnsFreeEvaluatedNode(tnsEvaluatedNode* n);
+void tnsFreeEvaluatedScene(tnsEvaluateData* ed);
+void tnsClearPlayState(tnsObject* o);
+void tnsExtrtnsEnsurePlayDuplicate(tnsObject *o);
 void tnsExtractSelfTransformValue(tnsObject *o);
 void tnsExtractGlobalTransformValue(tnsObject *o);
 
@@ -1040,6 +1047,8 @@ void tnsGetCameraMovingDeltas(tnsCamera *c, int ViewportW, int ViewportH, real x
 
 laPropContainer* tnsget_ObjectType(tnsObject* o);
 
+void *tnsget_detached_FirstRootObject(void *UNUSED1, void *UNUSED2);
+
 void tnsDestroyRootObject(tnsObject *root);
 void tnsDestroyObject(tnsObject *o);
 
@@ -1145,7 +1154,7 @@ void tnsEvaluateNewNode(tnsEvaluateData* ed, tnsObject* ob);
 int tnsEvaluateTryRelinkExistingNode(tnsEvaluateData* ed, tnsObject* ob);
 void tnsEvaluateFreeNode(tnsEvaluatedNode* en);
 void tnsEvaluateSyncNode(tnsEvaluateData* ed, tnsObject* ob);
-void tnsEnsureEvalueatedScene(tnsEvaluateData* ed, tnsObject* root);
+void tnsEnsureEvaluatedScene(tnsEvaluateData* ed, tnsObject* root);
 void tnsPrintEvaluatedNode(tnsEvaluatedNode* en,int level);
 
 void tnsDrawObjectTree(tnsObject* from, int Layers,void* CustomData, int DrawRuntime);

+ 41 - 11
la_tns_kernel.c

@@ -3728,6 +3728,29 @@ void tnsEvaluateThisObject(tnsObject *o, tnsEvaluateData* ed){
     }
 }
 
+void tnsClearPlayDuplicate(tnsObject* o){
+    if(!o->PlayDuplicate) return;
+    while(lstPopPointer(&o->PlayDuplicate->ChildObjects));
+    for(laListItemPointer* lip=o->ChildObjects.pFirst;lip;lip=lip->pNext){
+        tnsObject*co=lip->p; tnsClearPlayDuplicate(co);
+    }
+    memFree(o->PlayDuplicate); o->PlayDuplicate=0;
+}
+void tnsFreeEvaluatedNode(tnsEvaluatedNode* n){
+    tnsEvaluatedNode* en;
+    while(en=lstPopItem(&n->Children)){ tnsFreeEvaluatedNode(en); }
+    memFree(n);
+}
+void tnsFreeEvaluatedScene(tnsEvaluateData* ed){
+    if(!ed->Scene) ed->Scene=memAcquire(sizeof(tnsEvaluatedScene));
+    if(ed->Scene->Root) tnsFreeEvaluatedNode(ed->Scene->Root);
+    memFree(ed->Scene); ed->Scene=0;
+}
+void tnsClearPlayState(tnsObject* o){
+    tnsClearPlayDuplicate(o);
+    if(o->EvaluatedPlay.Commands) tnsFreeEvaluatedArray(&o->EvaluatedPlay);
+    tnsFreeEvaluatedScene(&o->EvaluatedPlay);
+}
 tnsObject* tnsEnsurePlayDuplicate(tnsObject* o){
     if(o->Flags&TNS_OBJECT_FLAGS_PLAY_DUPLICATE) return o;
     if(o->PlayDuplicate) return o->PlayDuplicate;
@@ -3744,9 +3767,7 @@ tnsObject* tnsEnsurePlayDuplicate(tnsObject* o){
     return o->PlayDuplicate;
 }
 void tnsFreePlayDuplicate(tnsObject* o){
-    for(laListItemPointer* lip=o->ChildObjects.pFirst;lip;lip=lip->pNext){
-        tnsFreePlayDuplicate(lip);
-    }
+    for(laListItemPointer* lip=o->ChildObjects.pFirst;lip;lip=lip->pNext){ tnsFreePlayDuplicate(lip); }
     memFree(o->PlayDuplicate);
     o->PlayDuplicate=0;
 }
@@ -3789,9 +3810,16 @@ void tnsSetObjectTreeEvaluationArgs(tnsObject* from, tnsObject* Active, int Fill
     SETARG(Active); SETARG(FillOutline); SETARG(FillSelectionID);
     if(set) ed->Done=0;
 }
+tnsEvaluatedNode* tnsAcquireEvaluateNode(tnsEvaluateData* ed){
+    if(!ed->Scene->WastedEvaluateNodes.pFirst){ 
+        tnsEvaluatedNode* en=memAcquire(sizeof(tnsEvaluatedNode));
+        en->LuaID=ed->Scene->NextLuaID; ed->Scene->NextLuaID++;
+        return en;
+    }else{ return lstPopItem(&ed->Scene->WastedEvaluateNodes); }
+}
 void tnsEvaluateNewNode(tnsEvaluateData* ed, tnsObject* ob){
     tnsEvaluatedScene* s=ed->Scene;
-    tnsEvaluatedNode* en=memAcquire(sizeof(tnsEvaluatedNode)); en->Target=ob;
+    tnsEvaluatedNode* en=tnsAcquireEvaluateNode(ed); en->Target=ob;
     tnsMultiply44d(en->Mat,ed->MatArr[ed->NextMat-1],ob->GlobalTransform);
     if(s->CurrentChild) lstInsertItemBefore(&s->CurrentParent->Children,en,s->CurrentChild);
     else lstAppendItem(&s->CurrentParent->Children,en);
@@ -3806,10 +3834,12 @@ int tnsEvaluateTryRelinkExistingNode(tnsEvaluateData* ed, tnsObject* ob){
     }
     return done;
 }
-void tnsEvaluateFreeNode(tnsEvaluatedNode* en){
+void tnsEvaluateEndNode(tnsEvaluateData* ed, tnsEvaluatedNode* en){
     tnsEvaluatedNode* sen;
-    while(sen=lstPopItem(&en->Children)){ tnsEvaluateFreeNode(sen); }
-    memFree(sen);
+    while(sen=lstPopItem(&en->Children)){ tnsEvaluateEndNode(ed,sen); }
+    int luaid=en->LuaID; memset(en,0,sizeof(tnsEvaluatedNode)); //memFree(sen);
+    en->LuaID=luaid;
+    lstAppendItem(&ed->Scene->WastedEvaluateNodes,en);
 }
 void tnsEvaluateSyncNode(tnsEvaluateData* ed, tnsObject* ob){
     tnsEvaluatedScene* s=ed->Scene;
@@ -3818,13 +3848,13 @@ void tnsEvaluateSyncNode(tnsEvaluateData* ed, tnsObject* ob){
     }
     if((!ob) && s->CurrentChild){ tnsEvaluatedNode* en=s->CurrentChild->Item.pPrev;
         if(en) while(en->Item.pNext){ tnsEvaluatedNode* den=en->Item.pNext;
-            lstRemoveItem(&s->CurrentParent->Children,den); tnsEvaluateFreeNode(den); }
+            lstRemoveItem(&s->CurrentParent->Children,den); tnsEvaluateEndNode(ed,den); }
         s->CurrentChild=0;
     }
 }
-void tnsEnsureEvalueatedScene(tnsEvaluateData* ed, tnsObject* root){
+void tnsEnsureEvaluatedScene(tnsEvaluateData* ed, tnsObject* root){
     if(!ed->Scene) ed->Scene=memAcquire(sizeof(tnsEvaluatedScene));
-    if(!ed->Scene->Root) ed->Scene->Root=memAcquire(sizeof(tnsEvaluatedNode));
+    if(!ed->Scene->Root) ed->Scene->Root=tnsAcquireEvaluateNode(ed);
     ed->Scene->CurrentChild=ed->Scene->Root;
     ed->Scene->CurrentChild->Target=root; tnsLoadIdentity44d(ed->Scene->CurrentChild->Mat);
 }
@@ -3853,7 +3883,7 @@ void tnsEvaluateObjectTree(tnsObject* from, tnsEvaluateData* UseED, int Evaluate
     tnsEvaluatedNode* CP,*CC;
     if(ed->SceneEvaluateMode){
         from=tnsEnsurePlayDuplicate(from);
-        if(!UseED){ tnsEnsureEvalueatedScene(ed,from); }
+        if(!UseED){ tnsEnsureEvaluatedScene(ed,from); }
         CP=ed->Scene->CurrentParent; CC=ed->Scene->CurrentChild;
     }
 

+ 202 - 1
la_util.c

@@ -19,6 +19,10 @@
 #define _CRT_SEQURE_NO_WARNINGS
 #include "la_util.h"
 #include "la_interface.h"
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+#include "luajit.h"
 #include <stdio.h>
 #include <stdlib.h>
 
@@ -2225,4 +2229,201 @@ void laSpinLock(SYSLOCK* lock) {
 void laSpinUnlock(SYSLOCK* lock) {
     pthread_spin_unlock(lock);
 }
-#endif
+#endif
+
+//======================================= lua utils
+
+static const char *progname = LUA_PROGNAME;
+static int traceback(lua_State *L){
+  if (!lua_isstring(L, 1)) { /* Non-string error object? Try metamethod. */
+    if (lua_isnoneornil(L, 1) ||
+	!luaL_callmeta(L, 1, "__tostring") ||
+	!lua_isstring(L, -1))
+      return 1;  /* Return non-string error object. */
+    lua_remove(L, 1);  /* Replace object by result of __tostring metamethod. */
+  }
+  luaL_traceback(L, L, lua_tostring(L, 1), 1);
+  return 1;
+}
+
+static void l_message(const char *msg){
+  if (progname) { fputs(progname, stderr); fputc(':', stderr); fputc(' ', stderr); }
+  fputs(msg, stderr); fputc('\n', stderr);
+  fflush(stderr);
+}
+static int report(lua_State *L, int status){
+  if (status && !lua_isnil(L, -1)) {
+    const char *msg = lua_tostring(L, -1);
+    if (msg == NULL) msg = "(error object is not a string)";
+    l_message(msg);
+    lua_pop(L, 1);
+  }
+  return status;
+}
+static int docall(lua_State *L, int narg, int clear){
+  int status;
+  int base = lua_gettop(L) - narg;  /* function index */
+  lua_pushcfunction(L, traceback);  /* push traceback function */
+  lua_insert(L, base);  /* put it under chunk and args */
+
+  status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base);
+
+  lua_remove(L, base);  /* remove traceback function */
+  /* force a complete garbage collection in case of errors */
+  if (status != LUA_OK) lua_gc(L, LUA_GCCOLLECT, 0);
+  return status;
+}
+static void write_prompt(lua_State *L, int firstline){
+  const char *p;
+  lua_getfield(L, LUA_GLOBALSINDEX, firstline ? "_PROMPT" : "_PROMPT2");
+  p = lua_tostring(L, -1);
+  if (p == NULL) p = firstline ? LUA_PROMPT : LUA_PROMPT2;
+  fputs(p, stdout);
+  fflush(stdout);
+  lua_pop(L, 1);  /* remove global */
+}
+static int incomplete(lua_State *L, int status){
+  if (status == LUA_ERRSYNTAX) {
+    size_t lmsg;
+    const char *msg = lua_tolstring(L, -1, &lmsg);
+    const char *tp = msg + lmsg - (sizeof(LUA_QL("<eof>")) - 1);
+    if (strstr(msg, LUA_QL("<eof>")) == tp) {
+      lua_pop(L, 1);
+      return 1;
+    }
+  }
+  return 0;  /* else... */
+}
+static int pushline(lua_State *L, int firstline){
+  char buf[LUA_MAXINPUT];
+  write_prompt(L, firstline);
+  if (fgets(buf, LUA_MAXINPUT, stdin)) {
+    size_t len = strlen(buf);
+    if (len > 0 && buf[len-1] == '\n')
+      buf[len-1] = '\0';
+    if (firstline && buf[0] == '=')
+      lua_pushfstring(L, "return %s", buf+1);
+    else
+      lua_pushstring(L, buf);
+    return 1;
+  }
+  return 0;
+}
+static int loadline(lua_State *L){
+  int status;
+  lua_settop(L, 0);
+  if (!pushline(L, 1))
+    return -1;  /* no input */
+  for (;;) {  /* repeat until gets a complete line */
+    status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin");
+    if (!incomplete(L, status)) break;  /* cannot try to add lines? */
+    if (!pushline(L, 0))  /* no more input? */
+      return -1;
+    lua_pushliteral(L, "\n");  /* add a new line... */
+    lua_insert(L, -2);  /* ...between the two lines */
+    lua_concat(L, 3);  /* join them */
+  }
+  lua_remove(L, 1);  /* remove line */
+  return status;
+}
+void dotty(lua_State *L){
+  int status;
+  const char *oldprogname = progname;
+  progname = NULL;
+  while ((status = loadline(L)) != -1) {
+    if (status == LUA_OK) status = docall(L, 0, 0);
+    report(L, status);
+    if (status == LUA_OK && lua_gettop(L) > 0) {  /* any result to print? */
+      lua_getglobal(L, "print");
+      lua_insert(L, 1);
+      if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0)
+	l_message(lua_pushfstring(L, "error calling " LUA_QL("print") " (%s)",
+				  lua_tostring(L, -1)));
+    }
+  }
+  lua_settop(L, 0);  /* clear stack */
+  fputs("\n", stdout);
+  fflush(stdout);
+  progname = oldprogname;
+}
+
+static int lalua_Log(lua_State *L) {
+  int n = lua_gettop(L); int i;
+  lua_getglobal(L, "tostring");
+  for (i=1; i<=n; i++) {
+    const char *s;
+    lua_pushvalue(L, -1);  /* tostring function */
+    lua_pushvalue(L, i);   /* value to print */
+    lua_call(L, 1, 1);
+    s = lua_tostring(L, -1);  /* get result */
+    if (s == NULL) return luaL_error(L, LUA_QL("tostring") " must return a string to "LUA_QL("use `log`"));
+    if (i>1) logPrint("    ");
+    logPrint(s); lua_pop(L, 1);  /* pop result */
+  }
+  logPrint("\n");
+  return 0;
+}
+
+void la_LuaJITLoadLibs(){
+    lua_State *L=MAIN.L;
+    lua_gc(MAIN.L, LUA_GCSTOP, 0);
+    
+    luaL_openlibs(MAIN.L);
+    lua_register(L,"log",lalua_Log);
+
+    lua_gc(MAIN.L, LUA_GCRESTART, -1);
+}
+
+int terLoadLine(char* buf, int firstline){
+    lua_State *L=MAIN.L;
+    
+    if(!MAIN.TerminalIncomplete){ lua_settop(L, 0); }
+
+    size_t len = strlen(buf); if(len>=512){ buf[512]=0; }
+    if(len > 0 && buf[len-1] == '\n') buf[len-1] = '\0';
+    if(firstline && buf[0] == '=') lua_pushfstring(L, "return %s", buf+1);
+    else lua_pushstring(L, buf);
+
+    if(MAIN.TerminalIncomplete){
+        lua_pushliteral(L, "\n"); lua_insert(L, -2); lua_concat(L, 3);
+    }
+
+    int status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "terLoadLine");
+    if(incomplete(L,status)){
+        MAIN.TerminalIncomplete=1;
+    }else{
+        MAIN.TerminalIncomplete=0;
+        lua_remove(L, 1);
+    }
+
+    if(status==LUA_OK && (!MAIN.TerminalIncomplete)){
+        status = docall(L, 0, 0);
+        report(L, status);
+        if (status == LUA_OK && lua_gettop(L) > 0) {  /* any result to print? */
+        lua_getglobal(L, "log");
+        lua_insert(L, 1);
+        if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0)
+        l_message(lua_pushfstring(L, "error calling " LUA_QL("log") " (%s)",
+                    lua_tostring(L, -1)));
+        }
+    }
+
+    return status;
+}
+
+void la_PrintLuaJITStatus(){
+    lua_State *L=MAIN.L;
+    logPrint(LUAJIT_VERSION " -- " LUAJIT_COPYRIGHT ". " LUAJIT_URL "\n");
+    int n; const char *s;
+    lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
+    lua_getfield(L, -1, "jit"); lua_remove(L, -2);  /* Get jit.* module table. */
+    lua_getfield(L, -1, "status"); lua_remove(L, -2);
+    n = lua_gettop(L);
+    lua_call(L, 0, LUA_MULTRET);
+    logPrint(lua_toboolean(L, n) ? "JIT: ON" : "JIT: OFF");
+    for (n++; (s = lua_tostring(L, n)); n++) {
+        logPrint("%s ",s);
+    }
+    putc('\n', stdout);
+    lua_settop(L, 0);  /* clear stack */
+}

+ 5 - 1
la_util.h

@@ -709,8 +709,12 @@ void laOpenInternetLink(char* url);
 void usleep(unsigned int usec);
 #endif
 
-
 void laSpinInit(SYSLOCK* lock);
 void laSpinDestroy(SYSLOCK * lock);
 void laSpinLock(SYSLOCK* lock);
 void laSpinUnlock(SYSLOCK* lock);
+
+void la_PrintLuaJITStatus();
+void la_LuaJITLoadLibs();
+
+int terLoadLine(char* buf, int firstline);

+ 7 - 0
lagui-config.cmake

@@ -8,11 +8,14 @@ if (POLICY CMP0072)
   set(OpenGL_GL_PREFERENCE GLVND)
 endif()
 
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_LIST_DIR})
+
 find_package(OpenGL REQUIRED)
 find_package(X11 REQUIRED)
 find_package(Freetype REQUIRED)
 find_package(GLEW REQUIRED)
 find_package(PNG REQUIRED)
+find_package(LuaJIT REQUIRED)
 
 set(CMAKE_THREAD_PREFER_PTHREAD ON)
 set(THREADS_PREFER_PTHREAD_FLAG ON)
@@ -31,6 +34,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux")
         m X11 Xi Xcursor
         ${CMAKE_DL_LIBS}
         Threads::Threads
+        ${LUA_LIBRARY}
         lagui
         CACHE INTERNAL "LaGUI shared libs"
     )
@@ -40,6 +44,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux")
         ${GLEW_INCLUDE_PATH}
         ${FREETYPE_INCLUDE_DIRS}
         ${LAGUI_INCLUDE_DIRS}
+        ${LUA_INCLUDE_DIR}
         CACHE INTERNAL "Include dirs of LaGUI and dependencies"
     )
 elseif (CMAKE_SYSTEM_NAME MATCHES "Windows")
@@ -49,6 +54,7 @@ elseif (CMAKE_SYSTEM_NAME MATCHES "Windows")
         ${FREETYPE_LIBRARIES}
         ${PNG_LIBRARY}
         ${CMAKE_DL_LIBS}
+        ${LUA_LIBRARY}
         Threads::Threads
         lagui shlwapi
         CACHE INTERNAL "LaGUI shared libs"
@@ -58,6 +64,7 @@ elseif (CMAKE_SYSTEM_NAME MATCHES "Windows")
         ${GLEW_INCLUDE_PATH}
         ${FREETYPE_INCLUDE_DIRS}
         ${LAGUI_INCLUDE_DIRS}
+        ${LUA_INCLUDE_DIR}
         CACHE INTERNAL "Include dirs of LaGUI and dependencies"
     )
 endif()

+ 19 - 6
resources/la_properties.c

@@ -21,6 +21,17 @@
 extern LA MAIN;
 extern struct _tnsMain *T;
 
+void laset_TerminalInput(void* unused, char* content){
+    if((!content)||(!content[0])){ MAIN.TerminalInput[0]=0; return; }
+    int len=strlen(content);{
+        //run command;
+        logPrint("%s\n",content);
+        terLoadLine(content,1);
+        MAIN.TerminalInput[0]=0; return;
+    }
+    strcpy(MAIN.TerminalInput,content);
+}
+
 void *laget_ActiveTheme(void *unused){
     return MAIN.CurrentTheme;
 }
@@ -247,13 +258,13 @@ void *laget_PropertyItemFirst(laPropContainer *p){
 void *laget_PropertyItemNext(laProp *p, void *UNUSED){
     return p->Item.pNext;
 }
-void laget_PropertyName(laProp *p, char *result){
+void laget_PropertyName(laProp *p, char *result,char** result_direct){
     strCopyFull(result, p ? p->Name : "");
 }
-void laget_PropertyIdentifier(laProp *p, char *result){
+void laget_PropertyIdentifier(laProp *p, char *result,char** result_direct){
     strCopyFull(result, p ? p->Identifier : "");
 }
-void laget_PropertyDescription(laProp *p, char *result){
+void laget_PropertyDescription(laProp *p, char *result,char** result_direct){
     strCopyFull(result, p ? p->Description : "");
 }
 int laget_PropertySubContainerIconID(laProp *p){
@@ -297,7 +308,7 @@ void laset_WindowHiddenPanel(laWindow *window, laPanel* p){
     laShowPanelWithExpandEffect(p);
     laPopPanel(p);
 }
-void laget_PanelTitle(laPanel *p, char *result){
+void laget_PanelTitle(laPanel *p, char *result,char** result_direct){
     strCopyFull(result, p->Title->Ptr);
 }
 void laset_PanelTitle(laPanel *p, char *content){
@@ -359,7 +370,7 @@ void laget_PanelSnapEnable(laPanel *p, int *result){
     result[2] = p->ST ? 1 : 0;
     result[3] = p->SB ? 1 : 0;
 }
-void laget_LayoutTitle(laLayout *l, char *result){
+void laget_LayoutTitle(laLayout *l, char *result, char** result_direct){
     strCopyFull(result, l->ID->Ptr);
 }
 void laset_LayoutTitle(laLayout *l, char *content){
@@ -1205,6 +1216,8 @@ void la_RegisterInternalProps(){
 
         p = laAddPropertyContainer("la_main", "LA Root", "LA Root Structure", U'🖴', 0,sizeof(LA), 0,0,2|LA_PROP_OTHER_ALLOC);{
             laAddSubGroup(p, "logs", "Logs", "Application logs", "la_log",0,0,laui_LogItem, -1, 0,0,0,0,0,0,offsetof(LA, Logs), LA_UDF_IGNORE|LA_READ_ONLY);
+            laAddStringProperty(p, "terminal_input", "Terminal Input", "Terminal input string", 0,0,0,0,0, offsetof(LA,TerminalInput),0,0,laset_TerminalInput,0,LA_UDF_LOCAL|LA_UDF_IGNORE);
+            
             laAddSubGroup(p, "differences", "Differences", "Difference stack (for undo/redo)", "la_difference",0,0,0,offsetof(LA, HeadDifference), 0,0,0,0,0,0,offsetof(LA, Differences), LA_UDF_IGNORE|LA_READ_ONLY);
             sp=laAddSubGroup(p, "panel_templates", "Panel Templates", "Panel templates used to create new panel", "panel_template",0,0,0,-1, 0,0,0,0,0,0,offsetof(LA, PanelTemplates), 0);
             
@@ -1285,7 +1298,7 @@ void la_RegisterInternalProps(){
             laAddEnumItemAs(ep, "NONE", "None", "This page is only evaluated when called from other pages",LA_PAGE_TRIGGER_NONE,0);
             ep=laAddEnumProperty(p, "use_script", "Use Script", "Use script instead of nodes",0,0,0,0,0,offsetof(laRackPage,UseScript),0,0,0,0,0,0,0,0,0,0);
             laAddEnumItemAs(ep, "NODES", "Nodes", "Use nodes to express logic",0,0);
-            laAddEnumItemAs(ep, "SCRIPT", "Script", "Use scripts to express logic",1,0);
+            laAddEnumItemAs(ep, "SCRIPT", "Script", "Use scripts to express logic",1,L'📃');
             laAddStringProperty(p, "script", "Script", "Script content",LA_WIDGET_STRING_MULTI,0,0,0,1,offsetof(laRackPage, Script), 0,0,0,0,0);
             laAddOperatorProperty(p,"add_rack","Add Rack", "Add a rack into the page", "LA_add_rack", '+', 0);
             laAddOperatorProperty(p,"remove_driver_page","Remove Page", "Remove this page", "LA_remove_driver_page", L'🗴', 0);

+ 13 - 4
resources/la_templates.c

@@ -1454,9 +1454,10 @@ void laui_UndoHistories(laUiList *uil, laPropPack *Base, laPropPack *OperatorIns
 void laui_Terminal(laUiList *uil, laPropPack *Base, laPropPack *OperatorInst, laColumn *ExtraColumns, int context){
     laColumn* c=laFirstColumn(uil);
     laUiItem* g=laMakeGroup(uil,c,"123",0);{ g->State=LA_UI_ACTIVE; g->Flags|=LA_UI_FLAGS_PREFER_BOTTOM;
-        laUiList* gu=g->Page; laColumn* gc=laFirstColumn(gu); gu->HeightCoeff=-1;
+        laUiList* gu=g->Page; laColumn* gc=laFirstColumn(gu); gu->HeightCoeff=-3;
         laShowItem(gu,gc,0,"la.logs")->Flags|=LA_UI_FLAGS_NO_DECAL;
     }
+    laShowItem(uil,c,0,"la.terminal_input");
 }
 void laui_IdleDataManager(laUiList *uil, laPropPack *Base, laPropPack *Extra, laColumn *ExtraColumns, int context){
     laColumn *c=laFirstColumn(uil);
@@ -1540,6 +1541,14 @@ void lauidetached_Drivers(laPanel* p){
     la_MakeDetachedProp(p, "la.detached_view_switch", "detached");
     la_MakeDetachedProp(p, "tns.world.root_objects", "root_object");
 }
+void laui_DriverListItem(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil),*cl, *cr,*cll,*clr;
+    laUiItem* b=laBeginRow(uil,c,0,0);
+    laShowItem(uil,c,This,"use_script")->Flags|=LA_UI_FLAGS_ICON|LA_UI_FLAGS_NO_DECAL|LA_UI_FLAGS_CYCLE;
+    laShowItem(uil,c,This,"identifier")->Flags|=LA_UI_FLAGS_PLAIN;
+    laShowItem(uil,c,This,"trigger")->Flags|=LA_UI_FLAGS_PLAIN;
+    laEndRow(uil,b);
+}
 void laui_Drivers(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil),*cl, *cr,*cll,*clr;
     laSplitColumn(uil,c,0.35); cl=laLeftColumn(c,7); cr=laRightColumn(c,0);
@@ -1551,17 +1560,17 @@ void laui_Drivers(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *
     laUiItem* b2=laOnConditionThat(uil,cl,laPropExpression(&rb->PP,""));{\
         laUiItem* b=laBeginRow(uil,cr,0,0);\
         laUiItem* b3=laOnConditionThat(uil,cl,laPropExpression(&rb->PP,"drivers.current_page"));{\
-            laUiItem* cp=laShowItemFull(uil,cr,&rb->PP,"drivers.current_page",LA_WIDGET_COLLECTION_SELECTOR,0,laui_IdentifierOnly,0);\
+            laUiItem* cp=laShowItemFull(uil,cr,&rb->PP,"drivers.current_page",LA_WIDGET_COLLECTION_SELECTOR,0,laui_DriverListItem,0);\
                 cp->Flags|=LA_UI_COLLECTION_SIMPLE_SELECTOR;\
             laShowItem(uil,cr,&cp->PP,"name");\
-            laShowItem(uil,cr,&rb->PP,"add_driver_page")->Flags|=LA_UI_FLAGS_ICON;\
+            laShowItem(uil,cr,&rb->PP,"add_driver_page")->Flags|=LA_UI_FLAGS_ICON|LA_UI_FLAGS_EXIT_WHEN_TRIGGERED;\
             laShowItem(uil,cr,&cp->PP,"remove_driver_page")->Flags|=LA_UI_FLAGS_ICON;\
             laShowSeparator(uil,cr);\
             laShowItem(uil,cr,&cp->PP,"trigger");\
             laShowItemFull(uil,cr,&cp->PP,"use_script",0,"icon=📃",0,0)\
                 ->Flags|=LA_UI_FLAGS_HIGHLIGHT|LA_UI_FLAGS_CYCLE|LA_UI_FLAGS_ICON;\
         }laElse(uil,b3);{\
-            laShowItem(uil,cr,&rb->PP,"add_driver_page")->Flags|=LA_UI_FLAGS_ICON;\
+            laShowItem(uil,cr,&rb->PP,"add_driver_page")->Flags|=LA_UI_FLAGS_ICON|LA_UI_FLAGS_EXIT_WHEN_TRIGGERED;\
         }laEndCondition(uil,b3);\
         laEndRow(uil,b);\
     }laEndCondition(uil,b2);\