updated codestyle
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-Make-Without-sqlite3 (push) Successful in 6s
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-CMake-Without-sqlite3 (push) Successful in 9s
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-Meson-Without-sqlite3 (push) Successful in 7s
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-Make-Without-sqlite3 (push) Successful in 6s
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-CMake-Without-sqlite3 (push) Successful in 9s
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-Meson-Without-sqlite3 (push) Successful in 7s
This commit is contained in:
@@ -8,7 +8,7 @@ OBJS = $(SRCS:.c=.o)
|
||||
TARGET = wterm
|
||||
BUILD_DIR=./build
|
||||
CROSS_COMPILE =
|
||||
LIBS = -lpthread
|
||||
LIBS = -lpthread
|
||||
# -lcrypto -lssl
|
||||
USE_SQLITE3 ?= 0
|
||||
WERROR ?= 0
|
||||
|
||||
+2244
-2570
File diff suppressed because it is too large
Load Diff
+218
-129
@@ -24,23 +24,27 @@
|
||||
#define cJSON__h
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
|
||||
#if !defined(__WINDOWS__) && \
|
||||
(defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
|
||||
#define __WINDOWS__
|
||||
#endif
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
|
||||
/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options:
|
||||
/* When compiling for windows, we specify a specific calling convention to avoid
|
||||
issues where we are being called from a project with a different default calling
|
||||
convention. For windows you have 3 define options:
|
||||
|
||||
CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
|
||||
CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
|
||||
CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
|
||||
CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever
|
||||
dllexport symbols CJSON_EXPORT_SYMBOLS - Define this on library build when you
|
||||
want to dllexport symbols (default) CJSON_IMPORT_SYMBOLS - Define this if you
|
||||
want to dllimport symbol
|
||||
|
||||
For *nix builds that support visibility attribute, you can define similar behavior by
|
||||
For *nix builds that support visibility attribute, you can define similar
|
||||
behavior by
|
||||
|
||||
setting default visibility to hidden by adding
|
||||
-fvisibility=hidden (for gcc)
|
||||
@@ -48,31 +52,35 @@ or
|
||||
-xldscope=hidden (for sun cc)
|
||||
to CFLAGS
|
||||
|
||||
then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
|
||||
then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way
|
||||
CJSON_EXPORT_SYMBOLS does
|
||||
|
||||
*/
|
||||
|
||||
#define CJSON_CDECL __cdecl
|
||||
#define CJSON_STDCALL __stdcall
|
||||
|
||||
/* export symbols by default, this is necessary for copy pasting the C and header file */
|
||||
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
|
||||
/* export symbols by default, this is necessary for copy pasting the C and
|
||||
* header file */
|
||||
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && \
|
||||
!defined(CJSON_EXPORT_SYMBOLS)
|
||||
#define CJSON_EXPORT_SYMBOLS
|
||||
#endif
|
||||
|
||||
#if defined(CJSON_HIDE_SYMBOLS)
|
||||
#define CJSON_PUBLIC(type) type CJSON_STDCALL
|
||||
#define CJSON_PUBLIC(type) type CJSON_STDCALL
|
||||
#elif defined(CJSON_EXPORT_SYMBOLS)
|
||||
#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
|
||||
#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
|
||||
#elif defined(CJSON_IMPORT_SYMBOLS)
|
||||
#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
|
||||
#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
|
||||
#endif
|
||||
#else /* !__WINDOWS__ */
|
||||
#define CJSON_CDECL
|
||||
#define CJSON_STDCALL
|
||||
|
||||
#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
|
||||
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
|
||||
#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined(__SUNPRO_C)) && \
|
||||
defined(CJSON_API_VISIBILITY)
|
||||
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
|
||||
#else
|
||||
#define CJSON_PUBLIC(type) type
|
||||
#endif
|
||||
@@ -87,115 +95,146 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ
|
||||
|
||||
/* cJSON Types: */
|
||||
#define cJSON_Invalid (0)
|
||||
#define cJSON_False (1 << 0)
|
||||
#define cJSON_True (1 << 1)
|
||||
#define cJSON_NULL (1 << 2)
|
||||
#define cJSON_False (1 << 0)
|
||||
#define cJSON_True (1 << 1)
|
||||
#define cJSON_NULL (1 << 2)
|
||||
#define cJSON_Number (1 << 3)
|
||||
#define cJSON_String (1 << 4)
|
||||
#define cJSON_Array (1 << 5)
|
||||
#define cJSON_Array (1 << 5)
|
||||
#define cJSON_Object (1 << 6)
|
||||
#define cJSON_Raw (1 << 7) /* raw json */
|
||||
#define cJSON_Raw (1 << 7) /* raw json */
|
||||
|
||||
#define cJSON_IsReference 256
|
||||
#define cJSON_StringIsConst 512
|
||||
|
||||
/* The cJSON structure: */
|
||||
typedef struct cJSON
|
||||
{
|
||||
/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
|
||||
struct cJSON *next;
|
||||
struct cJSON *prev;
|
||||
/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
|
||||
struct cJSON *child;
|
||||
typedef struct cJSON {
|
||||
/* next/prev allow you to walk array/object chains. Alternatively, use
|
||||
* GetArraySize/GetArrayItem/GetObjectItem */
|
||||
struct cJSON *next;
|
||||
struct cJSON *prev;
|
||||
/* An array or object item will have a child pointer pointing to a chain of
|
||||
* the items in the array/object. */
|
||||
struct cJSON *child;
|
||||
|
||||
/* The type of the item, as above. */
|
||||
int type;
|
||||
/* The type of the item, as above. */
|
||||
int type;
|
||||
|
||||
/* The item's string, if type==cJSON_String and type == cJSON_Raw */
|
||||
char *valuestring;
|
||||
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
|
||||
int valueint;
|
||||
/* The item's number, if type==cJSON_Number */
|
||||
double valuedouble;
|
||||
/* The item's string, if type==cJSON_String and type == cJSON_Raw */
|
||||
char *valuestring;
|
||||
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
|
||||
int valueint;
|
||||
/* The item's number, if type==cJSON_Number */
|
||||
double valuedouble;
|
||||
|
||||
/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
|
||||
char *string;
|
||||
/* The item's name string, if this item is the child of, or is in the list of
|
||||
* subitems of an object. */
|
||||
char *string;
|
||||
} cJSON;
|
||||
|
||||
typedef struct cJSON_Hooks
|
||||
{
|
||||
/* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
|
||||
void *(CJSON_CDECL *malloc_fn)(size_t sz);
|
||||
void (CJSON_CDECL *free_fn)(void *ptr);
|
||||
typedef struct cJSON_Hooks {
|
||||
/* malloc/free are CDECL on Windows regardless of the default calling
|
||||
* convention of the compiler, so ensure the hooks allow passing those
|
||||
* functions directly. */
|
||||
void *(CJSON_CDECL *malloc_fn)(size_t sz);
|
||||
void(CJSON_CDECL *free_fn)(void *ptr);
|
||||
} cJSON_Hooks;
|
||||
|
||||
typedef int cJSON_bool;
|
||||
|
||||
/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
|
||||
* This is to prevent stack overflows. */
|
||||
/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse
|
||||
* them. This is to prevent stack overflows. */
|
||||
#ifndef CJSON_NESTING_LIMIT
|
||||
#define CJSON_NESTING_LIMIT 1000
|
||||
#endif
|
||||
|
||||
/* Limits the length of circular references can be before cJSON rejects to parse them.
|
||||
* This is to prevent stack overflows. */
|
||||
/* Limits the length of circular references can be before cJSON rejects to parse
|
||||
* them. This is to prevent stack overflows. */
|
||||
#ifndef CJSON_CIRCULAR_LIMIT
|
||||
#define CJSON_CIRCULAR_LIMIT 10000
|
||||
#endif
|
||||
|
||||
/* returns the version of cJSON as a string */
|
||||
CJSON_PUBLIC(const char*) cJSON_Version(void);
|
||||
CJSON_PUBLIC(const char *) cJSON_Version(void);
|
||||
|
||||
/* Supply malloc, realloc and free functions to cJSON */
|
||||
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
|
||||
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks *hooks);
|
||||
|
||||
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
|
||||
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
|
||||
/* Memory Management: the caller is always responsible to free the results from
|
||||
* all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib
|
||||
* free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is
|
||||
* cJSON_PrintPreallocated, where the caller has full responsibility of the
|
||||
* buffer. */
|
||||
/* Supply a block of JSON, and this returns a cJSON object you can interrogate.
|
||||
*/
|
||||
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
|
||||
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
|
||||
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
|
||||
CJSON_PUBLIC(cJSON *)
|
||||
cJSON_ParseWithLength(const char *value, size_t buffer_length);
|
||||
/* ParseWithOpts allows you to require (and check) that the JSON is null
|
||||
* terminated, and to retrieve the pointer to the final byte parsed. */
|
||||
/* If you supply a ptr in return_parse_end and parsing fails, then
|
||||
* return_parse_end will contain a pointer to the error so will match
|
||||
* cJSON_GetErrorPtr(). */
|
||||
CJSON_PUBLIC(cJSON *)
|
||||
cJSON_ParseWithOpts(const char *value, const char **return_parse_end,
|
||||
cJSON_bool require_null_terminated);
|
||||
CJSON_PUBLIC(cJSON *)
|
||||
cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length,
|
||||
const char **return_parse_end,
|
||||
cJSON_bool require_null_terminated);
|
||||
|
||||
/* Render a cJSON entity to text for transfer/storage. */
|
||||
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
|
||||
/* Render a cJSON entity to text for transfer/storage without any formatting. */
|
||||
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
|
||||
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
|
||||
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
|
||||
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
|
||||
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
|
||||
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess
|
||||
* at the final size. guessing well reduces reallocation. fmt=0 gives
|
||||
* unformatted, =1 gives formatted */
|
||||
CJSON_PUBLIC(char *)
|
||||
cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
|
||||
/* Render a cJSON entity to text using a buffer already allocated in memory with
|
||||
* given length. Returns 1 on success and 0 on failure. */
|
||||
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will
|
||||
* use, so to be safe allocate 5 bytes more than you actually need */
|
||||
CJSON_PUBLIC(cJSON_bool)
|
||||
cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length,
|
||||
const cJSON_bool format);
|
||||
/* Delete a cJSON entity and all subentities. */
|
||||
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
|
||||
|
||||
/* Returns the number of items in an array (or object). */
|
||||
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
|
||||
/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
|
||||
/* Retrieve item number "index" from array "array". Returns NULL if
|
||||
* unsuccessful. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
|
||||
/* Get item "string" from object. Case insensitive. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
|
||||
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
|
||||
CJSON_PUBLIC(cJSON *)
|
||||
cJSON_GetObjectItem(const cJSON *const object, const char *const string);
|
||||
CJSON_PUBLIC(cJSON *)
|
||||
cJSON_GetObjectItemCaseSensitive(const cJSON *const object,
|
||||
const char *const string);
|
||||
CJSON_PUBLIC(cJSON_bool)
|
||||
cJSON_HasObjectItem(const cJSON *object, const char *string);
|
||||
/* For analysing failed parses. This returns a pointer to the parse error.
|
||||
* You'll probably need to look a few chars back to make sense of it. Defined
|
||||
* when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
|
||||
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
|
||||
|
||||
/* Check item type and return its value */
|
||||
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
|
||||
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
|
||||
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON *const item);
|
||||
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON *const item);
|
||||
|
||||
/* These functions check the type of an item */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON *const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON *const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON *const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON *const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON *const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON *const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON *const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON *const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON *const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON *const item);
|
||||
|
||||
/* These calls create a cJSON item of the appropriate type. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
|
||||
@@ -218,84 +257,134 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
|
||||
|
||||
/* These utilities create an Array of count items.
|
||||
* The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
|
||||
* The parameter count cannot be greater than the number of elements in the
|
||||
* number array, otherwise array access will be out of bounds.*/
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
|
||||
CJSON_PUBLIC(cJSON *)
|
||||
cJSON_CreateStringArray(const char *const *strings, int count);
|
||||
|
||||
/* Append item to the specified array/object. */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
|
||||
/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
|
||||
* WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
|
||||
* writing to `item->string` */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
|
||||
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
|
||||
CJSON_PUBLIC(cJSON_bool)
|
||||
cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
|
||||
/* Use this when string is definitely const (i.e. a literal, or as good as), and
|
||||
* will definitely survive the cJSON object. WARNING: When this function was
|
||||
* used, make sure to always check that (item->type & cJSON_StringIsConst) is
|
||||
* zero before writing to `item->string` */
|
||||
CJSON_PUBLIC(cJSON_bool)
|
||||
cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
|
||||
/* Append reference to item to the specified array/object. Use this when you
|
||||
* want to add an existing cJSON to a new cJSON, but don't want to corrupt your
|
||||
* existing cJSON. */
|
||||
CJSON_PUBLIC(cJSON_bool)
|
||||
cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
|
||||
CJSON_PUBLIC(cJSON_bool)
|
||||
cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
|
||||
|
||||
/* Remove/Detach items from Arrays/Objects. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON *)
|
||||
cJSON_DetachItemViaPointer(cJSON *parent, cJSON *const item);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
|
||||
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(cJSON *)
|
||||
cJSON_DetachItemFromObject(cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(cJSON *)
|
||||
cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(void)
|
||||
cJSON_DeleteItemFromObject(cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(void)
|
||||
cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
|
||||
|
||||
/* Update array items. */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
|
||||
CJSON_PUBLIC(cJSON_bool)
|
||||
cJSON_InsertItemInArray(
|
||||
cJSON *array, int which,
|
||||
cJSON *newitem); /* Shifts pre-existing items to the right. */
|
||||
CJSON_PUBLIC(cJSON_bool)
|
||||
cJSON_ReplaceItemViaPointer(cJSON *const parent, cJSON *const item,
|
||||
cJSON *replacement);
|
||||
CJSON_PUBLIC(cJSON_bool)
|
||||
cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
|
||||
CJSON_PUBLIC(cJSON_bool)
|
||||
cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem);
|
||||
CJSON_PUBLIC(cJSON_bool)
|
||||
cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string,
|
||||
cJSON *newitem);
|
||||
|
||||
/* Duplicate a cJSON item */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
|
||||
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
|
||||
* need to be released. With recurse!=0, it will duplicate any children connected to the item.
|
||||
* The item->next and ->prev pointers are always zero on return from Duplicate. */
|
||||
/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
|
||||
* case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
|
||||
/* Duplicate will create a new, identical cJSON item to the one you pass, in new
|
||||
* memory that will need to be released. With recurse!=0, it will duplicate any
|
||||
* children connected to the item. The item->next and ->prev pointers are always
|
||||
* zero on return from Duplicate. */
|
||||
/* Recursively compare two cJSON items for equality. If either a or b is NULL or
|
||||
* invalid, they will be considered unequal. case_sensitive determines if object
|
||||
* keys are treated case sensitive (1) or case insensitive (0) */
|
||||
CJSON_PUBLIC(cJSON_bool)
|
||||
cJSON_Compare(const cJSON *const a, const cJSON *const b,
|
||||
const cJSON_bool case_sensitive);
|
||||
|
||||
/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
|
||||
* The input pointer json cannot point to a read-only address area, such as a string constant,
|
||||
* but should point to a readable and writable address area. */
|
||||
/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from
|
||||
* strings. The input pointer json cannot point to a read-only address area,
|
||||
* such as a string constant, but should point to a readable and writable
|
||||
* address area. */
|
||||
CJSON_PUBLIC(void) cJSON_Minify(char *json);
|
||||
|
||||
/* Helper functions for creating and adding items to an object at the same time.
|
||||
* They return the added item or NULL on failure. */
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
|
||||
CJSON_PUBLIC(cJSON *)
|
||||
cJSON_AddNullToObject(cJSON *const object, const char *const name);
|
||||
CJSON_PUBLIC(cJSON *)
|
||||
cJSON_AddTrueToObject(cJSON *const object, const char *const name);
|
||||
CJSON_PUBLIC(cJSON *)
|
||||
cJSON_AddFalseToObject(cJSON *const object, const char *const name);
|
||||
CJSON_PUBLIC(cJSON *)
|
||||
cJSON_AddBoolToObject(cJSON *const object, const char *const name,
|
||||
const cJSON_bool boolean);
|
||||
CJSON_PUBLIC(cJSON *)
|
||||
cJSON_AddNumberToObject(cJSON *const object, const char *const name,
|
||||
const double number);
|
||||
CJSON_PUBLIC(cJSON *)
|
||||
cJSON_AddStringToObject(cJSON *const object, const char *const name,
|
||||
const char *const string);
|
||||
CJSON_PUBLIC(cJSON *)
|
||||
cJSON_AddRawToObject(cJSON *const object, const char *const name,
|
||||
const char *const raw);
|
||||
CJSON_PUBLIC(cJSON *)
|
||||
cJSON_AddObjectToObject(cJSON *const object, const char *const name);
|
||||
CJSON_PUBLIC(cJSON *)
|
||||
cJSON_AddArrayToObject(cJSON *const object, const char *const name);
|
||||
|
||||
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
|
||||
#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
|
||||
/* When assigning an integer value, it needs to be propagated to valuedouble
|
||||
* too. */
|
||||
#define cJSON_SetIntValue(object, number) \
|
||||
((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
|
||||
/* helper for the cJSON_SetNumberValue macro */
|
||||
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
|
||||
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
|
||||
/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
|
||||
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
|
||||
#define cJSON_SetNumberValue(object, number) \
|
||||
((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
|
||||
/* Change the valuestring of a cJSON_String object, only takes effect when type
|
||||
* of object is cJSON_String */
|
||||
CJSON_PUBLIC(char *)
|
||||
cJSON_SetValuestring(cJSON *object, const char *valuestring);
|
||||
|
||||
/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
|
||||
#define cJSON_SetBoolValue(object, boolValue) ( \
|
||||
(object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \
|
||||
(object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \
|
||||
cJSON_Invalid\
|
||||
)
|
||||
/* If the object is not a boolean type this does nothing and returns
|
||||
* cJSON_Invalid else it returns the new type*/
|
||||
#define cJSON_SetBoolValue(object, boolValue) \
|
||||
((object != NULL && ((object)->type & (cJSON_False | cJSON_True))) \
|
||||
? (object)->type = ((object)->type & (~(cJSON_False | cJSON_True))) | \
|
||||
((boolValue) ? cJSON_True : cJSON_False) \
|
||||
: cJSON_Invalid)
|
||||
|
||||
/* Macro for iterating over an array or object */
|
||||
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
|
||||
#define cJSON_ArrayForEach(element, array) \
|
||||
for (element = (array != NULL) ? (array)->child : NULL; element != NULL; \
|
||||
element = element->next)
|
||||
|
||||
/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
|
||||
/* malloc/free objects using the malloc/free functions that have been set with
|
||||
* cJSON_InitHooks */
|
||||
CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
|
||||
CJSON_PUBLIC(void) cJSON_free(void *object);
|
||||
|
||||
|
||||
+268
-251
@@ -1,13 +1,13 @@
|
||||
#include "terminal.h"
|
||||
#include <cJSON.h>
|
||||
#include <errno.h>
|
||||
#include <mongoose.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <mongoose.h>
|
||||
#include <cJSON.h>
|
||||
#include "terminal.h"
|
||||
#include <sys/wait.h>
|
||||
|
||||
#define HTTP_PORT "8888"
|
||||
#define STATIC_DIR "./static/"
|
||||
@@ -16,284 +16,301 @@ static const char *s_http_addr = "http://0.0.0.0:8888";
|
||||
static struct mg_mgr mgr;
|
||||
|
||||
struct client_data {
|
||||
struct terminal_session term;
|
||||
bool terminal_ready;
|
||||
struct terminal_session term;
|
||||
bool terminal_ready;
|
||||
};
|
||||
|
||||
// Функция для логирования запросов
|
||||
static void log_request(struct mg_connection *c, struct mg_http_message *hm) {
|
||||
printf("HTTP Request: %.*s %.*s\n", (int)hm->method.len, hm->method.buf,
|
||||
(int)hm->uri.len, hm->uri.buf);
|
||||
printf("HTTP Request: %.*s %.*s\n", (int)hm->method.len, hm->method.buf,
|
||||
(int)hm->uri.len, hm->uri.buf);
|
||||
}
|
||||
|
||||
// Проверяем, жив ли дочерний процесс
|
||||
static bool is_process_alive(pid_t pid) {
|
||||
if (pid <= 0) return false;
|
||||
|
||||
int status;
|
||||
pid_t result = waitpid(pid, &status, WNOHANG);
|
||||
if (result == 0) {
|
||||
// Процесс все еще работает
|
||||
return true;
|
||||
} else if (result == -1) {
|
||||
// Ошибка
|
||||
if (errno == ECHILD) {
|
||||
// Процесс не существует
|
||||
return false;
|
||||
}
|
||||
perror("waitpid");
|
||||
return false;
|
||||
} else {
|
||||
// Процесс завершился
|
||||
return false;
|
||||
if (pid <= 0)
|
||||
return false;
|
||||
|
||||
int status;
|
||||
pid_t result = waitpid(pid, &status, WNOHANG);
|
||||
if (result == 0) {
|
||||
// Процесс все еще работает
|
||||
return true;
|
||||
} else if (result == -1) {
|
||||
// Ошибка
|
||||
if (errno == ECHILD) {
|
||||
// Процесс не существует
|
||||
return false;
|
||||
}
|
||||
perror("waitpid");
|
||||
return false;
|
||||
} else {
|
||||
// Процесс завершился
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Перезапускаем терминальную сессию
|
||||
static void restart_terminal_session(struct mg_connection *c, struct client_data *cd) {
|
||||
if (!cd) return;
|
||||
|
||||
printf("Restarting terminal session...\n");
|
||||
|
||||
// Уничтожаем старую сессию
|
||||
if (cd->terminal_ready) {
|
||||
destroy_terminal_session(&cd->term);
|
||||
}
|
||||
|
||||
// Создаем новую сессию
|
||||
const char *shell = getenv("SHELL");
|
||||
if (!shell) shell = "/bin/bash";
|
||||
|
||||
if (create_terminal_session(&cd->term, shell) == 0) {
|
||||
cd->terminal_ready = true;
|
||||
mg_ws_send(c, "\r\n*** Terminal restarted ***\r\n", 29, WEBSOCKET_OP_TEXT);
|
||||
} else {
|
||||
mg_ws_send(c, "\r\n*** Failed to restart terminal ***\r\n", 37, WEBSOCKET_OP_TEXT);
|
||||
cd->terminal_ready = false;
|
||||
}
|
||||
static void restart_terminal_session(struct mg_connection *c,
|
||||
struct client_data *cd) {
|
||||
if (!cd)
|
||||
return;
|
||||
|
||||
printf("Restarting terminal session...\n");
|
||||
|
||||
// Уничтожаем старую сессию
|
||||
if (cd->terminal_ready) {
|
||||
destroy_terminal_session(&cd->term);
|
||||
}
|
||||
|
||||
// Создаем новую сессию
|
||||
const char *shell = getenv("SHELL");
|
||||
if (!shell)
|
||||
shell = "/bin/bash";
|
||||
|
||||
if (create_terminal_session(&cd->term, shell) == 0) {
|
||||
cd->terminal_ready = true;
|
||||
mg_ws_send(c, "\r\n*** Terminal restarted ***\r\n", 29, WEBSOCKET_OP_TEXT);
|
||||
} else {
|
||||
mg_ws_send(c, "\r\n*** Failed to restart terminal ***\r\n", 37,
|
||||
WEBSOCKET_OP_TEXT);
|
||||
cd->terminal_ready = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Обработчик WebSocket сообщений
|
||||
static void handle_websocket_message(struct mg_connection *c, struct mg_ws_message *wm, struct client_data *cd) {
|
||||
if (!cd) return;
|
||||
|
||||
if (wm->data.len == 0) {
|
||||
return;
|
||||
static void handle_websocket_message(struct mg_connection *c,
|
||||
struct mg_ws_message *wm,
|
||||
struct client_data *cd) {
|
||||
if (!cd)
|
||||
return;
|
||||
|
||||
if (wm->data.len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Парсим JSON для проверки resize сообщения
|
||||
cJSON *json = cJSON_ParseWithLength(wm->data.buf, wm->data.len);
|
||||
if (json != NULL) {
|
||||
cJSON *cols = cJSON_GetObjectItemCaseSensitive(json, "cols");
|
||||
cJSON *rows = cJSON_GetObjectItemCaseSensitive(json, "rows");
|
||||
|
||||
if (cJSON_IsNumber(cols) && cJSON_IsNumber(rows)) {
|
||||
printf("Resizing terminal to %dx%d\n", rows->valueint, cols->valueint);
|
||||
resize_terminal(&cd->term, rows->valueint, cols->valueint);
|
||||
cJSON_Delete(json);
|
||||
return;
|
||||
}
|
||||
|
||||
// Парсим JSON для проверки resize сообщения
|
||||
cJSON *json = cJSON_ParseWithLength(wm->data.buf, wm->data.len);
|
||||
if (json != NULL) {
|
||||
cJSON *cols = cJSON_GetObjectItemCaseSensitive(json, "cols");
|
||||
cJSON *rows = cJSON_GetObjectItemCaseSensitive(json, "rows");
|
||||
|
||||
if (cJSON_IsNumber(cols) && cJSON_IsNumber(rows)) {
|
||||
printf("Resizing terminal to %dx%d\n", rows->valueint, cols->valueint);
|
||||
resize_terminal(&cd->term, rows->valueint, cols->valueint);
|
||||
cJSON_Delete(json);
|
||||
return;
|
||||
}
|
||||
cJSON_Delete(json);
|
||||
}
|
||||
|
||||
// Проверяем, не команда ли это exit
|
||||
if (wm->data.len >= 4 && strncmp(wm->data.buf, "exit", 4) == 0) {
|
||||
printf("Exit command detected, restarting terminal session\n");
|
||||
write_to_terminal(&cd->term, wm->data.buf, wm->data.len);
|
||||
// Даем команде выполниться, затем перезапускаем
|
||||
usleep(100);
|
||||
restart_terminal_session(c, cd);
|
||||
return;
|
||||
}
|
||||
|
||||
// Обычный ввод от пользователя
|
||||
cJSON_Delete(json);
|
||||
}
|
||||
|
||||
// Проверяем, не команда ли это exit
|
||||
if (wm->data.len >= 4 && strncmp(wm->data.buf, "exit", 4) == 0) {
|
||||
printf("Exit command detected, restarting terminal session\n");
|
||||
write_to_terminal(&cd->term, wm->data.buf, wm->data.len);
|
||||
// Даем команде выполниться, затем перезапускаем
|
||||
usleep(100);
|
||||
restart_terminal_session(c, cd);
|
||||
return;
|
||||
}
|
||||
|
||||
// Обычный ввод от пользователя
|
||||
write_to_terminal(&cd->term, wm->data.buf, wm->data.len);
|
||||
}
|
||||
|
||||
// Обработчик HTTP запросов
|
||||
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *)ev_data;
|
||||
log_request(c, hm);
|
||||
|
||||
if (mg_match(hm->uri, mg_str("/ws"), nullptr)) {
|
||||
// Проверяем, нет ли уже активного соединения
|
||||
if (c->fn_data != NULL) {
|
||||
printf("WebSocket connection already exists for this connection, cleaning up...\n");
|
||||
struct client_data *old_cd = (struct client_data *)c->fn_data;
|
||||
destroy_terminal_session(&old_cd->term);
|
||||
free(old_cd);
|
||||
c->fn_data = NULL;
|
||||
}
|
||||
|
||||
// Upgrade до WebSocket
|
||||
struct client_data *cd = calloc(1, sizeof(struct client_data));
|
||||
if (!cd) {
|
||||
mg_http_reply(c, 500, "Content-Type: text/plain\r\n", "Memory allocation failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Создаем терминальную сессию
|
||||
const char *shell = getenv("SHELL");
|
||||
if (!shell) shell = "/bin/bash";
|
||||
|
||||
printf("Creating new terminal session with shell: %s\n", shell);
|
||||
if (create_terminal_session(&cd->term, shell) != 0) {
|
||||
mg_http_reply(c, 500, "Content-Type: text/plain\r\n", "Failed to create terminal session");
|
||||
free(cd);
|
||||
return;
|
||||
}
|
||||
|
||||
cd->terminal_ready = true;
|
||||
c->fn_data = cd;
|
||||
|
||||
// Upgrade to WebSocket
|
||||
struct mg_str *ws_proto = mg_http_get_header(hm, "Sec-WebSocket-Protocol");
|
||||
if (ws_proto != NULL) {
|
||||
mg_ws_upgrade(c, hm, ws_proto->buf);
|
||||
} else {
|
||||
mg_ws_upgrade(c, hm, NULL);
|
||||
}
|
||||
|
||||
printf("WebSocket upgrade completed\n");
|
||||
|
||||
} else if (mg_match(hm->uri, mg_str("/"), nullptr)) {
|
||||
// Главная страница
|
||||
struct mg_http_serve_opts opts = { .root_dir = STATIC_DIR };
|
||||
mg_http_serve_file(c, hm, "./static/index.html", &opts);
|
||||
|
||||
} else if (mg_match(hm->uri, mg_str("/xterm.js"), nullptr)) {
|
||||
// xterm.js
|
||||
struct mg_http_serve_opts opts = { .root_dir = STATIC_DIR };
|
||||
mg_http_serve_file(c, hm, "./static/xterm.js", &opts);
|
||||
|
||||
} else if (mg_match(hm->uri, mg_str("/xterm.css"), nullptr)) {
|
||||
// xterm.css
|
||||
struct mg_http_serve_opts opts = { .root_dir = STATIC_DIR };
|
||||
mg_http_serve_file(c, hm, "./static/xterm.css", &opts);
|
||||
|
||||
} else if (mg_match(hm->uri, mg_str("/xterm-addon-fit.js"), nullptr)) {
|
||||
// xterm-addon-fit.js
|
||||
struct mg_http_serve_opts opts = { .root_dir = STATIC_DIR };
|
||||
mg_http_serve_file(c, hm, "./static/xterm-addon-fit.js", &opts);
|
||||
|
||||
} else {
|
||||
mg_http_reply(c, 404, "Content-Type: text/plain\r\n", "Not Found");
|
||||
}
|
||||
} else if (ev == MG_EV_WS_OPEN) {
|
||||
// WebSocket соединение установлено
|
||||
printf("WebSocket connection opened\n");
|
||||
struct client_data *cd = (struct client_data *)c->fn_data;
|
||||
if (cd) {
|
||||
mg_ws_send(c, "Terminal ready - type 'exit' to restart session\r\n", 49, WEBSOCKET_OP_TEXT);
|
||||
}
|
||||
} else if (ev == MG_EV_WS_MSG) {
|
||||
// Сообщение от WebSocket клиента
|
||||
struct mg_ws_message *wm = (struct mg_ws_message *)ev_data;
|
||||
struct client_data *cd = (struct client_data *)c->fn_data;
|
||||
handle_websocket_message(c, wm, cd);
|
||||
} else if (ev == MG_EV_CLOSE) {
|
||||
// Закрытие соединения
|
||||
struct client_data *cd = (struct client_data *)c->fn_data;
|
||||
if (cd) {
|
||||
printf("WebSocket connection closed, cleaning up...\n");
|
||||
destroy_terminal_session(&cd->term);
|
||||
free(cd);
|
||||
c->fn_data = NULL;
|
||||
}
|
||||
} else if (ev == MG_EV_POLL) {
|
||||
// Проверяем данные от терминала и отправляем через WebSocket
|
||||
struct mg_connection *conn;
|
||||
for (conn = mgr.conns; conn != NULL; conn = conn->next) {
|
||||
if (!conn->is_websocket) continue;
|
||||
|
||||
struct client_data *cd = (struct client_data *)conn->fn_data;
|
||||
if (cd && cd->terminal_ready) {
|
||||
// Проверяем, не завершился ли процесс
|
||||
if (!is_process_alive(cd->term.child_pid)) {
|
||||
printf("Terminal process died, restarting...\n");
|
||||
restart_terminal_session(conn, cd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Читаем данные из терминала
|
||||
char buffer[1024];
|
||||
ssize_t bytes_read = read_from_terminal(&cd->term, buffer, sizeof(buffer));
|
||||
if (bytes_read > 0) {
|
||||
mg_ws_send(conn, buffer, bytes_read, WEBSOCKET_OP_BINARY);
|
||||
} else if (bytes_read == 0) {
|
||||
// EOF - процесс завершился
|
||||
printf("EOF from terminal, restarting...\n");
|
||||
restart_terminal_session(conn, cd);
|
||||
} else if (bytes_read < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||
// Ошибка чтения
|
||||
printf("Error reading from terminal: %s\n", strerror(errno));
|
||||
restart_terminal_session(conn, cd);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *)ev_data;
|
||||
log_request(c, hm);
|
||||
|
||||
if (mg_match(hm->uri, mg_str("/ws"), nullptr)) {
|
||||
// Проверяем, нет ли уже активного соединения
|
||||
if (c->fn_data != NULL) {
|
||||
printf("WebSocket connection already exists for this connection, "
|
||||
"cleaning up...\n");
|
||||
struct client_data *old_cd = (struct client_data *)c->fn_data;
|
||||
destroy_terminal_session(&old_cd->term);
|
||||
free(old_cd);
|
||||
c->fn_data = NULL;
|
||||
}
|
||||
|
||||
// Upgrade до WebSocket
|
||||
struct client_data *cd = calloc(1, sizeof(struct client_data));
|
||||
if (!cd) {
|
||||
mg_http_reply(c, 500, "Content-Type: text/plain\r\n",
|
||||
"Memory allocation failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Создаем терминальную сессию
|
||||
const char *shell = getenv("SHELL");
|
||||
if (!shell)
|
||||
shell = "/bin/bash";
|
||||
|
||||
printf("Creating new terminal session with shell: %s\n", shell);
|
||||
if (create_terminal_session(&cd->term, shell) != 0) {
|
||||
mg_http_reply(c, 500, "Content-Type: text/plain\r\n",
|
||||
"Failed to create terminal session");
|
||||
free(cd);
|
||||
return;
|
||||
}
|
||||
|
||||
cd->terminal_ready = true;
|
||||
c->fn_data = cd;
|
||||
|
||||
// Upgrade to WebSocket
|
||||
struct mg_str *ws_proto =
|
||||
mg_http_get_header(hm, "Sec-WebSocket-Protocol");
|
||||
if (ws_proto != NULL) {
|
||||
mg_ws_upgrade(c, hm, ws_proto->buf);
|
||||
} else {
|
||||
mg_ws_upgrade(c, hm, NULL);
|
||||
}
|
||||
|
||||
printf("WebSocket upgrade completed\n");
|
||||
|
||||
} else if (mg_match(hm->uri, mg_str("/"), nullptr)) {
|
||||
// Главная страница
|
||||
struct mg_http_serve_opts opts = {.root_dir = STATIC_DIR};
|
||||
mg_http_serve_file(c, hm, "./static/index.html", &opts);
|
||||
|
||||
} else if (mg_match(hm->uri, mg_str("/xterm.js"), nullptr)) {
|
||||
// xterm.js
|
||||
struct mg_http_serve_opts opts = {.root_dir = STATIC_DIR};
|
||||
mg_http_serve_file(c, hm, "./static/xterm.js", &opts);
|
||||
|
||||
} else if (mg_match(hm->uri, mg_str("/xterm.css"), nullptr)) {
|
||||
// xterm.css
|
||||
struct mg_http_serve_opts opts = {.root_dir = STATIC_DIR};
|
||||
mg_http_serve_file(c, hm, "./static/xterm.css", &opts);
|
||||
|
||||
} else if (mg_match(hm->uri, mg_str("/xterm-addon-fit.js"), nullptr)) {
|
||||
// xterm-addon-fit.js
|
||||
struct mg_http_serve_opts opts = {.root_dir = STATIC_DIR};
|
||||
mg_http_serve_file(c, hm, "./static/xterm-addon-fit.js", &opts);
|
||||
|
||||
} else {
|
||||
mg_http_reply(c, 404, "Content-Type: text/plain\r\n", "Not Found");
|
||||
}
|
||||
} else if (ev == MG_EV_WS_OPEN) {
|
||||
// WebSocket соединение установлено
|
||||
printf("WebSocket connection opened\n");
|
||||
struct client_data *cd = (struct client_data *)c->fn_data;
|
||||
if (cd) {
|
||||
mg_ws_send(c, "Terminal ready - type 'exit' to restart session\r\n", 49,
|
||||
WEBSOCKET_OP_TEXT);
|
||||
}
|
||||
} else if (ev == MG_EV_WS_MSG) {
|
||||
// Сообщение от WebSocket клиента
|
||||
struct mg_ws_message *wm = (struct mg_ws_message *)ev_data;
|
||||
struct client_data *cd = (struct client_data *)c->fn_data;
|
||||
handle_websocket_message(c, wm, cd);
|
||||
} else if (ev == MG_EV_CLOSE) {
|
||||
// Закрытие соединения
|
||||
struct client_data *cd = (struct client_data *)c->fn_data;
|
||||
if (cd) {
|
||||
printf("WebSocket connection closed, cleaning up...\n");
|
||||
destroy_terminal_session(&cd->term);
|
||||
free(cd);
|
||||
c->fn_data = NULL;
|
||||
}
|
||||
} else if (ev == MG_EV_POLL) {
|
||||
// Проверяем данные от терминала и отправляем через WebSocket
|
||||
struct mg_connection *conn;
|
||||
for (conn = mgr.conns; conn != NULL; conn = conn->next) {
|
||||
if (!conn->is_websocket)
|
||||
continue;
|
||||
|
||||
struct client_data *cd = (struct client_data *)conn->fn_data;
|
||||
if (cd && cd->terminal_ready) {
|
||||
// Проверяем, не завершился ли процесс
|
||||
if (!is_process_alive(cd->term.child_pid)) {
|
||||
printf("Terminal process died, restarting...\n");
|
||||
restart_terminal_session(conn, cd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Читаем данные из терминала
|
||||
char buffer[1024];
|
||||
ssize_t bytes_read =
|
||||
read_from_terminal(&cd->term, buffer, sizeof(buffer));
|
||||
if (bytes_read > 0) {
|
||||
mg_ws_send(conn, buffer, bytes_read, WEBSOCKET_OP_BINARY);
|
||||
} else if (bytes_read == 0) {
|
||||
// EOF - процесс завершился
|
||||
printf("EOF from terminal, restarting...\n");
|
||||
restart_terminal_session(conn, cd);
|
||||
} else if (bytes_read < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||
// Ошибка чтения
|
||||
printf("Error reading from terminal: %s\n", strerror(errno));
|
||||
restart_terminal_session(conn, cd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Обработчик сигналов для graceful shutdown
|
||||
static void signal_handler(int sig) {
|
||||
printf("\nShutting down server...\n");
|
||||
|
||||
// Очищаем все соединения
|
||||
struct mg_connection *conn;
|
||||
for (conn = mgr.conns; conn != NULL; conn = conn->next) {
|
||||
if (conn->fn_data) {
|
||||
struct client_data *cd = (struct client_data *)conn->fn_data;
|
||||
destroy_terminal_session(&cd->term);
|
||||
free(cd);
|
||||
conn->fn_data = NULL;
|
||||
}
|
||||
printf("\nShutting down server...\n");
|
||||
|
||||
// Очищаем все соединения
|
||||
struct mg_connection *conn;
|
||||
for (conn = mgr.conns; conn != NULL; conn = conn->next) {
|
||||
if (conn->fn_data) {
|
||||
struct client_data *cd = (struct client_data *)conn->fn_data;
|
||||
destroy_terminal_session(&cd->term);
|
||||
free(cd);
|
||||
conn->fn_data = NULL;
|
||||
}
|
||||
|
||||
mg_mgr_free(&mgr);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
mg_mgr_free(&mgr);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Проверяем существование директории
|
||||
static bool directory_exists(const char *path) {
|
||||
struct stat st;
|
||||
return stat(path, &st) == 0 && S_ISDIR(st.st_mode);
|
||||
struct stat st;
|
||||
return stat(path, &st) == 0 && S_ISDIR(st.st_mode);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
// Устанавливаем обработчики сигналов
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
|
||||
// Инициализируем менеджер соединений
|
||||
mg_mgr_init(&mgr);
|
||||
printf("Mongoose version: %s\n", MG_VERSION);
|
||||
|
||||
// Проверяем существование статической директории
|
||||
if (!directory_exists(STATIC_DIR)) {
|
||||
fprintf(stderr, "Static directory '%s' not found! Creating it...\n", STATIC_DIR);
|
||||
if (mkdir(STATIC_DIR, 0755) != 0) {
|
||||
fprintf(stderr, "Failed to create static directory\n");
|
||||
return 1;
|
||||
}
|
||||
// Устанавливаем обработчики сигналов
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
|
||||
// Инициализируем менеджер соединений
|
||||
mg_mgr_init(&mgr);
|
||||
printf("Mongoose version: %s\n", MG_VERSION);
|
||||
|
||||
// Проверяем существование статической директории
|
||||
if (!directory_exists(STATIC_DIR)) {
|
||||
fprintf(stderr, "Static directory '%s' not found! Creating it...\n",
|
||||
STATIC_DIR);
|
||||
if (mkdir(STATIC_DIR, 0755) != 0) {
|
||||
fprintf(stderr, "Failed to create static directory\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Создаем HTTP сервер
|
||||
struct mg_connection *c = mg_http_listen(&mgr, s_http_addr, fn, NULL);
|
||||
if (c == NULL) {
|
||||
fprintf(stderr, "Failed to start server on %s\n", s_http_addr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Starting web terminal server on %s\n", s_http_addr);
|
||||
printf("Static files directory: %s\n", STATIC_DIR);
|
||||
printf("Server is ready. Open http://localhost:8000 in your browser\n");
|
||||
|
||||
// Основной цикл
|
||||
while (true) {
|
||||
mg_mgr_poll(&mgr, 50); // 50ms timeout
|
||||
}
|
||||
|
||||
mg_mgr_free(&mgr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Создаем HTTP сервер
|
||||
struct mg_connection *c = mg_http_listen(&mgr, s_http_addr, fn, NULL);
|
||||
if (c == NULL) {
|
||||
fprintf(stderr, "Failed to start server on %s\n", s_http_addr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Starting web terminal server on %s\n", s_http_addr);
|
||||
printf("Static files directory: %s\n", STATIC_DIR);
|
||||
printf("Server is ready. Open http://localhost:8000 in your browser\n");
|
||||
|
||||
// Основной цикл
|
||||
while (true) {
|
||||
mg_mgr_poll(&mgr, 50); // 50ms timeout
|
||||
}
|
||||
|
||||
mg_mgr_free(&mgr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
+18
-18
@@ -16361,7 +16361,7 @@ int mg_rsa_mod_pow(const uint8_t *mod, size_t modsz, const uint8_t *exp, size_t
|
||||
#define REPEATM(N, macro) EVAL(REPEATM_SOME(N, macro))
|
||||
#endif
|
||||
|
||||
//
|
||||
//
|
||||
|
||||
#if (MG_UECC_WORD_SIZE == 1)
|
||||
#if MG_UECC_SUPPORTS_secp160r1
|
||||
@@ -20899,7 +20899,7 @@ static bool cyw_wifi_connect(char *ssid, char *pass) {
|
||||
uint32_t data[64/4 + 1]; // max pass length: 64 for WPA, 128 for WPA3 SAE
|
||||
uint16_t *da = (uint16_t *) data;
|
||||
unsigned int len;
|
||||
uint32_t val;
|
||||
uint32_t val;
|
||||
val = 4; // security type: 0 for none, 2 for WPA, 4 for WPA2/WPA3, 6 for mixed WPA/WPA2
|
||||
// sup_wpa[1] = 0 if not using security
|
||||
if (!(cyw_ioctl_set(134 /* SET_WSEC */, (uint8_t *)&val, sizeof(val))
|
||||
@@ -20941,7 +20941,7 @@ static bool cyw_wifi_ap_start(char *ssid, char *pass, unsigned int channel) {
|
||||
uint32_t data[64/4 + 2]; // max pass length: 64 for WPA, 128 for WPA3 SAE
|
||||
uint16_t *da = (uint16_t *) data;
|
||||
unsigned int len;
|
||||
uint32_t val;
|
||||
uint32_t val;
|
||||
// CHIP DEPENDENCY
|
||||
// RPi set the AMPDU parameter for AP (window size = 2) *****************
|
||||
// val = 2 ; cyw_ioctl_iovar_set_(0, "ampdu_ba_wsize", (uint8_t *)&val, sizeof(val));
|
||||
@@ -20970,10 +20970,10 @@ static bool cyw_wifi_ap_start(char *ssid, char *pass, unsigned int channel) {
|
||||
da[1] = 1; // indicates wireless security key, skip for WPA3 SAE
|
||||
memcpy((uint8_t *)data + 2 * sizeof(uint16_t), pass, len); // skip for WPA3 SAE
|
||||
if (!cyw_ioctl_set_(1, 268 /* SET_WSEC_PMK */, data, sizeof(data))) return false; // skip for WPA3 SAE, sizeof/2 if supporting SAE but using WPA
|
||||
/* for WPA3 SAE:
|
||||
/* for WPA3 SAE:
|
||||
memcpy((uint8_t *)data + sizeof(uint16_t), pass, len);
|
||||
cyw_ioctl_iovar_set_(1, "sae_password", data, sizeof(data)); */
|
||||
/* for WPA3 or mixed WPA2/WPA3:
|
||||
/* for WPA3 or mixed WPA2/WPA3:
|
||||
val = 5 ; cyw_ioctl_iovar_set_(1, "sae_max_pwe_loop", (uint8_t *)&val, sizeof(val)); // Some chipsets do not support this */
|
||||
// resume if not using auth
|
||||
|
||||
@@ -21119,7 +21119,7 @@ static void cyw_handle_scan_result(uint32_t status, struct scan_result *data, si
|
||||
bss.band = band & CYW_BSS_BAND2G ? MG_WIFI_BAND_2G : MG_WIFI_BAND_5G;
|
||||
bss.security = (sbss->capability & MG_BIT(4) /* CAP_PRIVACY */) ? MG_WIFI_SECURITY_WEP : MG_WIFI_SECURITY_OPEN;
|
||||
{ // travel IEs (Information Elements) in search of security definitions
|
||||
const uint8_t wot1[4] = {0x00, 0x50, 0xf2, 0x01}; // WPA_OUI_TYPE1
|
||||
const uint8_t wot1[4] = {0x00, 0x50, 0xf2, 0x01}; // WPA_OUI_TYPE1
|
||||
uint8_t *ie = (uint8_t *)sbss + sbss->ie_offset;
|
||||
int bytes = (int) sbss->ie_length;
|
||||
while (bytes > 0 && ie[1] + 2 < bytes) { // ie[0] -> type, ie[1] -> bytes from ie[2]
|
||||
@@ -21130,11 +21130,11 @@ static void cyw_handle_scan_result(uint32_t status, struct scan_result *data, si
|
||||
bytes -= ie[1] + 2;
|
||||
}
|
||||
}
|
||||
MG_VERBOSE(("BSS: %.*s (%u) (%M) %d dBm %u", bss.SSID.len, bss.SSID.buf, bss.channel, mg_print_mac, bss.BSSID, (int) bss.RSSI, bss.security));
|
||||
MG_VERBOSE(("BSS: %.*s (%u) (%M) %d dBm %u", bss.SSID.len, bss.SSID.buf, bss.channel, mg_print_mac, bss.BSSID, (int) bss.RSSI, bss.security));
|
||||
mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_SCAN_RESULT, &bss);
|
||||
} else {
|
||||
MG_ERROR(("scan error"));
|
||||
}
|
||||
}
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
@@ -21326,7 +21326,7 @@ static bool cyw_init(uint8_t *mac) {
|
||||
// CHIP DEPENDENCY
|
||||
val = 8 ; cyw_ioctl_iovar_set("ampdu_ba_wsize", (uint8_t *)&val, sizeof(val));
|
||||
val = 4 ; cyw_ioctl_iovar_set("ampdu_mpdu", (uint8_t *)&val, sizeof(val));
|
||||
val = 0 /* 8K */; cyw_ioctl_iovar_set("ampdu_rx_factor", (uint8_t *)&val, sizeof(val));
|
||||
val = 0 /* 8K */; cyw_ioctl_iovar_set("ampdu_rx_factor", (uint8_t *)&val, sizeof(val));
|
||||
//
|
||||
{
|
||||
struct cyw_country c;
|
||||
@@ -21544,7 +21544,7 @@ static bool cyw_load_fwll(void *fwdata, size_t fwlen, void *nvramdata, size_t nv
|
||||
cyw_load_data(CYW_CHIP_ATCMRAM_BASE, fwdata, fwlen);
|
||||
mg_delayms(5); // TODO(scaprile): CHECK IF THIS IS ACTUALLY NEEDED
|
||||
// Load NVRAM and place 'length ~length' at the end; end of chip RAM
|
||||
{
|
||||
{
|
||||
const uint32_t start = CYW_CHIP_RAM_SIZE - 4 - nvramlen;
|
||||
cyw_load_data(start, nvramdata, nvramlen); // nvramlen must be a multiple of 4
|
||||
// RAM_SIZE is a multiple of WINSZ, so the place for len ~len will be at the end of the window
|
||||
@@ -21835,7 +21835,7 @@ static bool cyw_sdio_init() {
|
||||
struct mg_tcpip_sdio *s = (struct mg_tcpip_sdio *) d->bus;
|
||||
uint32_t val = 0;
|
||||
if (!mg_sdio_init(s)) return false;
|
||||
// no block transfers on F0. if (!mg_sdio_set_blksz(s, CYW_SDIO_FUNC_BUS, 32)) return false;
|
||||
// no block transfers on F0. if (!mg_sdio_set_blksz(s, CYW_SDIO_FUNC_BUS, 32)) return false;
|
||||
if (!mg_sdio_set_blksz(s, CYW_SDIO_FUNC_CHIP, 64)) return false;
|
||||
if (!mg_sdio_set_blksz(s, CYW_SDIO_FUNC_WLAN, 64)) return false;
|
||||
// TODO(scaprile): we don't handle SDIO interrupts, study CCCR INTEN and SDIO support (SDIO 6.3, 8)
|
||||
@@ -22052,7 +22052,7 @@ static bool mg_tcpip_driver_imxrt_init(struct mg_tcpip_if *ifp) {
|
||||
int cr = (d == NULL || d->mdc_cr < 0) ? 24 : d->mdc_cr;
|
||||
ENET->MSCR = (1 << 8) | ((cr & 0x3f) << 1); // HOLDTIME 2 clks
|
||||
struct mg_phy phy = {enet_read_phy, enet_write_phy};
|
||||
mg_phy_init(&phy, d->phy_addr, MG_PHY_LEDS_ACTIVE_HIGH); // MAC clocks PHY
|
||||
mg_phy_init(&phy, d->phy_addr, MG_PHY_LEDS_ACTIVE_HIGH); // MAC clocks PHY
|
||||
// Select RMII mode, 100M, keep CRC, set max rx length, disable loop
|
||||
ENET->RCR = (1518 << 16) | MG_BIT(8) | MG_BIT(2);
|
||||
// ENET->RCR |= MG_BIT(3); // Receive all
|
||||
@@ -24551,13 +24551,13 @@ struct tms570_mdio {
|
||||
#define ETH_PKT_SIZE 1540 // Max frame size
|
||||
#define ETH_DESC_CNT 4 // Descriptors count
|
||||
#define ETH_DS 4 // Descriptor size (words)
|
||||
static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]
|
||||
static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]
|
||||
__attribute__((section(".ETH_CPPI"), aligned(4))); // TX descriptors
|
||||
static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]
|
||||
static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]
|
||||
__attribute__((section(".ETH_CPPI"), aligned(4))); // RX descriptors
|
||||
static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]
|
||||
static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]
|
||||
__attribute__((aligned(4))); // RX ethernet buffers
|
||||
static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]
|
||||
static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]
|
||||
__attribute__((aligned(4))); // TX ethernet buffers
|
||||
static struct mg_tcpip_if *s_ifp; // MIP interface
|
||||
static uint16_t emac_read_phy(uint8_t addr, uint8_t reg) {
|
||||
@@ -24638,7 +24638,7 @@ static bool mg_tcpip_driver_tms570_init(struct mg_tcpip_if *ifp) {
|
||||
}
|
||||
s_txdesc[ETH_DESC_CNT - 1][0] = 0;
|
||||
s_rxdesc[ETH_DESC_CNT - 1][0] = 0;
|
||||
|
||||
|
||||
EMAC->MACCONTROL = MG_BIT(5) | MG_BIT(0); // Enable MII, Full-duplex
|
||||
//EMAC->TXINTMASKSET = 1; // Enable TX interrupt
|
||||
EMAC->RXINTMASKSET = 1; // Enable RX interrupt
|
||||
@@ -24665,7 +24665,7 @@ static size_t mg_tcpip_driver_tms570_tx(const void *buf, size_t len,
|
||||
s_txdesc[s_txno][2] = SWAP32((uint32_t) len); // Set data len
|
||||
s_txdesc[s_txno][3] =
|
||||
SWAP32(MG_BIT(31) | MG_BIT(30) | MG_BIT(29) | len); // SOP, EOP, OWN, length
|
||||
|
||||
|
||||
while(EMAC->TXHDP[0] != 0) (void) 0;
|
||||
EMAC->TXHDP[0] = (uint32_t) &s_txdesc[s_txno][0];
|
||||
if(++s_txno == ETH_DESC_CNT) {
|
||||
|
||||
+757
-929
File diff suppressed because it is too large
Load Diff
+142
-142
@@ -1,158 +1,158 @@
|
||||
#include "terminal.h"
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pty.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <pty.h>
|
||||
#include <unistd.h>
|
||||
#include <utmp.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
|
||||
int create_terminal_session(struct terminal_session *session, const char *shell) {
|
||||
// Создаем pty
|
||||
int master, slave;
|
||||
char slave_name[64];
|
||||
|
||||
if (openpty(&master, &slave, slave_name, NULL, NULL) == -1) {
|
||||
perror("openpty failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) {
|
||||
perror("fork failed");
|
||||
close(master);
|
||||
close(slave);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
// Дочерний процесс
|
||||
close(master);
|
||||
|
||||
// Устанавливаем pty как контролирующий терминал
|
||||
setsid();
|
||||
ioctl(slave, TIOCSCTTY, 1);
|
||||
|
||||
// Перенаправляем стандартные потоки
|
||||
dup2(slave, STDIN_FILENO);
|
||||
dup2(slave, STDOUT_FILENO);
|
||||
dup2(slave, STDERR_FILENO);
|
||||
|
||||
if (slave > STDERR_FILENO) {
|
||||
close(slave);
|
||||
}
|
||||
|
||||
// Устанавливаем переменные окружения
|
||||
setenv("TERM", "xterm-256color", 1);
|
||||
setenv("COLORTERM", "truecolor", 1);
|
||||
|
||||
// Запускаем shell
|
||||
execlp(shell, shell, NULL);
|
||||
|
||||
// Если execlp вернулся, значит произошла ошибка
|
||||
perror("execlp failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Родительский процесс
|
||||
int create_terminal_session(struct terminal_session *session,
|
||||
const char *shell) {
|
||||
// Создаем pty
|
||||
int master, slave;
|
||||
char slave_name[64];
|
||||
|
||||
if (openpty(&master, &slave, slave_name, NULL, NULL) == -1) {
|
||||
perror("openpty failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) {
|
||||
perror("fork failed");
|
||||
close(master);
|
||||
close(slave);
|
||||
|
||||
session->master_pty = master;
|
||||
session->slave_pty = slave;
|
||||
session->child_pid = pid;
|
||||
session->active = true;
|
||||
|
||||
// Устанавливаем неблокирующий режим
|
||||
int flags = fcntl(master, F_GETFL, 0);
|
||||
fcntl(master, F_SETFL, flags | O_NONBLOCK);
|
||||
|
||||
printf("Created terminal session with PID %d\n", pid);
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
// Дочерний процесс
|
||||
close(master);
|
||||
|
||||
// Устанавливаем pty как контролирующий терминал
|
||||
setsid();
|
||||
ioctl(slave, TIOCSCTTY, 1);
|
||||
|
||||
// Перенаправляем стандартные потоки
|
||||
dup2(slave, STDIN_FILENO);
|
||||
dup2(slave, STDOUT_FILENO);
|
||||
dup2(slave, STDERR_FILENO);
|
||||
|
||||
if (slave > STDERR_FILENO) {
|
||||
close(slave);
|
||||
}
|
||||
|
||||
// Устанавливаем переменные окружения
|
||||
setenv("TERM", "xterm-256color", 1);
|
||||
setenv("COLORTERM", "truecolor", 1);
|
||||
|
||||
// Запускаем shell
|
||||
execlp(shell, shell, NULL);
|
||||
|
||||
// Если execlp вернулся, значит произошла ошибка
|
||||
perror("execlp failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Родительский процесс
|
||||
close(slave);
|
||||
|
||||
session->master_pty = master;
|
||||
session->slave_pty = slave;
|
||||
session->child_pid = pid;
|
||||
session->active = true;
|
||||
|
||||
// Устанавливаем неблокирующий режим
|
||||
int flags = fcntl(master, F_GETFL, 0);
|
||||
fcntl(master, F_SETFL, flags | O_NONBLOCK);
|
||||
|
||||
printf("Created terminal session with PID %d\n", pid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void destroy_terminal_session(struct terminal_session *session) {
|
||||
if (!session || !session->active) {
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Destroying terminal session with PID %d\n", session->child_pid);
|
||||
|
||||
// Завершаем дочерний процесс
|
||||
if (session->child_pid > 0) {
|
||||
// Отправляем SIGKILL
|
||||
int status;
|
||||
kill(session->child_pid, SIGKILL);
|
||||
waitpid(session->child_pid, &status, 0);
|
||||
}
|
||||
|
||||
// Закрываем pty
|
||||
if (session->master_pty != -1) {
|
||||
close(session->master_pty);
|
||||
session->master_pty = -1;
|
||||
}
|
||||
|
||||
session->active = false;
|
||||
session->child_pid = 0;
|
||||
printf("Terminal session destroyed\n");
|
||||
if (!session || !session->active) {
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Destroying terminal session with PID %d\n", session->child_pid);
|
||||
|
||||
// Завершаем дочерний процесс
|
||||
if (session->child_pid > 0) {
|
||||
// Отправляем SIGKILL
|
||||
int status;
|
||||
kill(session->child_pid, SIGKILL);
|
||||
waitpid(session->child_pid, &status, 0);
|
||||
}
|
||||
|
||||
// Закрываем pty
|
||||
if (session->master_pty != -1) {
|
||||
close(session->master_pty);
|
||||
session->master_pty = -1;
|
||||
}
|
||||
|
||||
session->active = false;
|
||||
session->child_pid = 0;
|
||||
printf("Terminal session destroyed\n");
|
||||
}
|
||||
|
||||
ssize_t read_from_terminal(struct terminal_session *session, char *buffer, size_t size) {
|
||||
if (!session || !session->active) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t bytes_read = read(session->master_pty, buffer, size - 1);
|
||||
if (bytes_read > 0) {
|
||||
buffer[bytes_read] = '\0';
|
||||
} else if (bytes_read == 0) {
|
||||
// EOF - соединение закрыто
|
||||
return 0;
|
||||
} else if (bytes_read < 0) {
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||
perror("read from terminal failed");
|
||||
return -1;
|
||||
}
|
||||
// Нет данных для чтения - это нормально в неблокирующем режиме
|
||||
return -2;
|
||||
}
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
ssize_t read_from_terminal(struct terminal_session *session, char *buffer,
|
||||
size_t size) {
|
||||
if (!session || !session->active) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t write_to_terminal(struct terminal_session *session, const char *data, size_t size) {
|
||||
if (!session || !session->active) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t bytes_written = write(session->master_pty, data, size);
|
||||
if (bytes_written < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||
perror("write to terminal failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
int resize_terminal(struct terminal_session *session, uint16_t rows, uint16_t cols) {
|
||||
if (!session || !session->active) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct winsize ws = {
|
||||
.ws_row = rows,
|
||||
.ws_col = cols,
|
||||
.ws_xpixel = 0,
|
||||
.ws_ypixel = 0
|
||||
};
|
||||
|
||||
if (ioctl(session->master_pty, TIOCSWINSZ, &ws) == -1) {
|
||||
perror("ioctl TIOCSWINSZ failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t bytes_read = read(session->master_pty, buffer, size - 1);
|
||||
if (bytes_read > 0) {
|
||||
buffer[bytes_read] = '\0';
|
||||
} else if (bytes_read == 0) {
|
||||
// EOF - соединение закрыто
|
||||
return 0;
|
||||
}
|
||||
} else if (bytes_read < 0) {
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||
perror("read from terminal failed");
|
||||
return -1;
|
||||
}
|
||||
// Нет данных для чтения - это нормально в неблокирующем режиме
|
||||
return -2;
|
||||
}
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
ssize_t write_to_terminal(struct terminal_session *session, const char *data,
|
||||
size_t size) {
|
||||
if (!session || !session->active) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t bytes_written = write(session->master_pty, data, size);
|
||||
if (bytes_written < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||
perror("write to terminal failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
int resize_terminal(struct terminal_session *session, uint16_t rows,
|
||||
uint16_t cols) {
|
||||
if (!session || !session->active) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct winsize ws = {
|
||||
.ws_row = rows, .ws_col = cols, .ws_xpixel = 0, .ws_ypixel = 0};
|
||||
|
||||
if (ioctl(session->master_pty, TIOCSWINSZ, &ws) == -1) {
|
||||
perror("ioctl TIOCSWINSZ failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+14
-10
@@ -1,25 +1,29 @@
|
||||
#ifndef TERMINAL_H
|
||||
#define TERMINAL_H
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <signal.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
struct terminal_session {
|
||||
int master_pty;
|
||||
int slave_pty;
|
||||
pid_t child_pid;
|
||||
bool active;
|
||||
int master_pty;
|
||||
int slave_pty;
|
||||
pid_t child_pid;
|
||||
bool active;
|
||||
};
|
||||
|
||||
// Функции для работы с терминалом
|
||||
int create_terminal_session(struct terminal_session *session, const char *shell);
|
||||
int create_terminal_session(struct terminal_session *session,
|
||||
const char *shell);
|
||||
void destroy_terminal_session(struct terminal_session *session);
|
||||
ssize_t read_from_terminal(struct terminal_session *session, char *buffer, size_t size);
|
||||
ssize_t write_to_terminal(struct terminal_session *session, const char *data, size_t size);
|
||||
int resize_terminal(struct terminal_session *session, uint16_t rows, uint16_t cols);
|
||||
ssize_t read_from_terminal(struct terminal_session *session, char *buffer,
|
||||
size_t size);
|
||||
ssize_t write_to_terminal(struct terminal_session *session, const char *data,
|
||||
size_t size);
|
||||
int resize_terminal(struct terminal_session *session, uint16_t rows,
|
||||
uint16_t cols);
|
||||
|
||||
#endif
|
||||
|
||||
+187
-168
@@ -1,176 +1,195 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Web Terminal</title>
|
||||
<link rel="stylesheet" href="xterm.css">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background-color: #1e1e1e;
|
||||
color: #fff;
|
||||
font-family: Arial, sans-serif;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#terminal-container {
|
||||
flex: 1;
|
||||
border: 1px solid #333;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.status {
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.connected { background-color: #2d5a2d; }
|
||||
.disconnected { background-color: #5a2d2d; }
|
||||
.connecting { background-color: #5a5a2d; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Web Terminal</h1>
|
||||
<div class="status connecting" id="status">Connecting...</div>
|
||||
<div id="terminal-container"></div>
|
||||
<head>
|
||||
<title>Web Terminal</title>
|
||||
<link rel="stylesheet" href="xterm.css" />
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background-color: #1e1e1e;
|
||||
color: #fff;
|
||||
font-family: Arial, sans-serif;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
<script src="xterm.js"></script>
|
||||
<script src="xterm-addon-fit.js"></script>
|
||||
<script>
|
||||
class WebTerminal {
|
||||
constructor() {
|
||||
this.terminal = new Terminal({
|
||||
cursorBlink: true,
|
||||
theme: {
|
||||
background: '#000000',
|
||||
foreground: '#ffffff',
|
||||
cursor: '#ffffff'
|
||||
}
|
||||
});
|
||||
|
||||
this.fitAddon = new FitAddon.FitAddon();
|
||||
this.terminal.loadAddon(this.fitAddon);
|
||||
|
||||
this.ws = null;
|
||||
this.reconnectAttempts = 0;
|
||||
this.maxReconnectAttempts = 5;
|
||||
this.reconnectDelay = 3000;
|
||||
|
||||
this.init();
|
||||
#terminal-container {
|
||||
flex: 1;
|
||||
border: 1px solid #333;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
init() {
|
||||
const terminalContainer = document.getElementById('terminal-container');
|
||||
this.terminal.open(terminalContainer);
|
||||
this.fitAddon.fit();
|
||||
|
||||
this.terminal.writeln('Welcome to Web Terminal');
|
||||
this.terminal.writeln('Connecting to server...');
|
||||
|
||||
this.connect();
|
||||
|
||||
// Handle terminal input
|
||||
this.terminal.onData((data) => {
|
||||
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
||||
this.ws.send(data);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle resize
|
||||
window.addEventListener('resize', () => {
|
||||
this.fitAddon.fit();
|
||||
this.sendResize();
|
||||
});
|
||||
|
||||
// Handle page unload
|
||||
window.addEventListener('beforeunload', () => {
|
||||
if (this.ws) {
|
||||
this.ws.close();
|
||||
}
|
||||
});
|
||||
|
||||
.status {
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
connect() {
|
||||
this.updateStatus('Connecting...', 'connecting');
|
||||
|
||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const wsUrl = `${protocol}//${window.location.host}/ws`;
|
||||
|
||||
this.ws = new WebSocket(wsUrl);
|
||||
|
||||
this.ws.onopen = () => {
|
||||
console.log('WebSocket connected');
|
||||
|
||||
.connected {
|
||||
background-color: #2d5a2d;
|
||||
}
|
||||
.disconnected {
|
||||
background-color: #5a2d2d;
|
||||
}
|
||||
.connecting {
|
||||
background-color: #5a5a2d;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Web Terminal</h1>
|
||||
<div class="status connecting" id="status">Connecting...</div>
|
||||
<div id="terminal-container"></div>
|
||||
|
||||
<script src="xterm.js"></script>
|
||||
<script src="xterm-addon-fit.js"></script>
|
||||
<script>
|
||||
class WebTerminal {
|
||||
constructor() {
|
||||
this.terminal = new Terminal({
|
||||
cursorBlink: true,
|
||||
theme: {
|
||||
background: "#000000",
|
||||
foreground: "#ffffff",
|
||||
cursor: "#ffffff",
|
||||
},
|
||||
});
|
||||
|
||||
this.fitAddon = new FitAddon.FitAddon();
|
||||
this.terminal.loadAddon(this.fitAddon);
|
||||
|
||||
this.ws = null;
|
||||
this.reconnectAttempts = 0;
|
||||
this.updateStatus('Connected', 'connected');
|
||||
this.terminal.clear();
|
||||
this.sendResize();
|
||||
};
|
||||
|
||||
this.ws.onmessage = (event) => {
|
||||
if (typeof event.data === 'string') {
|
||||
this.terminal.write(event.data);
|
||||
} else {
|
||||
// Handle binary data
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
const data = new Uint8Array(reader.result);
|
||||
this.terminal.write(data);
|
||||
};
|
||||
reader.readAsArrayBuffer(event.data);
|
||||
}
|
||||
};
|
||||
|
||||
this.ws.onclose = (event) => {
|
||||
console.log('WebSocket disconnected:', event.code, event.reason);
|
||||
this.updateStatus('Disconnected', 'disconnected');
|
||||
|
||||
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
||||
this.reconnectAttempts++;
|
||||
const delay = this.reconnectDelay * this.reconnectAttempts;
|
||||
this.terminal.write(`\r\nConnection lost. Reconnecting in ${delay/1000}s...\r\n`);
|
||||
|
||||
setTimeout(() => {
|
||||
this.connect();
|
||||
}, delay);
|
||||
} else {
|
||||
this.terminal.write('\r\nFailed to reconnect. Please refresh the page.\r\n');
|
||||
}
|
||||
};
|
||||
|
||||
this.ws.onerror = (error) => {
|
||||
console.error('WebSocket error:', error);
|
||||
this.updateStatus('Connection Error', 'disconnected');
|
||||
};
|
||||
}
|
||||
|
||||
sendResize() {
|
||||
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
||||
const dimensions = this.terminal;
|
||||
const resizeMsg = {
|
||||
cols: dimensions.cols,
|
||||
rows: dimensions.rows
|
||||
this.maxReconnectAttempts = 5;
|
||||
this.reconnectDelay = 3000;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
const terminalContainer =
|
||||
document.getElementById("terminal-container");
|
||||
this.terminal.open(terminalContainer);
|
||||
this.fitAddon.fit();
|
||||
|
||||
this.terminal.writeln("Welcome to Web Terminal");
|
||||
this.terminal.writeln("Connecting to server...");
|
||||
|
||||
this.connect();
|
||||
|
||||
// Handle terminal input
|
||||
this.terminal.onData((data) => {
|
||||
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
||||
this.ws.send(data);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle resize
|
||||
window.addEventListener("resize", () => {
|
||||
this.fitAddon.fit();
|
||||
this.sendResize();
|
||||
});
|
||||
|
||||
// Handle page unload
|
||||
window.addEventListener("beforeunload", () => {
|
||||
if (this.ws) {
|
||||
this.ws.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
connect() {
|
||||
this.updateStatus("Connecting...", "connecting");
|
||||
|
||||
const protocol =
|
||||
window.location.protocol === "https:" ? "wss:" : "ws:";
|
||||
const wsUrl = `${protocol}//${window.location.host}/ws`;
|
||||
|
||||
this.ws = new WebSocket(wsUrl);
|
||||
|
||||
this.ws.onopen = () => {
|
||||
console.log("WebSocket connected");
|
||||
this.reconnectAttempts = 0;
|
||||
this.updateStatus("Connected", "connected");
|
||||
this.terminal.clear();
|
||||
this.sendResize();
|
||||
};
|
||||
this.ws.send(JSON.stringify(resizeMsg));
|
||||
|
||||
this.ws.onmessage = (event) => {
|
||||
if (typeof event.data === "string") {
|
||||
this.terminal.write(event.data);
|
||||
} else {
|
||||
// Handle binary data
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
const data = new Uint8Array(reader.result);
|
||||
this.terminal.write(data);
|
||||
};
|
||||
reader.readAsArrayBuffer(event.data);
|
||||
}
|
||||
};
|
||||
|
||||
this.ws.onclose = (event) => {
|
||||
console.log(
|
||||
"WebSocket disconnected:",
|
||||
event.code,
|
||||
event.reason,
|
||||
);
|
||||
this.updateStatus("Disconnected", "disconnected");
|
||||
|
||||
if (
|
||||
this.reconnectAttempts < this.maxReconnectAttempts
|
||||
) {
|
||||
this.reconnectAttempts++;
|
||||
const delay =
|
||||
this.reconnectDelay * this.reconnectAttempts;
|
||||
this.terminal.write(
|
||||
`\r\nConnection lost. Reconnecting in ${delay / 1000}s...\r\n`,
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
this.connect();
|
||||
}, delay);
|
||||
} else {
|
||||
this.terminal.write(
|
||||
"\r\nFailed to reconnect. Please refresh the page.\r\n",
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
this.ws.onerror = (error) => {
|
||||
console.error("WebSocket error:", error);
|
||||
this.updateStatus("Connection Error", "disconnected");
|
||||
};
|
||||
}
|
||||
|
||||
sendResize() {
|
||||
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
||||
const dimensions = this.terminal;
|
||||
const resizeMsg = {
|
||||
cols: dimensions.cols,
|
||||
rows: dimensions.rows,
|
||||
};
|
||||
this.ws.send(JSON.stringify(resizeMsg));
|
||||
}
|
||||
}
|
||||
|
||||
updateStatus(text, className) {
|
||||
const status = document.getElementById("status");
|
||||
status.textContent = text;
|
||||
status.className = `status ${className}`;
|
||||
}
|
||||
}
|
||||
|
||||
updateStatus(text, className) {
|
||||
const status = document.getElementById('status');
|
||||
status.textContent = text;
|
||||
status.className = `status ${className}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize terminal when page loads
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
new WebTerminal();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
// Initialize terminal when page loads
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
new WebTerminal();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,2 +1,73 @@
|
||||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(self,(()=>(()=>{"use strict";var e={};return(()=>{var t=e;Object.defineProperty(t,"__esModule",{value:!0}),t.FitAddon=void 0,t.FitAddon=class{activate(e){this._terminal=e}dispose(){}fit(){const e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;const t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal)return;if(!this._terminal.element||!this._terminal.element.parentElement)return;const e=this._terminal._core,t=e._renderService.dimensions;if(0===t.css.cell.width||0===t.css.cell.height)return;const r=0===this._terminal.options.scrollback?0:e.viewport.scrollBarWidth,i=window.getComputedStyle(this._terminal.element.parentElement),o=parseInt(i.getPropertyValue("height")),s=Math.max(0,parseInt(i.getPropertyValue("width"))),n=window.getComputedStyle(this._terminal.element),l=o-(parseInt(n.getPropertyValue("padding-top"))+parseInt(n.getPropertyValue("padding-bottom"))),a=s-(parseInt(n.getPropertyValue("padding-right"))+parseInt(n.getPropertyValue("padding-left")))-r;return{cols:Math.max(2,Math.floor(a/t.css.cell.width)),rows:Math.max(1,Math.floor(l/t.css.cell.height))}}}})(),e})()));
|
||||
//# sourceMappingURL=xterm-addon-fit.js.map
|
||||
!(function (e, t) {
|
||||
"object" == typeof exports && "object" == typeof module
|
||||
? (module.exports = t())
|
||||
: "function" == typeof define && define.amd
|
||||
? define([], t)
|
||||
: "object" == typeof exports
|
||||
? (exports.FitAddon = t())
|
||||
: (e.FitAddon = t());
|
||||
})(self, () =>
|
||||
(() => {
|
||||
"use strict";
|
||||
var e = {};
|
||||
return (
|
||||
(() => {
|
||||
var t = e;
|
||||
(Object.defineProperty(t, "__esModule", { value: !0 }),
|
||||
(t.FitAddon = void 0),
|
||||
(t.FitAddon = class {
|
||||
activate(e) {
|
||||
this._terminal = e;
|
||||
}
|
||||
dispose() {}
|
||||
fit() {
|
||||
const e = this.proposeDimensions();
|
||||
if (!e || !this._terminal || isNaN(e.cols) || isNaN(e.rows))
|
||||
return;
|
||||
const t = this._terminal._core;
|
||||
(this._terminal.rows === e.rows &&
|
||||
this._terminal.cols === e.cols) ||
|
||||
(t._renderService.clear(),
|
||||
this._terminal.resize(e.cols, e.rows));
|
||||
}
|
||||
proposeDimensions() {
|
||||
if (!this._terminal) return;
|
||||
if (
|
||||
!this._terminal.element ||
|
||||
!this._terminal.element.parentElement
|
||||
)
|
||||
return;
|
||||
const e = this._terminal._core,
|
||||
t = e._renderService.dimensions;
|
||||
if (0 === t.css.cell.width || 0 === t.css.cell.height) return;
|
||||
const r =
|
||||
0 === this._terminal.options.scrollback
|
||||
? 0
|
||||
: e.viewport.scrollBarWidth,
|
||||
i = window.getComputedStyle(
|
||||
this._terminal.element.parentElement,
|
||||
),
|
||||
o = parseInt(i.getPropertyValue("height")),
|
||||
s = Math.max(0, parseInt(i.getPropertyValue("width"))),
|
||||
n = window.getComputedStyle(this._terminal.element),
|
||||
l =
|
||||
o -
|
||||
(parseInt(n.getPropertyValue("padding-top")) +
|
||||
parseInt(n.getPropertyValue("padding-bottom"))),
|
||||
a =
|
||||
s -
|
||||
(parseInt(n.getPropertyValue("padding-right")) +
|
||||
parseInt(n.getPropertyValue("padding-left"))) -
|
||||
r;
|
||||
return {
|
||||
cols: Math.max(2, Math.floor(a / t.css.cell.width)),
|
||||
rows: Math.max(1, Math.floor(l / t.css.cell.height)),
|
||||
};
|
||||
}
|
||||
}));
|
||||
})(),
|
||||
e
|
||||
);
|
||||
})(),
|
||||
);
|
||||
//# sourceMappingURL=xterm-addon-fit.js.map
|
||||
|
||||
+37
-15
@@ -79,7 +79,7 @@
|
||||
.xterm .composition-view {
|
||||
/* TODO: Composition position got messed up somewhere */
|
||||
background: #000;
|
||||
color: #FFF;
|
||||
color: #fff;
|
||||
display: none;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
@@ -166,33 +166,55 @@
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
.xterm-underline-1 { text-decoration: underline; }
|
||||
.xterm-underline-2 { text-decoration: double underline; }
|
||||
.xterm-underline-3 { text-decoration: wavy underline; }
|
||||
.xterm-underline-4 { text-decoration: dotted underline; }
|
||||
.xterm-underline-5 { text-decoration: dashed underline; }
|
||||
.xterm-underline-1 {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.xterm-underline-2 {
|
||||
text-decoration: double underline;
|
||||
}
|
||||
.xterm-underline-3 {
|
||||
text-decoration: wavy underline;
|
||||
}
|
||||
.xterm-underline-4 {
|
||||
text-decoration: dotted underline;
|
||||
}
|
||||
.xterm-underline-5 {
|
||||
text-decoration: dashed underline;
|
||||
}
|
||||
|
||||
.xterm-overline {
|
||||
text-decoration: overline;
|
||||
}
|
||||
|
||||
.xterm-overline.xterm-underline-1 { text-decoration: overline underline; }
|
||||
.xterm-overline.xterm-underline-2 { text-decoration: overline double underline; }
|
||||
.xterm-overline.xterm-underline-3 { text-decoration: overline wavy underline; }
|
||||
.xterm-overline.xterm-underline-4 { text-decoration: overline dotted underline; }
|
||||
.xterm-overline.xterm-underline-5 { text-decoration: overline dashed underline; }
|
||||
.xterm-overline.xterm-underline-1 {
|
||||
text-decoration: overline underline;
|
||||
}
|
||||
.xterm-overline.xterm-underline-2 {
|
||||
text-decoration: overline double underline;
|
||||
}
|
||||
.xterm-overline.xterm-underline-3 {
|
||||
text-decoration: overline wavy underline;
|
||||
}
|
||||
.xterm-overline.xterm-underline-4 {
|
||||
text-decoration: overline dotted underline;
|
||||
}
|
||||
.xterm-overline.xterm-underline-5 {
|
||||
text-decoration: overline dashed underline;
|
||||
}
|
||||
|
||||
.xterm-strikethrough {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.xterm-screen .xterm-decoration-container .xterm-decoration {
|
||||
z-index: 6;
|
||||
position: absolute;
|
||||
z-index: 6;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer {
|
||||
z-index: 7;
|
||||
.xterm-screen
|
||||
.xterm-decoration-container
|
||||
.xterm-decoration.xterm-decoration-top-layer {
|
||||
z-index: 7;
|
||||
}
|
||||
|
||||
.xterm-decoration-overview-ruler {
|
||||
|
||||
+14698
-2
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user