From fa948f6245dec5b6ca048a2f2c24475b5836d6e8 Mon Sep 17 00:00:00 2001 From: xiaojin Date: Thu, 15 Jul 2021 10:50:08 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=B3=20chore(=E5=B7=A5=E5=85=B7):=20?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=20=E7=BC=96=E8=AF=91=20=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + .gitmodules | 3 + framework/3rd/dmon/.clang-format | 38 - framework/3rd/dmon/.gitignore | 72 - framework/3rd/dmon/README.md | 75 - framework/3rd/dmon/dmon.h | 1502 -------------- framework/3rd/dmon/test.c | 40 - framework/3rd/greatest/greatest.h | 1266 ------------ framework/3rd/minicoro/minicoro.h | 1789 ----------------- framework/3rd/termbox | 1 + framework/3rd/termbox/.gitignore | 7 - framework/3rd/termbox/makefile | 40 - framework/3rd/termbox/readme.md | 57 - framework/3rd/termbox/src/demo/keyboard.c | 827 -------- framework/3rd/termbox/src/demo/makefile | 30 - framework/3rd/termbox/src/demo/output.c | 156 -- framework/3rd/termbox/src/demo/paint.c | 183 -- framework/3rd/termbox/src/demo/truecolor.c | 69 - framework/3rd/termbox/src/input.c | 319 --- framework/3rd/termbox/src/memstream.c | 36 - framework/3rd/termbox/src/memstream.h | 20 - framework/3rd/termbox/src/ringbuffer.c | 195 -- framework/3rd/termbox/src/ringbuffer.h | 26 - framework/3rd/termbox/src/term.c | 412 ---- framework/3rd/termbox/src/term.h | 38 - framework/3rd/termbox/src/termbox.c | 885 -------- framework/3rd/termbox/src/utf8.c | 106 - framework/3rd/termbox/tools/astylerc | 27 - .../3rd/termbox/tools/collect_terminfo.py | 108 - framework/lualib-src/Makefile | 12 +- .../{lua-zset => lua-skiplist}/Makefile | 0 .../{lua-zset => lua-skiplist}/lua-skiplist.c | 112 +- .../{lua-zset => lua-skiplist}/skiplist.c | 132 +- .../{lua-zset => lua-skiplist}/skiplist.h | 9 +- framework/lualib-src/lua-skipset/Makefile | 30 + .../{lua-zset => lua-skipset}/lua-skipset.c | 176 +- .../src => lualib-src/lua-termfx}/termbox.h | 0 framework/lualib-src/testluaclib.lua | 6 + framework/skynet | 2 +- 39 files changed, 336 insertions(+), 8471 deletions(-) delete mode 100755 framework/3rd/dmon/.clang-format delete mode 100755 framework/3rd/dmon/.gitignore delete mode 100755 framework/3rd/dmon/README.md delete mode 100755 framework/3rd/dmon/dmon.h delete mode 100755 framework/3rd/dmon/test.c delete mode 100644 framework/3rd/greatest/greatest.h delete mode 100644 framework/3rd/minicoro/minicoro.h create mode 160000 framework/3rd/termbox delete mode 100755 framework/3rd/termbox/.gitignore delete mode 100755 framework/3rd/termbox/makefile delete mode 100755 framework/3rd/termbox/readme.md delete mode 100755 framework/3rd/termbox/src/demo/keyboard.c delete mode 100755 framework/3rd/termbox/src/demo/makefile delete mode 100755 framework/3rd/termbox/src/demo/output.c delete mode 100755 framework/3rd/termbox/src/demo/paint.c delete mode 100755 framework/3rd/termbox/src/demo/truecolor.c delete mode 100755 framework/3rd/termbox/src/input.c delete mode 100755 framework/3rd/termbox/src/memstream.c delete mode 100755 framework/3rd/termbox/src/memstream.h delete mode 100755 framework/3rd/termbox/src/ringbuffer.c delete mode 100755 framework/3rd/termbox/src/ringbuffer.h delete mode 100755 framework/3rd/termbox/src/term.c delete mode 100755 framework/3rd/termbox/src/term.h delete mode 100755 framework/3rd/termbox/src/termbox.c delete mode 100755 framework/3rd/termbox/src/utf8.c delete mode 100755 framework/3rd/termbox/tools/astylerc delete mode 100755 framework/3rd/termbox/tools/collect_terminfo.py rename framework/lualib-src/{lua-zset => lua-skiplist}/Makefile (100%) rename framework/lualib-src/{lua-zset => lua-skiplist}/lua-skiplist.c (77%) rename framework/lualib-src/{lua-zset => lua-skiplist}/skiplist.c (81%) rename framework/lualib-src/{lua-zset => lua-skiplist}/skiplist.h (89%) create mode 100644 framework/lualib-src/lua-skipset/Makefile rename framework/lualib-src/{lua-zset => lua-skipset}/lua-skipset.c (75%) rename framework/{3rd/termbox/src => lualib-src/lua-termfx}/termbox.h (100%) mode change 100755 => 100644 diff --git a/.gitignore b/.gitignore index f9f032e..1c88493 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *.pid *.o *.so +*.a *.lib *.tar.gz *.log diff --git a/.gitmodules b/.gitmodules index 6cc4605..044283d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "framework/3rd/moon"] path = framework/3rd/moon url = https://github.com/siffiejoe/lua-moon.git +[submodule "framework/3rd/termbox"] + path = framework/3rd/termbox + url = https://github.com/nullgemm/termbox_next.git diff --git a/framework/3rd/dmon/.clang-format b/framework/3rd/dmon/.clang-format deleted file mode 100755 index 61a73e2..0000000 --- a/framework/3rd/dmon/.clang-format +++ /dev/null @@ -1,38 +0,0 @@ ---- -AlignAfterOpenBracket: Align -AlignConsecutiveAssignments: 'false' -AlignConsecutiveDeclarations: 'false' -AlignEscapedNewlines: Left -AlignTrailingComments: 'true' -AllowAllParametersOfDeclarationOnNextLine: 'true' -AllowShortBlocksOnASingleLine: 'false' -AllowShortCaseLabelsOnASingleLine: 'false' -AllowShortFunctionsOnASingleLine: 'false' -AllowShortIfStatementsOnASingleLine: 'false' -AllowShortLoopsOnASingleLine: 'false' -AlwaysBreakBeforeMultilineStrings: 'true' -AlwaysBreakTemplateDeclarations: 'true' -BreakBeforeBinaryOperators: None -BreakBeforeBraces: Linux -Cpp11BracedListStyle: 'false' -IncludeBlocks: Preserve -IndentCaseLabels: 'false' -IndentPPDirectives: AfterHash -IndentWidth: '4' -IndentWrappedFunctionNames: 'true' -Language: Cpp -MaxEmptyLinesToKeep: '2' -NamespaceIndentation: Inner -PointerAlignment: Left -SpaceBeforeAssignmentOperators: 'true' -SpaceBeforeParens: ControlStatements -SpaceInEmptyParentheses: 'false' -SpacesBeforeTrailingComments: '4' -SpacesInCStyleCastParentheses: 'false' -SpacesInContainerLiterals: 'false' -SpacesInParentheses: 'false' -Standard: Cpp11 -TabWidth: '4' -UseTab: Never - -... diff --git a/framework/3rd/dmon/.gitignore b/framework/3rd/dmon/.gitignore deleted file mode 100755 index 0308f50..0000000 --- a/framework/3rd/dmon/.gitignore +++ /dev/null @@ -1,72 +0,0 @@ -# External junk -.DS_Store -_ReSharper* -*.opensdf -*.sdf -*.dir -*.suo -*.user -*.bak -Win32 -Win64 -Debug -Release -Profile -Development -Obj -Bin -Lib -.tags -.tags_sorted_by_file -*.lnk -ipch -__pycache__ -Thumbs.db -.build* - -# Gradle -.gradle -/local.properties -/.idea/workspace.xml -/.idea/libraries -/build -/build-* -/captures - -# assets -assets/shaders/* - -# Generated files -bin -docs/Doxyfile -docs/html -docs/warnings.txt -imgui.ini -install -*.log -.vscode/launch.json -.vscode/c_cpp_properties.json -shaders_h -src/rizz/plugin_bundle.h -src/rizz/plugin_bundle_native.h -android -assets-db.json -compile_commands.json -examples/assets/shaders/** - -# temp -scripts/generators/fake_libc_include -temp - -# Compiled binaries -*.so -*.dylib -*.dll -*.a -*.lib -*.exe -.build -*.fso -*.vso -*.pyc -*.obj diff --git a/framework/3rd/dmon/README.md b/framework/3rd/dmon/README.md deleted file mode 100755 index 55465de..0000000 --- a/framework/3rd/dmon/README.md +++ /dev/null @@ -1,75 +0,0 @@ -## dmon -[@septag](https://twitter.com/septagh) - -_dmon_ is a tiny C library that monitors changes in a directory. -It provides a unified solution to multiple system APIs that exist for each OS. It can also monitor directories recursively. - -### Platforms -- Windows: `ReadDirectoryChangesW` backend. Tested with Windows10 SDK + Visual Studio 2019 -- Linux: `inotify` backend. Tested with gcc-7.4/clang-6, ubuntu 18.04 LTS -- MacOS: `FSEvents` backend. Tested with MacOS-10.14 clang 10 - -### Usage - -You just have to include the file and use it's functions. It is also compatible with C++ code. -Backslashes in Windows paths are also converted to '/' for portability. - -```c -#define DMON_IMPL -#include "dmon.h" - -static void watch_callback(dmon_watch_id watch_id, dmon_action action, const char* rootdir, - const char* filepath, const char* oldfilepath, void* user) -{ - // receive change events. type of event is stored in 'action' variable -} - -int main() -{ - dmon_init(); - dmon_watch("/path/to/directory", watch_callback, DMON_WATCHFLAGS_RECURSIVE, NULL); - // wait ... - dmon_deinit(); - return 0; -} -``` - -For more information and how to customize functionality, see [dmon.h](dmon.h) - -To build on linux, link with `pthread`: -```gcc test.c -lpthread -o test``` - -To build on MacOS, link with `CoreServices` and `CoreFoundation`: -```clang test.c -framework CoreFoundation -framework CoreServices -lpthread -o test``` - -[License (BSD 2-clause)](https://github.com/septag/dmon/blob/master/LICENSE) --------------------------------------------------------------------------- - - - - - - Copyright 2019 Sepehr Taghdisian. All rights reserved. - - https://github.com/septag/dmon - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/framework/3rd/dmon/dmon.h b/framework/3rd/dmon/dmon.h deleted file mode 100755 index 54928bc..0000000 --- a/framework/3rd/dmon/dmon.h +++ /dev/null @@ -1,1502 +0,0 @@ -// -// Copyright 2021 Sepehr Taghdisian (septag@github). All rights reserved. -// License: https://github.com/septag/dmon#license-bsd-2-clause -// -// Portable directory monitoring library -// watches directories for file or directory changes. -// -// clang-format off -// Usage: -// define DMON_IMPL and include this file to use it: -// #define DMON_IMPL -// #include "dmon.h" -// -// dmon_init(): -// Call this once at the start of your program. -// This will start a low-priority monitoring thread -// dmon_deinit(): -// Call this when your work with dmon is finished, usually on program terminate -// This will free resources and stop the monitoring thread -// dmon_watch: -// Watch for directories -// You can watch multiple directories by calling this function multiple times -// rootdir: root directory to monitor -// watch_cb: callback function to receive events. -// NOTE that this function is called from another thread, so you should -// beware of data races in your application when accessing data within this -// callback -// flags: watch flags, see dmon_watch_flags_t -// user_data: user pointer that is passed to callback function -// Returns the Id of the watched directory after successful call, or returns Id=0 if error -// dmon_unwatch: -// Remove the directory from watch list -// -// see test.c for the basic example -// -// Configuration: -// You can customize some low-level functionality like malloc and logging by overriding macros: -// -// DMON_MALLOC, DMON_FREE, DMON_REALLOC: -// define these macros to override memory allocations -// default is 'malloc', 'free' and 'realloc' -// DMON_ASSERT: -// define this to provide your own assert -// default is 'assert' -// DMON_LOG_ERROR: -// define this to provide your own logging mechanism -// default implementation logs to stdout and breaks the program -// DMON_LOG_DEBUG -// define this to provide your own extra debug logging mechanism -// default implementation logs to stdout in DEBUG and does nothing in other builds -// DMON_API_DECL, DMON_API_IMPL -// define these to provide your own API declerations. (for example: static) -// default is nothing (which is extern in C language ) -// DMON_MAX_PATH -// Maximum size of path characters -// default is 260 characters -// DMON_MAX_WATCHES -// Maximum number of watch directories -// default is 64 -// -// TODO: -// - DMON_WATCHFLAGS_FOLLOW_SYMLINKS does not resolve files -// - implement DMON_WATCHFLAGS_OUTOFSCOPE_LINKS -// - implement DMON_WATCHFLAGS_IGNORE_DIRECTORIES -// -// History: -// 1.0.0 First version. working Win32/Linux backends -// 1.1.0 MacOS backend -// 1.1.1 Minor fixes, eliminate gcc/clang warnings with -Wall -// 1.1.2 Eliminate some win32 dead code -// 1.1.3 Fixed select not resetting causing high cpu usage on linux -// -#ifndef __DMON_H__ -#define __DMON_H__ - -#include -#include - -#ifndef DMON_API_DECL -# define DMON_API_DECL -#endif - -#ifndef DMON_API_IMPL -# define DMON_API_IMPL -#endif - -typedef struct { uint32_t id; } dmon_watch_id; - -// Pass these flags to `dmon_watch` -typedef enum dmon_watch_flags_t { - DMON_WATCHFLAGS_RECURSIVE = 0x1, // monitor all child directories - DMON_WATCHFLAGS_FOLLOW_SYMLINKS = 0x2, // resolve symlinks (linux only) - DMON_WATCHFLAGS_OUTOFSCOPE_LINKS = 0x4, // TODO: not implemented yet - DMON_WATCHFLAGS_IGNORE_DIRECTORIES = 0x8 // TODO: not implemented yet -} dmon_watch_flags; - -// Action is what operation performed on the file. this value is provided by watch callback -typedef enum dmon_action_t { - DMON_ACTION_CREATE = 1, - DMON_ACTION_DELETE, - DMON_ACTION_MODIFY, - DMON_ACTION_MOVE -} dmon_action; - -#ifdef __cplusplus -extern "C" { -#endif - -DMON_API_DECL void dmon_init(void); -DMON_API_DECL void dmon_deinit(void); - -DMON_API_DECL dmon_watch_id dmon_watch(const char* rootdir, - void (*watch_cb)(dmon_watch_id watch_id, dmon_action action, - const char* rootdir, const char* filepath, - const char* oldfilepath, void* user), - uint32_t flags, void* user_data); -DMON_API_DECL void dmon_unwatch(dmon_watch_id id); - -#ifdef __cplusplus -} -#endif - -#ifdef DMON_IMPL - -#define DMON_OS_WINDOWS 0 -#define DMON_OS_MACOS 0 -#define DMON_OS_LINUX 0 - -#if defined(_WIN32) || defined(_WIN64) -# undef DMON_OS_WINDOWS -# define DMON_OS_WINDOWS 1 -#elif defined(__linux__) -# undef DMON_OS_LINUX -# define DMON_OS_LINUX 1 -#elif defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) -# undef DMON_OS_MACOS -# define DMON_OS_MACOS __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ -#else -# define DMON_OS 0 -# error "unsupported platform" -#endif - -#if DMON_OS_WINDOWS -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif -# ifndef NOMINMAX -# define NOMINMAX -# endif -# include -# include -# ifdef _MSC_VER -# pragma intrinsic(_InterlockedExchange) -# endif -#elif DMON_OS_LINUX -# ifndef __USE_MISC -# define __USE_MISC -# endif -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -#elif DMON_OS_MACOS -# include -# include -# include -# include -# include -#endif - -#ifndef DMON_MALLOC -# include -# define DMON_MALLOC(size) malloc(size) -# define DMON_FREE(ptr) free(ptr) -# define DMON_REALLOC(ptr, size) realloc(ptr, size) -#endif - -#ifndef DMON_ASSERT -# include -# define DMON_ASSERT(e) assert(e) -#endif - -#ifndef DMON_LOG_ERROR -# include -# define DMON_LOG_ERROR(s) do { puts(s); DMON_ASSERT(0); } while(0) -#endif - -#ifndef DMON_LOG_DEBUG -# ifndef NDEBUG -# include -# define DMON_LOG_DEBUG(s) do { puts(s); } while(0) -# else -# define DMON_LOG_DEBUG(s) -# endif -#endif - -#ifndef DMON_MAX_WATCHES -# define DMON_MAX_WATCHES 64 -#endif - -#ifndef DMON_MAX_PATH -# define DMON_MAX_PATH 260 -#endif - -#define _DMON_UNUSED(x) (void)(x) - -#ifndef _DMON_PRIVATE -# if defined(__GNUC__) || defined(__clang__) -# define _DMON_PRIVATE __attribute__((unused)) static -# else -# define _DMON_PRIVATE static -# endif -#endif - -#include - -#ifndef _DMON_LOG_ERRORF -# define _DMON_LOG_ERRORF(str, ...) do { char msg[512]; snprintf(msg, sizeof(msg), str, __VA_ARGS__); DMON_LOG_ERROR(msg); } while(0); -#endif - -#ifndef _DMON_LOG_DEBUGF -# define _DMON_LOG_DEBUGF(str, ...) do { char msg[512]; snprintf(msg, sizeof(msg), str, __VA_ARGS__); DMON_LOG_DEBUG(msg); } while(0); -#endif - -#ifndef dmon__min -# define dmon__min(a, b) ((a) < (b) ? (a) : (b)) -#endif - -#ifndef dmon__max -# define dmon__max(a, b) ((a) > (b) ? (a) : (b)) -#endif - -#ifndef dmon__swap -# define dmon__swap(a, b, _type) \ - do { \ - _type tmp = a; \ - a = b; \ - b = tmp; \ - } while (0) -#endif - -#ifndef dmon__make_id -# ifdef __cplusplus -# define dmon__make_id(id) {id} -# else -# define dmon__make_id(id) (dmon_watch_id) {id} -# endif -#endif // dmon__make_id - -_DMON_PRIVATE bool dmon__isrange(char ch, char from, char to) -{ - return (uint8_t)(ch - from) <= (uint8_t)(to - from); -} - -_DMON_PRIVATE bool dmon__isupperchar(char ch) -{ - return dmon__isrange(ch, 'A', 'Z'); -} - -_DMON_PRIVATE char dmon__tolowerchar(char ch) -{ - return ch + (dmon__isupperchar(ch) ? 0x20 : 0); -} - -_DMON_PRIVATE char* dmon__tolower(char* dst, int dst_sz, const char* str) -{ - int offset = 0; - int dst_max = dst_sz - 1; - while (*str && offset < dst_max) { - dst[offset++] = dmon__tolowerchar(*str); - ++str; - } - dst[offset] = '\0'; - return dst; -} - -_DMON_PRIVATE char* dmon__strcpy(char* dst, int dst_sz, const char* src) -{ - DMON_ASSERT(dst); - DMON_ASSERT(src); - - const int32_t len = (int32_t)strlen(src); - const int32_t _max = dst_sz - 1; - const int32_t num = (len < _max ? len : _max); - memcpy(dst, src, num); - dst[num] = '\0'; - - return dst; -} - -_DMON_PRIVATE char* dmon__unixpath(char* dst, int size, const char* path) -{ - size_t len = strlen(path); - len = dmon__min(len, (size_t)size - 1); - - for (size_t i = 0; i < len; i++) { - if (path[i] != '\\') - dst[i] = path[i]; - else - dst[i] = '/'; - } - dst[len] = '\0'; - return dst; -} - -#if DMON_OS_LINUX || DMON_OS_MACOS -_DMON_PRIVATE char* dmon__strcat(char* dst, int dst_sz, const char* src) -{ - int len = (int)strlen(dst); - return dmon__strcpy(dst + len, dst_sz - len, src); -} -#endif // DMON_OS_LINUX || DMON_OS_MACOS - -// stretchy buffer: https://github.com/nothings/stb/blob/master/stretchy_buffer.h -#define stb_sb_free(a) ((a) ? DMON_FREE(stb__sbraw(a)),0 : 0) -#define stb_sb_push(a,v) (stb__sbmaybegrow(a,1), (a)[stb__sbn(a)++] = (v)) -#define stb_sb_count(a) ((a) ? stb__sbn(a) : 0) -#define stb_sb_add(a,n) (stb__sbmaybegrow(a,n), stb__sbn(a)+=(n), &(a)[stb__sbn(a)-(n)]) -#define stb_sb_last(a) ((a)[stb__sbn(a)-1]) -#define stb_sb_reset(a) ((a) ? (stb__sbn(a) = 0) : 0) - -#define stb__sbraw(a) ((int *) (a) - 2) -#define stb__sbm(a) stb__sbraw(a)[0] -#define stb__sbn(a) stb__sbraw(a)[1] - -#define stb__sbneedgrow(a,n) ((a)==0 || stb__sbn(a)+(n) >= stb__sbm(a)) -#define stb__sbmaybegrow(a,n) (stb__sbneedgrow(a,(n)) ? stb__sbgrow(a,n) : 0) -#define stb__sbgrow(a,n) (*((void **)&(a)) = stb__sbgrowf((a), (n), sizeof(*(a)))) - -static void * stb__sbgrowf(void *arr, int increment, int itemsize) -{ - int dbl_cur = arr ? 2*stb__sbm(arr) : 0; - int min_needed = stb_sb_count(arr) + increment; - int m = dbl_cur > min_needed ? dbl_cur : min_needed; - int *p = (int *) DMON_REALLOC(arr ? stb__sbraw(arr) : 0, itemsize * m + sizeof(int)*2); - if (p) { - if (!arr) - p[1] = 0; - p[0] = m; - return p+2; - } else { - return (void *) (2*sizeof(int)); // try to force a NULL pointer exception later - } -} - -// watcher callback (same as dmon.h's decleration) -typedef void (dmon__watch_cb)(dmon_watch_id, dmon_action, const char*, const char*, const char*, void*); - -#if DMON_OS_WINDOWS -// IOCP (windows) -#ifdef UNICODE -# define _DMON_WINAPI_STR(name, size) wchar_t _##name[size]; MultiByteToWideChar(CP_UTF8, 0, name, -1, _##name, size) -#else -# define _DMON_WINAPI_STR(name, size) const char* _##name = name -#endif - -typedef struct dmon__win32_event { - char filepath[DMON_MAX_PATH]; - DWORD action; - dmon_watch_id watch_id; - bool skip; -} dmon__win32_event; - -typedef struct dmon__watch_state { - dmon_watch_id id; - OVERLAPPED overlapped; - HANDLE dir_handle; - uint8_t buffer[64512]; // http://msdn.microsoft.com/en-us/library/windows/desktop/aa365465(v=vs.85).aspx - DWORD notify_filter; - dmon__watch_cb* watch_cb; - uint32_t watch_flags; - void* user_data; - char rootdir[DMON_MAX_PATH]; - char old_filepath[DMON_MAX_PATH]; -} dmon__watch_state; - -typedef struct dmon__state { - int num_watches; - dmon__watch_state watches[DMON_MAX_WATCHES]; - HANDLE thread_handle; - CRITICAL_SECTION mutex; - volatile LONG modify_watches; - dmon__win32_event* events; - bool quit; -} dmon__state; - -static bool _dmon_init; -static dmon__state _dmon; - -// clang-format on - -_DMON_PRIVATE bool dmon__refresh_watch(dmon__watch_state* watch) -{ - return ReadDirectoryChangesW(watch->dir_handle, watch->buffer, sizeof(watch->buffer), - (watch->watch_flags & DMON_WATCHFLAGS_RECURSIVE) ? TRUE : FALSE, - watch->notify_filter, NULL, &watch->overlapped, NULL) != 0; -} - -_DMON_PRIVATE void dmon__unwatch(dmon__watch_state* watch) -{ - CancelIo(watch->dir_handle); - CloseHandle(watch->overlapped.hEvent); - CloseHandle(watch->dir_handle); - memset(watch, 0x0, sizeof(dmon__watch_state)); -} - -_DMON_PRIVATE void dmon__win32_process_events(void) -{ - for (int i = 0, c = stb_sb_count(_dmon.events); i < c; i++) { - dmon__win32_event* ev = &_dmon.events[i]; - if (ev->skip) { - continue; - } - - if (ev->action == FILE_ACTION_MODIFIED || ev->action == FILE_ACTION_ADDED) { - // remove duplicate modifies on a single file - for (int j = i + 1; j < c; j++) { - dmon__win32_event* check_ev = &_dmon.events[j]; - if (check_ev->action == FILE_ACTION_MODIFIED && - strcmp(ev->filepath, check_ev->filepath) == 0) { - check_ev->skip = true; - } - } - } - } - - // trigger user callbacks - for (int i = 0, c = stb_sb_count(_dmon.events); i < c; i++) { - dmon__win32_event* ev = &_dmon.events[i]; - if (ev->skip) { - continue; - } - dmon__watch_state* watch = &_dmon.watches[ev->watch_id.id - 1]; - - if(watch == NULL || watch->watch_cb == NULL) { - continue; - } - - switch (ev->action) { - case FILE_ACTION_ADDED: - watch->watch_cb(ev->watch_id, DMON_ACTION_CREATE, watch->rootdir, ev->filepath, NULL, - watch->user_data); - break; - case FILE_ACTION_MODIFIED: - watch->watch_cb(ev->watch_id, DMON_ACTION_MODIFY, watch->rootdir, ev->filepath, NULL, - watch->user_data); - break; - case FILE_ACTION_RENAMED_OLD_NAME: { - // find the first occurance of the NEW_NAME - // this is somewhat API flaw that we have no reference for relating old and new files - for (int j = i + 1; j < c; j++) { - dmon__win32_event* check_ev = &_dmon.events[j]; - if (check_ev->action == FILE_ACTION_RENAMED_NEW_NAME) { - watch->watch_cb(check_ev->watch_id, DMON_ACTION_MOVE, watch->rootdir, - check_ev->filepath, ev->filepath, watch->user_data); - break; - } - } - } break; - case FILE_ACTION_REMOVED: - watch->watch_cb(ev->watch_id, DMON_ACTION_DELETE, watch->rootdir, ev->filepath, NULL, - watch->user_data); - break; - } - } - stb_sb_reset(_dmon.events); -} - -_DMON_PRIVATE DWORD WINAPI dmon__thread(LPVOID arg) -{ - _DMON_UNUSED(arg); - HANDLE wait_handles[DMON_MAX_WATCHES]; - - SYSTEMTIME starttm; - GetSystemTime(&starttm); - uint64_t msecs_elapsed = 0; - - while (!_dmon.quit) { - if (_dmon.modify_watches || !TryEnterCriticalSection(&_dmon.mutex)) { - Sleep(10); - continue; - } - - if (_dmon.num_watches == 0) { - Sleep(10); - LeaveCriticalSection(&_dmon.mutex); - continue; - } - - for (int i = 0; i < _dmon.num_watches; i++) { - dmon__watch_state* watch = &_dmon.watches[i]; - wait_handles[i] = watch->overlapped.hEvent; - } - - DWORD wait_result = WaitForMultipleObjects(_dmon.num_watches, wait_handles, FALSE, 10); - DMON_ASSERT(wait_result != WAIT_FAILED); - if (wait_result != WAIT_TIMEOUT) { - dmon__watch_state* watch = &_dmon.watches[wait_result - WAIT_OBJECT_0]; - DMON_ASSERT(HasOverlappedIoCompleted(&watch->overlapped)); - - DWORD bytes; - if (GetOverlappedResult(watch->dir_handle, &watch->overlapped, &bytes, FALSE)) { - char filepath[DMON_MAX_PATH]; - PFILE_NOTIFY_INFORMATION notify; - size_t offset = 0; - - if (bytes == 0) { - dmon__refresh_watch(watch); - LeaveCriticalSection(&_dmon.mutex); - continue; - } - - do { - notify = (PFILE_NOTIFY_INFORMATION)&watch->buffer[offset]; - - int count = WideCharToMultiByte(CP_UTF8, 0, notify->FileName, - notify->FileNameLength / sizeof(WCHAR), - filepath, DMON_MAX_PATH - 1, NULL, NULL); - filepath[count] = TEXT('\0'); - dmon__unixpath(filepath, sizeof(filepath), filepath); - - // TODO: ignore directories if flag is set - - if (stb_sb_count(_dmon.events) == 0) { - msecs_elapsed = 0; - } - dmon__win32_event wev = { { 0 }, notify->Action, watch->id, false }; - dmon__strcpy(wev.filepath, sizeof(wev.filepath), filepath); - stb_sb_push(_dmon.events, wev); - - offset += notify->NextEntryOffset; - } while (notify->NextEntryOffset > 0); - - if (!_dmon.quit) { - dmon__refresh_watch(watch); - } - } - } // if (WaitForMultipleObjects) - - SYSTEMTIME tm; - GetSystemTime(&tm); - LONG dt = - (tm.wSecond - starttm.wSecond) * 1000 + (tm.wMilliseconds - starttm.wMilliseconds); - starttm = tm; - msecs_elapsed += dt; - if (msecs_elapsed > 100 && stb_sb_count(_dmon.events) > 0) { - dmon__win32_process_events(); - msecs_elapsed = 0; - } - - LeaveCriticalSection(&_dmon.mutex); - } - return 0; -} - - -DMON_API_IMPL void dmon_init(void) -{ - DMON_ASSERT(!_dmon_init); - InitializeCriticalSection(&_dmon.mutex); - - _dmon.thread_handle = - CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)dmon__thread, NULL, 0, NULL); - DMON_ASSERT(_dmon.thread_handle); - _dmon_init = true; -} - - -DMON_API_IMPL void dmon_deinit(void) -{ - DMON_ASSERT(_dmon_init); - _dmon.quit = true; - if (_dmon.thread_handle != INVALID_HANDLE_VALUE) { - WaitForSingleObject(_dmon.thread_handle, INFINITE); - CloseHandle(_dmon.thread_handle); - } - - for (int i = 0; i < _dmon.num_watches; i++) { - dmon__unwatch(&_dmon.watches[i]); - } - - DeleteCriticalSection(&_dmon.mutex); - stb_sb_free(_dmon.events); - _dmon_init = false; -} - -DMON_API_IMPL dmon_watch_id dmon_watch(const char* rootdir, - void (*watch_cb)(dmon_watch_id watch_id, dmon_action action, - const char* dirname, const char* filename, - const char* oldname, void* user), - uint32_t flags, void* user_data) -{ - DMON_ASSERT(watch_cb); - DMON_ASSERT(rootdir && rootdir[0]); - - _InterlockedExchange(&_dmon.modify_watches, 1); - EnterCriticalSection(&_dmon.mutex); - - DMON_ASSERT(_dmon.num_watches < DMON_MAX_WATCHES); - - uint32_t id = ++_dmon.num_watches; - dmon__watch_state* watch = &_dmon.watches[id - 1]; - watch->id = dmon__make_id(id); - watch->watch_flags = flags; - watch->watch_cb = watch_cb; - watch->user_data = user_data; - - dmon__strcpy(watch->rootdir, sizeof(watch->rootdir) - 1, rootdir); - dmon__unixpath(watch->rootdir, sizeof(watch->rootdir), rootdir); - size_t rootdir_len = strlen(watch->rootdir); - if (watch->rootdir[rootdir_len - 1] != '/') { - watch->rootdir[rootdir_len] = '/'; - watch->rootdir[rootdir_len + 1] = '\0'; - } - - _DMON_WINAPI_STR(rootdir, DMON_MAX_PATH); - watch->dir_handle = - CreateFile(_rootdir, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL); - if (watch->dir_handle != INVALID_HANDLE_VALUE) { - watch->notify_filter = FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_LAST_WRITE | - FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | - FILE_NOTIFY_CHANGE_SIZE; - watch->overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - DMON_ASSERT(watch->overlapped.hEvent != INVALID_HANDLE_VALUE); - - if (!dmon__refresh_watch(watch)) { - dmon__unwatch(watch); - DMON_LOG_ERROR("ReadDirectoryChanges failed"); - LeaveCriticalSection(&_dmon.mutex); - _InterlockedExchange(&_dmon.modify_watches, 0); - return dmon__make_id(0); - } - } else { - _DMON_LOG_ERRORF("Could not open: %s", rootdir); - LeaveCriticalSection(&_dmon.mutex); - _InterlockedExchange(&_dmon.modify_watches, 0); - return dmon__make_id(0); - } - - LeaveCriticalSection(&_dmon.mutex); - _InterlockedExchange(&_dmon.modify_watches, 0); - return dmon__make_id(id); -} - -DMON_API_IMPL void dmon_unwatch(dmon_watch_id id) -{ - DMON_ASSERT(id.id > 0); - - _InterlockedExchange(&_dmon.modify_watches, 1); - EnterCriticalSection(&_dmon.mutex); - - int index = id.id - 1; - DMON_ASSERT(index < _dmon.num_watches); - - dmon__unwatch(&_dmon.watches[index]); - if (index != _dmon.num_watches - 1) { - dmon__swap(_dmon.watches[index], _dmon.watches[_dmon.num_watches - 1], dmon__watch_state); - } - --_dmon.num_watches; - - LeaveCriticalSection(&_dmon.mutex); - _InterlockedExchange(&_dmon.modify_watches, 0); -} - -// clang-format off -#elif DMON_OS_LINUX -// inotify linux backend -#define _DMON_TEMP_BUFFSIZE ((sizeof(struct inotify_event) + PATH_MAX) * 1024) - -typedef struct dmon__watch_subdir { - char rootdir[DMON_MAX_PATH]; -} dmon__watch_subdir; - -typedef struct dmon__inotify_event { - char filepath[DMON_MAX_PATH]; - uint32_t mask; - uint32_t cookie; - dmon_watch_id watch_id; - bool skip; -} dmon__inotify_event; - -typedef struct dmon__watch_state { - dmon_watch_id id; - int fd; - uint32_t watch_flags; - dmon__watch_cb* watch_cb; - void* user_data; - char rootdir[DMON_MAX_PATH]; - dmon__watch_subdir* subdirs; - int* wds; -} dmon__watch_state; - -typedef struct dmon__state { - dmon__watch_state watches[DMON_MAX_WATCHES]; - dmon__inotify_event* events; - int num_watches; - volatile int modify_watches; - pthread_t thread_handle; - pthread_mutex_t mutex; - bool quit; -} dmon__state; - -static bool _dmon_init; -static dmon__state _dmon; -// clang-format on - -_DMON_PRIVATE void dmon__watch_recursive(const char* dirname, int fd, uint32_t mask, - bool followlinks, dmon__watch_state* watch) -{ - struct dirent* entry; - DIR* dir = opendir(dirname); - DMON_ASSERT(dir); - - char watchdir[DMON_MAX_PATH]; - - while ((entry = readdir(dir)) != NULL) { - bool entry_valid = false; - if (entry->d_type == DT_DIR) { - if (strcmp(entry->d_name, "..") != 0 && strcmp(entry->d_name, ".") != 0) { - dmon__strcpy(watchdir, sizeof(watchdir), dirname); - dmon__strcat(watchdir, sizeof(watchdir), entry->d_name); - entry_valid = true; - } - } else if (followlinks && entry->d_type == DT_LNK) { - char linkpath[PATH_MAX]; - dmon__strcpy(watchdir, sizeof(watchdir), dirname); - dmon__strcat(watchdir, sizeof(watchdir), entry->d_name); - char* r = realpath(watchdir, linkpath); - _DMON_UNUSED(r); - DMON_ASSERT(r); - dmon__strcpy(watchdir, sizeof(watchdir), linkpath); - entry_valid = true; - } - - // add sub-directory to watch dirs - if (entry_valid) { - int watchdir_len = strlen(watchdir); - if (watchdir[watchdir_len - 1] != '/') { - watchdir[watchdir_len] = '/'; - watchdir[watchdir_len + 1] = '\0'; - } - int wd = inotify_add_watch(fd, watchdir, mask); - _DMON_UNUSED(wd); - DMON_ASSERT(wd != -1); - - dmon__watch_subdir subdir; - dmon__strcpy(subdir.rootdir, sizeof(subdir.rootdir), watchdir); - stb_sb_push(watch->subdirs, subdir); - stb_sb_push(watch->wds, wd); - - // recurse - dmon__watch_recursive(watchdir, fd, mask, followlinks, watch); - } - } - closedir(dir); -} - -_DMON_PRIVATE void dmon__inotify_process_events(void) -{ - for (int i = 0, c = stb_sb_count(_dmon.events); i < c; i++) { - dmon__inotify_event* ev = &_dmon.events[i]; - if (ev->skip) { - continue; - } - - // remove redundant modify events on a single file - if (ev->mask == IN_MODIFY) { - for (int j = i + 1; j < c; j++) { - dmon__inotify_event* check_ev = &_dmon.events[j]; - if (check_ev->mask == IN_MODIFY && strcmp(ev->filepath, check_ev->filepath) == 0) { - ev->skip = true; - break; - } - } - } else if (ev->mask == IN_CREATE) { - bool loop_break = false; - for (int j = i + 1; j < c && !loop_break; j++) { - dmon__inotify_event* check_ev = &_dmon.events[j]; - if (check_ev->mask == IN_MOVED_FROM && - strcmp(ev->filepath, check_ev->filepath) == 0) { - // there is a case where some programs (like gedit): - // when we save, it creates a temp file, and moves it to the file being modified - // search for these cases and remove all of them - for (int k = j + 1; k < c; k++) { - dmon__inotify_event* third_ev = &_dmon.events[k]; - if (third_ev->mask == IN_MOVED_TO && check_ev->cookie == third_ev->cookie) { - third_ev->mask = IN_MODIFY; // change to modified - ev->skip = check_ev->skip = true; - loop_break = true; - break; - } - } - } else if (check_ev->mask == IN_MODIFY && strcmp(ev->filepath, check_ev->filepath) == 0) { - // Another case is that file is copied. CREATE and MODIFY happens sequentially - // so we ignore MODIFY event - check_ev->skip = true; - } - } - } else if (ev->mask == IN_MOVED_FROM) { - bool move_valid = false; - for (int j = i + 1; j < c; j++) { - dmon__inotify_event* check_ev = &_dmon.events[j]; - if (check_ev->mask == IN_MOVED_TO && ev->cookie == check_ev->cookie) { - move_valid = true; - break; - } - } - - // in some environments like nautilus file explorer: - // when a file is deleted, it is moved to recycle bin - // so if the destination of the move is not valid, it's probably DELETE - if (!move_valid) { - ev->mask = IN_DELETE; - } - } else if (ev->mask == IN_MOVED_TO) { - bool move_valid = false; - for (int j = 0; j < i; j++) { - dmon__inotify_event* check_ev = &_dmon.events[j]; - if (check_ev->mask == IN_MOVED_FROM && ev->cookie == check_ev->cookie) { - move_valid = true; - break; - } - } - - // in some environments like nautilus file explorer: - // when a file is deleted, it is moved to recycle bin, on undo it is moved back it - // so if the destination of the move is not valid, it's probably CREATE - if (!move_valid) { - ev->mask = IN_CREATE; - } - } - } - - // trigger user callbacks - for (int i = 0, c = stb_sb_count(_dmon.events); i < c; i++) { - dmon__inotify_event* ev = &_dmon.events[i]; - if (ev->skip) { - continue; - } - dmon__watch_state* watch = &_dmon.watches[ev->watch_id.id - 1]; - - if(watch == NULL || watch->watch_cb == NULL) { - continue; - } - - switch (ev->mask) { - case IN_CREATE: - watch->watch_cb(ev->watch_id, DMON_ACTION_CREATE, watch->rootdir, ev->filepath, NULL, watch->user_data); - break; - case IN_MODIFY: - watch->watch_cb(ev->watch_id, DMON_ACTION_MODIFY, watch->rootdir, ev->filepath, NULL, watch->user_data); - break; - case IN_MOVED_FROM: { - for (int j = i + 1; j < c; j++) { - dmon__inotify_event* check_ev = &_dmon.events[j]; - if (check_ev->mask == IN_MOVED_TO && ev->cookie == check_ev->cookie) { - watch->watch_cb(check_ev->watch_id, DMON_ACTION_MOVE, watch->rootdir, - check_ev->filepath, ev->filepath, watch->user_data); - break; - } - } - } break; - case IN_DELETE: - watch->watch_cb(ev->watch_id, DMON_ACTION_DELETE, watch->rootdir, ev->filepath, NULL, watch->user_data); - break; - } - } - - - stb_sb_reset(_dmon.events); -} - -static void* dmon__thread(void* arg) -{ - _DMON_UNUSED(arg); - - static uint8_t buff[_DMON_TEMP_BUFFSIZE]; - struct timespec req = { (time_t)10 / 1000, (long)(10 * 1000000) }; - struct timespec rem = { 0, 0 }; - struct timeval timeout; - uint64_t usecs_elapsed = 0; - - struct timeval starttm; - gettimeofday(&starttm, 0); - - while (!_dmon.quit) { - - if (_dmon.modify_watches || pthread_mutex_trylock(&_dmon.mutex) != 0) { - nanosleep(&req, &rem); - continue; - } - - if (_dmon.num_watches == 0) { - nanosleep(&req, &rem); - pthread_mutex_unlock(&_dmon.mutex); - continue; - } - - // Create read FD set - fd_set rfds; - FD_ZERO(&rfds); - for (int i = 0; i < _dmon.num_watches; i++) { - dmon__watch_state* watch = &_dmon.watches[i]; - FD_SET(watch->fd, &rfds); - } - - timeout.tv_sec = 0; - timeout.tv_usec = 100000; - if (select(FD_SETSIZE, &rfds, NULL, NULL, &timeout)) { - for (int i = 0; i < _dmon.num_watches; i++) { - dmon__watch_state* watch = &_dmon.watches[i]; - if (FD_ISSET(watch->fd, &rfds)) { - ssize_t offset = 0; - ssize_t len = read(watch->fd, buff, _DMON_TEMP_BUFFSIZE); - if (len <= 0) { - continue; - } - - while (offset < len) { - struct inotify_event* iev = (struct inotify_event*)&buff[offset]; - - char filepath[DMON_MAX_PATH]; - dmon__strcpy(filepath, sizeof(filepath), iev->name); - - // TODO: ignore directories if flag is set - - if (stb_sb_count(_dmon.events) == 0) { - usecs_elapsed = 0; - } - dmon__inotify_event dev = { { 0 }, iev->mask, iev->cookie, watch->id, false }; - dmon__strcpy(dev.filepath, sizeof(dev.filepath), filepath); - stb_sb_push(_dmon.events, dev); - - offset += sizeof(struct inotify_event) + iev->len; - } - } - } - } - - struct timeval tm; - gettimeofday(&tm, 0); - long dt = (tm.tv_sec - starttm.tv_sec) * 1000000 + tm.tv_usec - starttm.tv_usec; - starttm = tm; - usecs_elapsed += dt; - if (usecs_elapsed > 100000 && stb_sb_count(_dmon.events) > 0) { - dmon__inotify_process_events(); - usecs_elapsed = 0; - } - - pthread_mutex_unlock(&_dmon.mutex); - } - return 0x0; -} - -_DMON_PRIVATE void dmon__unwatch(dmon__watch_state* watch) -{ - close(watch->fd); - stb_sb_free(watch->subdirs); - stb_sb_free(watch->wds); - memset(watch, 0x0, sizeof(dmon__watch_state)); -} - -DMON_API_IMPL void dmon_init(void) -{ - DMON_ASSERT(!_dmon_init); - pthread_mutex_init(&_dmon.mutex, NULL); - - int r = pthread_create(&_dmon.thread_handle, NULL, dmon__thread, NULL); - _DMON_UNUSED(r); - DMON_ASSERT(r == 0 && "pthread_create failed"); - _dmon_init = true; -} - -DMON_API_IMPL void dmon_deinit(void) -{ - DMON_ASSERT(_dmon_init); - _dmon.quit = true; - pthread_join(_dmon.thread_handle, NULL); - - for (int i = 0; i < _dmon.num_watches; i++) { - dmon__unwatch(&_dmon.watches[i]); - } - - pthread_mutex_destroy(&_dmon.mutex); - stb_sb_free(_dmon.events); - _dmon_init = false; -} - -DMON_API_IMPL dmon_watch_id dmon_watch(const char* rootdir, - void (*watch_cb)(dmon_watch_id watch_id, dmon_action action, - const char* dirname, const char* filename, - const char* oldname, void* user), - uint32_t flags, void* user_data) -{ - DMON_ASSERT(watch_cb); - DMON_ASSERT(rootdir && rootdir[0]); - - __sync_lock_test_and_set(&_dmon.modify_watches, 1); - pthread_mutex_lock(&_dmon.mutex); - - DMON_ASSERT(_dmon.num_watches < DMON_MAX_WATCHES); - - uint32_t id = ++_dmon.num_watches; - dmon__watch_state* watch = &_dmon.watches[id - 1]; - watch->id = dmon__make_id(id); - watch->watch_flags = flags; - watch->watch_cb = watch_cb; - watch->user_data = user_data; - - struct stat root_st; - if (stat(rootdir, &root_st) != 0 || !S_ISDIR(root_st.st_mode) || - (root_st.st_mode & S_IRUSR) != S_IRUSR) { - _DMON_LOG_ERRORF("Could not open/read directory: %s", rootdir); - pthread_mutex_unlock(&_dmon.mutex); - __sync_lock_test_and_set(&_dmon.modify_watches, 0); - return dmon__make_id(0); - } - - - if (S_ISLNK(root_st.st_mode)) { - if (flags & DMON_WATCHFLAGS_FOLLOW_SYMLINKS) { - char linkpath[PATH_MAX]; - char* r = realpath(rootdir, linkpath); - _DMON_UNUSED(r); - DMON_ASSERT(r); - - dmon__strcpy(watch->rootdir, sizeof(watch->rootdir) - 1, linkpath); - } else { - _DMON_LOG_ERRORF("symlinks are unsupported: %s. use DMON_WATCHFLAGS_FOLLOW_SYMLINKS", - rootdir); - pthread_mutex_unlock(&_dmon.mutex); - __sync_lock_test_and_set(&_dmon.modify_watches, 0); - return dmon__make_id(0); - } - } else { - dmon__strcpy(watch->rootdir, sizeof(watch->rootdir) - 1, rootdir); - } - - // add trailing slash - int rootdir_len = strlen(watch->rootdir); - if (watch->rootdir[rootdir_len - 1] != '/') { - watch->rootdir[rootdir_len] = '/'; - watch->rootdir[rootdir_len + 1] = '\0'; - } - - watch->fd = inotify_init(); - if (watch->fd < -1) { - DMON_LOG_ERROR("could not create inotify instance"); - pthread_mutex_unlock(&_dmon.mutex); - __sync_lock_test_and_set(&_dmon.modify_watches, 0); - return dmon__make_id(0); - } - - uint32_t inotify_mask = IN_MOVED_TO | IN_CREATE | IN_MOVED_FROM | IN_DELETE | IN_MODIFY; - int wd = inotify_add_watch(watch->fd, watch->rootdir, inotify_mask); - if (wd < 0) { - _DMON_LOG_ERRORF("watch failed: %s", watch->rootdir); - pthread_mutex_unlock(&_dmon.mutex); - __sync_lock_test_and_set(&_dmon.modify_watches, 0); - return dmon__make_id(0); - } - dmon__watch_subdir subdir; - dmon__strcpy(subdir.rootdir, sizeof(subdir.rootdir), watch->rootdir); - stb_sb_push(watch->subdirs, subdir); - stb_sb_push(watch->wds, wd); - - // recursive mode: enumarate all child directories and add them to watch - if (flags & DMON_WATCHFLAGS_RECURSIVE) { - dmon__watch_recursive(watch->rootdir, watch->fd, inotify_mask, - (flags & DMON_WATCHFLAGS_FOLLOW_SYMLINKS) ? true : false, watch); - } - - - pthread_mutex_unlock(&_dmon.mutex); - __sync_lock_test_and_set(&_dmon.modify_watches, 0); - return dmon__make_id(id); -} - -DMON_API_IMPL void dmon_unwatch(dmon_watch_id id) -{ - DMON_ASSERT(id.id > 0); - - __sync_lock_test_and_set(&_dmon.modify_watches, 1); - pthread_mutex_lock(&_dmon.mutex); - - int index = id.id - 1; - DMON_ASSERT(index < _dmon.num_watches); - - dmon__unwatch(&_dmon.watches[index]); - if (index != _dmon.num_watches - 1) { - dmon__swap(_dmon.watches[index], _dmon.watches[_dmon.num_watches - 1], dmon__watch_state); - } - --_dmon.num_watches; - - pthread_mutex_unlock(&_dmon.mutex); - __sync_lock_test_and_set(&_dmon.modify_watches, 0); -} -// clang-format off -#elif DMON_OS_MACOS -// FSEvents MacOS backend -typedef struct dmon__fsevent_event { - char filepath[DMON_MAX_PATH]; - uint64_t event_id; - long event_flags; - dmon_watch_id watch_id; - bool skip; - bool move_valid; -} dmon__fsevent_event; - -typedef struct dmon__watch_state { - dmon_watch_id id; - uint32_t watch_flags; - FSEventStreamRef fsev_stream_ref; - dmon__watch_cb* watch_cb; - void* user_data; - char rootdir[DMON_MAX_PATH]; - bool init; -} dmon__watch_state; - -typedef struct dmon__state { - dmon__watch_state watches[DMON_MAX_WATCHES]; - dmon__fsevent_event* events; - int num_watches; - volatile int modify_watches; - pthread_t thread_handle; - dispatch_semaphore_t thread_sem; - pthread_mutex_t mutex; - CFRunLoopRef cf_loop_ref; - CFAllocatorRef cf_alloc_ref; - bool quit; -} dmon__state; - -union dmon__cast_userdata { - void* ptr; - uint32_t id; -}; - -static bool _dmon_init; -static dmon__state _dmon; -// clang-format on - -_DMON_PRIVATE void* dmon__cf_malloc(CFIndex size, CFOptionFlags hints, void* info) -{ - _DMON_UNUSED(hints); - _DMON_UNUSED(info); - return DMON_MALLOC(size); -} - -_DMON_PRIVATE void dmon__cf_free(void* ptr, void* info) -{ - _DMON_UNUSED(info); - DMON_FREE(ptr); -} - -_DMON_PRIVATE void* dmon__cf_realloc(void* ptr, CFIndex newsize, CFOptionFlags hints, void* info) -{ - _DMON_UNUSED(hints); - _DMON_UNUSED(info); - return DMON_REALLOC(ptr, (size_t)newsize); -} - -_DMON_PRIVATE void dmon__fsevent_process_events(void) -{ - for (int i = 0, c = stb_sb_count(_dmon.events); i < c; i++) { - dmon__fsevent_event* ev = &_dmon.events[i]; - if (ev->skip) { - continue; - } - - // remove redundant modify events on a single file - if (ev->event_flags & kFSEventStreamEventFlagItemModified) { - for (int j = i + 1; j < c; j++) { - dmon__fsevent_event* check_ev = &_dmon.events[j]; - if ((check_ev->event_flags & kFSEventStreamEventFlagItemModified) && - strcmp(ev->filepath, check_ev->filepath) == 0) { - ev->skip = true; - break; - } - } - } else if ((ev->event_flags & kFSEventStreamEventFlagItemRenamed) && !ev->move_valid) { - for (int j = i + 1; j < c; j++) { - dmon__fsevent_event* check_ev = &_dmon.events[j]; - if ((check_ev->event_flags & kFSEventStreamEventFlagItemRenamed) && - check_ev->event_id == (ev->event_id + 1)) { - ev->move_valid = check_ev->move_valid = true; - break; - } - } - - // in some environments like finder file explorer: - // when a file is deleted, it is moved to recycle bin - // so if the destination of the move is not valid, it's probably DELETE or CREATE - // decide CREATE if file exists - if (!ev->move_valid) { - ev->event_flags &= ~kFSEventStreamEventFlagItemRenamed; - - char abs_filepath[DMON_MAX_PATH]; - dmon__watch_state* watch = &_dmon.watches[ev->watch_id.id-1]; - dmon__strcpy(abs_filepath, sizeof(abs_filepath), watch->rootdir); - dmon__strcat(abs_filepath, sizeof(abs_filepath), ev->filepath); - - struct stat root_st; - if (stat(abs_filepath, &root_st) != 0) { - ev->event_flags |= kFSEventStreamEventFlagItemRemoved; - } else { - ev->event_flags |= kFSEventStreamEventFlagItemCreated; - } - } - } - } - - // trigger user callbacks - for (int i = 0, c = stb_sb_count(_dmon.events); i < c; i++) { - dmon__fsevent_event* ev = &_dmon.events[i]; - if (ev->skip) { - continue; - } - dmon__watch_state* watch = &_dmon.watches[ev->watch_id.id - 1]; - - if(watch == NULL || watch->watch_cb == NULL) { - continue; - } - - if (ev->event_flags & kFSEventStreamEventFlagItemCreated) { - watch->watch_cb(ev->watch_id, DMON_ACTION_CREATE, watch->rootdir, ev->filepath, NULL, - watch->user_data); - } else if (ev->event_flags & kFSEventStreamEventFlagItemModified) { - watch->watch_cb(ev->watch_id, DMON_ACTION_MODIFY, watch->rootdir, ev->filepath, NULL, - watch->user_data); - } else if (ev->event_flags & kFSEventStreamEventFlagItemRenamed) { - for (int j = i + 1; j < c; j++) { - dmon__fsevent_event* check_ev = &_dmon.events[j]; - if (check_ev->event_flags & kFSEventStreamEventFlagItemRenamed) { - watch->watch_cb(check_ev->watch_id, DMON_ACTION_MOVE, watch->rootdir, - check_ev->filepath, ev->filepath, watch->user_data); - break; - } - } - } else if (ev->event_flags & kFSEventStreamEventFlagItemRemoved) { - watch->watch_cb(ev->watch_id, DMON_ACTION_DELETE, watch->rootdir, ev->filepath, NULL, - watch->user_data); - } - } - - stb_sb_reset(_dmon.events); -} - -static void* dmon__thread(void* arg) -{ - _DMON_UNUSED(arg); - - struct timespec req = { (time_t)10 / 1000, (long)(10 * 1000000) }; - struct timespec rem = { 0, 0 }; - - _dmon.cf_loop_ref = CFRunLoopGetCurrent(); - dispatch_semaphore_signal(_dmon.thread_sem); - - while (!_dmon.quit) { - if (_dmon.modify_watches || pthread_mutex_trylock(&_dmon.mutex) != 0) { - nanosleep(&req, &rem); - continue; - } - - if (_dmon.num_watches == 0) { - nanosleep(&req, &rem); - pthread_mutex_unlock(&_dmon.mutex); - continue; - } - - for (int i = 0; i < _dmon.num_watches; i++) { - dmon__watch_state* watch = &_dmon.watches[i]; - if (!watch->init) { - DMON_ASSERT(watch->fsev_stream_ref); - FSEventStreamScheduleWithRunLoop(watch->fsev_stream_ref, _dmon.cf_loop_ref, - kCFRunLoopDefaultMode); - FSEventStreamStart(watch->fsev_stream_ref); - - watch->init = true; - } - } - - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.5, kCFRunLoopRunTimedOut); - dmon__fsevent_process_events(); - - pthread_mutex_unlock(&_dmon.mutex); - } - - CFRunLoopStop(_dmon.cf_loop_ref); - _dmon.cf_loop_ref = NULL; - return 0x0; -} - -_DMON_PRIVATE void dmon__unwatch(dmon__watch_state* watch) -{ - if (watch->fsev_stream_ref) { - FSEventStreamStop(watch->fsev_stream_ref); - FSEventStreamInvalidate(watch->fsev_stream_ref); - FSEventStreamRelease(watch->fsev_stream_ref); - watch->fsev_stream_ref = NULL; - } - - memset(watch, 0x0, sizeof(dmon__watch_state)); -} - -DMON_API_IMPL void dmon_init(void) -{ - DMON_ASSERT(!_dmon_init); - pthread_mutex_init(&_dmon.mutex, NULL); - - CFAllocatorContext cf_alloc_ctx = { 0 }; - cf_alloc_ctx.allocate = dmon__cf_malloc; - cf_alloc_ctx.deallocate = dmon__cf_free; - cf_alloc_ctx.reallocate = dmon__cf_realloc; - _dmon.cf_alloc_ref = CFAllocatorCreate(NULL, &cf_alloc_ctx); - - _dmon.thread_sem = dispatch_semaphore_create(0); - DMON_ASSERT(_dmon.thread_sem); - - int r = pthread_create(&_dmon.thread_handle, NULL, dmon__thread, NULL); - _DMON_UNUSED(r); - DMON_ASSERT(r == 0 && "pthread_create failed"); - - // wait for thread to initialize loop object - dispatch_semaphore_wait(_dmon.thread_sem, DISPATCH_TIME_FOREVER); - - _dmon_init = true; -} - -DMON_API_IMPL void dmon_deinit(void) -{ - DMON_ASSERT(_dmon_init); - _dmon.quit = true; - pthread_join(_dmon.thread_handle, NULL); - - dispatch_release(_dmon.thread_sem); - - for (int i = 0; i < _dmon.num_watches; i++) { - dmon__unwatch(&_dmon.watches[i]); - } - - pthread_mutex_destroy(&_dmon.mutex); - stb_sb_free(_dmon.events); - if (_dmon.cf_alloc_ref) { - CFRelease(_dmon.cf_alloc_ref); - } - - _dmon_init = false; -} - -_DMON_PRIVATE void dmon__fsevent_callback(ConstFSEventStreamRef stream_ref, void* user_data, - size_t num_events, void* event_paths, - const FSEventStreamEventFlags event_flags[], - const FSEventStreamEventId event_ids[]) -{ - _DMON_UNUSED(stream_ref); - - union dmon__cast_userdata _userdata; - _userdata.ptr = user_data; - dmon_watch_id watch_id = dmon__make_id(_userdata.id); - DMON_ASSERT(watch_id.id > 0); - dmon__watch_state* watch = &_dmon.watches[watch_id.id - 1]; - char abs_filepath[DMON_MAX_PATH]; - - for (size_t i = 0; i < num_events; i++) { - const char* filepath = ((const char**)event_paths)[i]; - long flags = (long)event_flags[i]; - uint64_t event_id = (uint64_t)event_ids[i]; - dmon__fsevent_event ev; - memset(&ev, 0x0, sizeof(ev)); - - dmon__strcpy(abs_filepath, sizeof(abs_filepath), filepath); - - // normalize path (TODO: have to recheck this to be consistent with other platforms) - dmon__tolower(abs_filepath, sizeof(abs_filepath), - dmon__unixpath(abs_filepath, sizeof(abs_filepath), abs_filepath)); - - // strip the root dir - DMON_ASSERT(strstr(abs_filepath, watch->rootdir) == abs_filepath); - dmon__strcpy(ev.filepath, sizeof(ev.filepath), abs_filepath + strlen(watch->rootdir)); - - ev.event_flags = flags; - ev.event_id = event_id; - ev.watch_id = watch_id; - stb_sb_push(_dmon.events, ev); - } -} - -DMON_API_IMPL dmon_watch_id dmon_watch(const char* rootdir, - void (*watch_cb)(dmon_watch_id watch_id, dmon_action action, - const char* dirname, const char* filename, - const char* oldname, void* user), - uint32_t flags, void* user_data) -{ - DMON_ASSERT(watch_cb); - DMON_ASSERT(rootdir && rootdir[0]); - - __sync_lock_test_and_set(&_dmon.modify_watches, 1); - pthread_mutex_lock(&_dmon.mutex); - - DMON_ASSERT(_dmon.num_watches < DMON_MAX_WATCHES); - - uint32_t id = ++_dmon.num_watches; - dmon__watch_state* watch = &_dmon.watches[id - 1]; - watch->id = dmon__make_id(id); - watch->watch_flags = flags; - watch->watch_cb = watch_cb; - watch->user_data = user_data; - - struct stat root_st; - if (stat(rootdir, &root_st) != 0 || !S_ISDIR(root_st.st_mode) || - (root_st.st_mode & S_IRUSR) != S_IRUSR) { - _DMON_LOG_ERRORF("Could not open/read directory: %s", rootdir); - pthread_mutex_unlock(&_dmon.mutex); - __sync_lock_test_and_set(&_dmon.modify_watches, 0); - return dmon__make_id(0); - } - - if (S_ISLNK(root_st.st_mode)) { - if (flags & DMON_WATCHFLAGS_FOLLOW_SYMLINKS) { - char linkpath[PATH_MAX]; - char* r = realpath(rootdir, linkpath); - _DMON_UNUSED(r); - DMON_ASSERT(r); - - dmon__strcpy(watch->rootdir, sizeof(watch->rootdir) - 1, linkpath); - } else { - _DMON_LOG_ERRORF("symlinks are unsupported: %s. use DMON_WATCHFLAGS_FOLLOW_SYMLINKS", - rootdir); - pthread_mutex_unlock(&_dmon.mutex); - __sync_lock_test_and_set(&_dmon.modify_watches, 0); - return dmon__make_id(0); - } - } else { - dmon__strcpy(watch->rootdir, sizeof(watch->rootdir) - 1, rootdir); - } - - // add trailing slash - int rootdir_len = strlen(watch->rootdir); - if (watch->rootdir[rootdir_len - 1] != '/') { - watch->rootdir[rootdir_len] = '/'; - watch->rootdir[rootdir_len + 1] = '\0'; - } - - // create FS objects - CFStringRef cf_dir = CFStringCreateWithCString(NULL, watch->rootdir, kCFStringEncodingUTF8); - CFArrayRef cf_dirarr = CFArrayCreate(NULL, (const void**)&cf_dir, 1, NULL); - - FSEventStreamContext ctx; - union dmon__cast_userdata userdata; - userdata.id = id; - ctx.version = 0; - ctx.info = userdata.ptr; - ctx.retain = NULL; - ctx.release = NULL; - ctx.copyDescription = NULL; - watch->fsev_stream_ref = FSEventStreamCreate(_dmon.cf_alloc_ref, dmon__fsevent_callback, &ctx, - cf_dirarr, kFSEventStreamEventIdSinceNow, 0.25, - kFSEventStreamCreateFlagFileEvents); - - - CFRelease(cf_dirarr); - CFRelease(cf_dir); - - pthread_mutex_unlock(&_dmon.mutex); - __sync_lock_test_and_set(&_dmon.modify_watches, 0); - return dmon__make_id(id); -} - -DMON_API_IMPL void dmon_unwatch(dmon_watch_id id) -{ - DMON_ASSERT(id.id > 0); - - __sync_lock_test_and_set(&_dmon.modify_watches, 1); - pthread_mutex_lock(&_dmon.mutex); - - int index = id.id - 1; - DMON_ASSERT(index < _dmon.num_watches); - - dmon__unwatch(&_dmon.watches[index]); - if (index != _dmon.num_watches - 1) { - dmon__swap(_dmon.watches[index], _dmon.watches[_dmon.num_watches - 1], dmon__watch_state); - } - --_dmon.num_watches; - - pthread_mutex_unlock(&_dmon.mutex); - __sync_lock_test_and_set(&_dmon.modify_watches, 0); -} - -// clang-format off -#endif - -#endif // DMON_IMPL -#endif // __DMON_H__ -// clang-format on diff --git a/framework/3rd/dmon/test.c b/framework/3rd/dmon/test.c deleted file mode 100755 index dabc631..0000000 --- a/framework/3rd/dmon/test.c +++ /dev/null @@ -1,40 +0,0 @@ -#include - -#define DMON_IMPL -#include "dmon.h" - -static void watch_callback(dmon_watch_id watch_id, dmon_action action, const char* rootdir, - const char* filepath, const char* oldfilepath, void* user) -{ - (void)(user); - (void)(watch_id); - - switch (action) { - case DMON_ACTION_CREATE: - printf("CREATE: [%s]%s\n", rootdir, filepath); - break; - case DMON_ACTION_DELETE: - printf("DELETE: [%s]%s\n", rootdir, filepath); - break; - case DMON_ACTION_MODIFY: - printf("MODIFY: [%s]%s\n", rootdir, filepath); - break; - case DMON_ACTION_MOVE: - printf("MOVE: [%s]%s -> [%s]%s\n", rootdir, oldfilepath, rootdir, filepath); - break; - } -} - -int main(int argc, char* argv[]) -{ - if (argc > 1) { - dmon_init(); - puts("waiting for changes .."); - dmon_watch(argv[1], watch_callback, DMON_WATCHFLAGS_RECURSIVE, NULL); - getchar(); - dmon_deinit(); - } else { - puts("usage: test dirname"); - } - return 0; -} diff --git a/framework/3rd/greatest/greatest.h b/framework/3rd/greatest/greatest.h deleted file mode 100644 index 9022c95..0000000 --- a/framework/3rd/greatest/greatest.h +++ /dev/null @@ -1,1266 +0,0 @@ -/* - * Copyright (c) 2011-2021 Scott Vokes - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef GREATEST_H -#define GREATEST_H - -#if defined(__cplusplus) && !defined(GREATEST_NO_EXTERN_CPLUSPLUS) -extern "C" { -#endif - -/* 1.5.0 */ -#define GREATEST_VERSION_MAJOR 1 -#define GREATEST_VERSION_MINOR 5 -#define GREATEST_VERSION_PATCH 0 - -/* A unit testing system for C, contained in 1 file. - * It doesn't use dynamic allocation or depend on anything - * beyond ANSI C89. - * - * An up-to-date version can be found at: - * https://github.com/silentbicycle/greatest/ - */ - - -/********************************************************************* - * Minimal test runner template - *********************************************************************/ -#if 0 - -#include "greatest.h" - -TEST foo_should_foo(void) { - PASS(); -} - -static void setup_cb(void *data) { - printf("setup callback for each test case\n"); -} - -static void teardown_cb(void *data) { - printf("teardown callback for each test case\n"); -} - -SUITE(suite) { - /* Optional setup/teardown callbacks which will be run before/after - * every test case. If using a test suite, they will be cleared when - * the suite finishes. */ - SET_SETUP(setup_cb, voidp_to_callback_data); - SET_TEARDOWN(teardown_cb, voidp_to_callback_data); - - RUN_TEST(foo_should_foo); -} - -/* Add definitions that need to be in the test runner's main file. */ -GREATEST_MAIN_DEFS(); - -/* Set up, run suite(s) of tests, report pass/fail/skip stats. */ -int run_tests(void) { - GREATEST_INIT(); /* init. greatest internals */ - /* List of suites to run (if any). */ - RUN_SUITE(suite); - - /* Tests can also be run directly, without using test suites. */ - RUN_TEST(foo_should_foo); - - GREATEST_PRINT_REPORT(); /* display results */ - return greatest_all_passed(); -} - -/* main(), for a standalone command-line test runner. - * This replaces run_tests above, and adds command line option - * handling and exiting with a pass/fail status. */ -int main(int argc, char **argv) { - GREATEST_MAIN_BEGIN(); /* init & parse command-line args */ - RUN_SUITE(suite); - GREATEST_MAIN_END(); /* display results */ -} - -#endif -/*********************************************************************/ - - -#include -#include -#include -#include - -/*********** - * Options * - ***********/ - -/* Default column width for non-verbose output. */ -#ifndef GREATEST_DEFAULT_WIDTH -#define GREATEST_DEFAULT_WIDTH 72 -#endif - -/* FILE *, for test logging. */ -#ifndef GREATEST_STDOUT -#define GREATEST_STDOUT stdout -#endif - -/* Remove GREATEST_ prefix from most commonly used symbols? */ -#ifndef GREATEST_USE_ABBREVS -#define GREATEST_USE_ABBREVS 1 -#endif - -/* Set to 0 to disable all use of setjmp/longjmp. */ -#ifndef GREATEST_USE_LONGJMP -#define GREATEST_USE_LONGJMP 0 -#endif - -/* Make it possible to replace fprintf with another - * function with the same interface. */ -#ifndef GREATEST_FPRINTF -#define GREATEST_FPRINTF fprintf -#endif - -#if GREATEST_USE_LONGJMP -#include -#endif - -/* Set to 0 to disable all use of time.h / clock(). */ -#ifndef GREATEST_USE_TIME -#define GREATEST_USE_TIME 1 -#endif - -#if GREATEST_USE_TIME -#include -#endif - -/* Floating point type, for ASSERT_IN_RANGE. */ -#ifndef GREATEST_FLOAT -#define GREATEST_FLOAT double -#define GREATEST_FLOAT_FMT "%g" -#endif - -/* Size of buffer for test name + optional '_' separator and suffix */ -#ifndef GREATEST_TESTNAME_BUF_SIZE -#define GREATEST_TESTNAME_BUF_SIZE 128 -#endif - - -/********* - * Types * - *********/ - -/* Info for the current running suite. */ -typedef struct greatest_suite_info { - unsigned int tests_run; - unsigned int passed; - unsigned int failed; - unsigned int skipped; - -#if GREATEST_USE_TIME - /* timers, pre/post running suite and individual tests */ - clock_t pre_suite; - clock_t post_suite; - clock_t pre_test; - clock_t post_test; -#endif -} greatest_suite_info; - -/* Type for a suite function. */ -typedef void greatest_suite_cb(void); - -/* Types for setup/teardown callbacks. If non-NULL, these will be run - * and passed the pointer to their additional data. */ -typedef void greatest_setup_cb(void *udata); -typedef void greatest_teardown_cb(void *udata); - -/* Type for an equality comparison between two pointers of the same type. - * Should return non-0 if equal, otherwise 0. - * UDATA is a closure value, passed through from ASSERT_EQUAL_T[m]. */ -typedef int greatest_equal_cb(const void *expd, const void *got, void *udata); - -/* Type for a callback that prints a value pointed to by T. - * Return value has the same meaning as printf's. - * UDATA is a closure value, passed through from ASSERT_EQUAL_T[m]. */ -typedef int greatest_printf_cb(const void *t, void *udata); - -/* Callbacks for an arbitrary type; needed for type-specific - * comparisons via GREATEST_ASSERT_EQUAL_T[m].*/ -typedef struct greatest_type_info { - greatest_equal_cb *equal; - greatest_printf_cb *print; -} greatest_type_info; - -typedef struct greatest_memory_cmp_env { - const unsigned char *exp; - const unsigned char *got; - size_t size; -} greatest_memory_cmp_env; - -/* Callbacks for string and raw memory types. */ -extern greatest_type_info greatest_type_info_string; -extern greatest_type_info greatest_type_info_memory; - -typedef enum { - GREATEST_FLAG_FIRST_FAIL = 0x01, - GREATEST_FLAG_LIST_ONLY = 0x02, - GREATEST_FLAG_ABORT_ON_FAIL = 0x04 -} greatest_flag_t; - -/* Internal state for a PRNG, used to shuffle test order. */ -struct greatest_prng { - unsigned char random_order; /* use random ordering? */ - unsigned char initialized; /* is random ordering initialized? */ - unsigned char pad_0[6]; - unsigned long state; /* PRNG state */ - unsigned long count; /* how many tests, this pass */ - unsigned long count_ceil; /* total number of tests */ - unsigned long count_run; /* total tests run */ - unsigned long a; /* LCG multiplier */ - unsigned long c; /* LCG increment */ - unsigned long m; /* LCG modulus, based on count_ceil */ -}; - -/* Struct containing all test runner state. */ -typedef struct greatest_run_info { - unsigned char flags; - unsigned char verbosity; - unsigned char running_test; /* guard for nested RUN_TEST calls */ - unsigned char exact_name_match; - - unsigned int tests_run; /* total test count */ - - /* currently running test suite */ - greatest_suite_info suite; - - /* overall pass/fail/skip counts */ - unsigned int passed; - unsigned int failed; - unsigned int skipped; - unsigned int assertions; - - /* info to print about the most recent failure */ - unsigned int fail_line; - unsigned int pad_1; - const char *fail_file; - const char *msg; - - /* current setup/teardown hooks and userdata */ - greatest_setup_cb *setup; - void *setup_udata; - greatest_teardown_cb *teardown; - void *teardown_udata; - - /* formatting info for ".....s...F"-style output */ - unsigned int col; - unsigned int width; - - /* only run a specific suite or test */ - const char *suite_filter; - const char *test_filter; - const char *test_exclude; - const char *name_suffix; /* print suffix with test name */ - char name_buf[GREATEST_TESTNAME_BUF_SIZE]; - - struct greatest_prng prng[2]; /* 0: suites, 1: tests */ - -#if GREATEST_USE_TIME - /* overall timers */ - clock_t begin; - clock_t end; -#endif - -#if GREATEST_USE_LONGJMP - int pad_jmp_buf; - unsigned char pad_2[4]; - jmp_buf jump_dest; -#endif -} greatest_run_info; - -struct greatest_report_t { - /* overall pass/fail/skip counts */ - unsigned int passed; - unsigned int failed; - unsigned int skipped; - unsigned int assertions; -}; - -/* Global var for the current testing context. - * Initialized by GREATEST_MAIN_DEFS(). */ -extern greatest_run_info greatest_info; - -/* Type for ASSERT_ENUM_EQ's ENUM_STR argument. */ -typedef const char *greatest_enum_str_fun(int value); - - -/********************** - * Exported functions * - **********************/ - -/* These are used internally by greatest macros. */ -int greatest_test_pre(const char *name); -void greatest_test_post(int res); -int greatest_do_assert_equal_t(const void *expd, const void *got, - greatest_type_info *type_info, void *udata); -void greatest_prng_init_first_pass(int id); -int greatest_prng_init_second_pass(int id, unsigned long seed); -void greatest_prng_step(int id); - -/* These are part of the public greatest API. */ -void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata); -void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, void *udata); -void GREATEST_INIT(void); -void GREATEST_PRINT_REPORT(void); -int greatest_all_passed(void); -void greatest_set_suite_filter(const char *filter); -void greatest_set_test_filter(const char *filter); -void greatest_set_test_exclude(const char *filter); -void greatest_set_exact_name_match(void); -void greatest_stop_at_first_fail(void); -void greatest_abort_on_fail(void); -void greatest_list_only(void); -void greatest_get_report(struct greatest_report_t *report); -unsigned int greatest_get_verbosity(void); -void greatest_set_verbosity(unsigned int verbosity); -void greatest_set_flag(greatest_flag_t flag); -void greatest_set_test_suffix(const char *suffix); - - -/******************** -* Language Support * -********************/ - -/* If __VA_ARGS__ (C99) is supported, allow parametric testing -* without needing to manually manage the argument struct. */ -#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 19901L) || \ - (defined(_MSC_VER) && _MSC_VER >= 1800) -#define GREATEST_VA_ARGS -#endif - - -/********** - * Macros * - **********/ - -/* Define a suite. (The duplication is intentional -- it eliminates - * a warning from -Wmissing-declarations.) */ -#define GREATEST_SUITE(NAME) void NAME(void); void NAME(void) - -/* Declare a suite, provided by another compilation unit. */ -#define GREATEST_SUITE_EXTERN(NAME) void NAME(void) - -/* Start defining a test function. - * The arguments are not included, to allow parametric testing. */ -#define GREATEST_TEST static enum greatest_test_res - -/* PASS/FAIL/SKIP result from a test. Used internally. */ -typedef enum greatest_test_res { - GREATEST_TEST_RES_PASS = 0, - GREATEST_TEST_RES_FAIL = -1, - GREATEST_TEST_RES_SKIP = 1 -} greatest_test_res; - -/* Run a suite. */ -#define GREATEST_RUN_SUITE(S_NAME) greatest_run_suite(S_NAME, #S_NAME) - -/* Run a test in the current suite. */ -#define GREATEST_RUN_TEST(TEST) \ - do { \ - if (greatest_test_pre(#TEST) == 1) { \ - enum greatest_test_res res = GREATEST_SAVE_CONTEXT(); \ - if (res == GREATEST_TEST_RES_PASS) { \ - res = TEST(); \ - } \ - greatest_test_post(res); \ - } \ - } while (0) - -/* Ignore a test, don't warn about it being unused. */ -#define GREATEST_IGNORE_TEST(TEST) (void)TEST - -/* Run a test in the current suite with one void * argument, - * which can be a pointer to a struct with multiple arguments. */ -#define GREATEST_RUN_TEST1(TEST, ENV) \ - do { \ - if (greatest_test_pre(#TEST) == 1) { \ - enum greatest_test_res res = GREATEST_SAVE_CONTEXT(); \ - if (res == GREATEST_TEST_RES_PASS) { \ - res = TEST(ENV); \ - } \ - greatest_test_post(res); \ - } \ - } while (0) - -#ifdef GREATEST_VA_ARGS -#define GREATEST_RUN_TESTp(TEST, ...) \ - do { \ - if (greatest_test_pre(#TEST) == 1) { \ - enum greatest_test_res res = GREATEST_SAVE_CONTEXT(); \ - if (res == GREATEST_TEST_RES_PASS) { \ - res = TEST(__VA_ARGS__); \ - } \ - greatest_test_post(res); \ - } \ - } while (0) -#endif - - -/* Check if the test runner is in verbose mode. */ -#define GREATEST_IS_VERBOSE() ((greatest_info.verbosity) > 0) -#define GREATEST_LIST_ONLY() \ - (greatest_info.flags & GREATEST_FLAG_LIST_ONLY) -#define GREATEST_FIRST_FAIL() \ - (greatest_info.flags & GREATEST_FLAG_FIRST_FAIL) -#define GREATEST_ABORT_ON_FAIL() \ - (greatest_info.flags & GREATEST_FLAG_ABORT_ON_FAIL) -#define GREATEST_FAILURE_ABORT() \ - (GREATEST_FIRST_FAIL() && \ - (greatest_info.suite.failed > 0 || greatest_info.failed > 0)) - -/* Message-less forms of tests defined below. */ -#define GREATEST_PASS() GREATEST_PASSm(NULL) -#define GREATEST_FAIL() GREATEST_FAILm(NULL) -#define GREATEST_SKIP() GREATEST_SKIPm(NULL) -#define GREATEST_ASSERT(COND) \ - GREATEST_ASSERTm(#COND, COND) -#define GREATEST_ASSERT_OR_LONGJMP(COND) \ - GREATEST_ASSERT_OR_LONGJMPm(#COND, COND) -#define GREATEST_ASSERT_FALSE(COND) \ - GREATEST_ASSERT_FALSEm(#COND, COND) -#define GREATEST_ASSERT_EQ(EXP, GOT) \ - GREATEST_ASSERT_EQm(#EXP " != " #GOT, EXP, GOT) -#define GREATEST_ASSERT_NEQ(EXP, GOT) \ - GREATEST_ASSERT_NEQm(#EXP " == " #GOT, EXP, GOT) -#define GREATEST_ASSERT_GT(EXP, GOT) \ - GREATEST_ASSERT_GTm(#EXP " <= " #GOT, EXP, GOT) -#define GREATEST_ASSERT_GTE(EXP, GOT) \ - GREATEST_ASSERT_GTEm(#EXP " < " #GOT, EXP, GOT) -#define GREATEST_ASSERT_LT(EXP, GOT) \ - GREATEST_ASSERT_LTm(#EXP " >= " #GOT, EXP, GOT) -#define GREATEST_ASSERT_LTE(EXP, GOT) \ - GREATEST_ASSERT_LTEm(#EXP " > " #GOT, EXP, GOT) -#define GREATEST_ASSERT_EQ_FMT(EXP, GOT, FMT) \ - GREATEST_ASSERT_EQ_FMTm(#EXP " != " #GOT, EXP, GOT, FMT) -#define GREATEST_ASSERT_IN_RANGE(EXP, GOT, TOL) \ - GREATEST_ASSERT_IN_RANGEm(#EXP " != " #GOT " +/- " #TOL, EXP, GOT, TOL) -#define GREATEST_ASSERT_EQUAL_T(EXP, GOT, TYPE_INFO, UDATA) \ - GREATEST_ASSERT_EQUAL_Tm(#EXP " != " #GOT, EXP, GOT, TYPE_INFO, UDATA) -#define GREATEST_ASSERT_STR_EQ(EXP, GOT) \ - GREATEST_ASSERT_STR_EQm(#EXP " != " #GOT, EXP, GOT) -#define GREATEST_ASSERT_STRN_EQ(EXP, GOT, SIZE) \ - GREATEST_ASSERT_STRN_EQm(#EXP " != " #GOT, EXP, GOT, SIZE) -#define GREATEST_ASSERT_MEM_EQ(EXP, GOT, SIZE) \ - GREATEST_ASSERT_MEM_EQm(#EXP " != " #GOT, EXP, GOT, SIZE) -#define GREATEST_ASSERT_ENUM_EQ(EXP, GOT, ENUM_STR) \ - GREATEST_ASSERT_ENUM_EQm(#EXP " != " #GOT, EXP, GOT, ENUM_STR) - -/* The following forms take an additional message argument first, - * to be displayed by the test runner. */ - -/* Fail if a condition is not true, with message. */ -#define GREATEST_ASSERTm(MSG, COND) \ - do { \ - greatest_info.assertions++; \ - if (!(COND)) { GREATEST_FAILm(MSG); } \ - } while (0) - -/* Fail if a condition is not true, longjmping out of test. */ -#define GREATEST_ASSERT_OR_LONGJMPm(MSG, COND) \ - do { \ - greatest_info.assertions++; \ - if (!(COND)) { GREATEST_FAIL_WITH_LONGJMPm(MSG); } \ - } while (0) - -/* Fail if a condition is not false, with message. */ -#define GREATEST_ASSERT_FALSEm(MSG, COND) \ - do { \ - greatest_info.assertions++; \ - if ((COND)) { GREATEST_FAILm(MSG); } \ - } while (0) - -/* Internal macro for relational assertions */ -#define GREATEST__REL(REL, MSG, EXP, GOT) \ - do { \ - greatest_info.assertions++; \ - if (!((EXP) REL (GOT))) { GREATEST_FAILm(MSG); } \ - } while (0) - -/* Fail if EXP is not ==, !=, >, <, >=, or <= to GOT. */ -#define GREATEST_ASSERT_EQm(MSG,E,G) GREATEST__REL(==, MSG,E,G) -#define GREATEST_ASSERT_NEQm(MSG,E,G) GREATEST__REL(!=, MSG,E,G) -#define GREATEST_ASSERT_GTm(MSG,E,G) GREATEST__REL(>, MSG,E,G) -#define GREATEST_ASSERT_GTEm(MSG,E,G) GREATEST__REL(>=, MSG,E,G) -#define GREATEST_ASSERT_LTm(MSG,E,G) GREATEST__REL(<, MSG,E,G) -#define GREATEST_ASSERT_LTEm(MSG,E,G) GREATEST__REL(<=, MSG,E,G) - -/* Fail if EXP != GOT (equality comparison by ==). - * Warning: FMT, EXP, and GOT will be evaluated more - * than once on failure. */ -#define GREATEST_ASSERT_EQ_FMTm(MSG, EXP, GOT, FMT) \ - do { \ - greatest_info.assertions++; \ - if ((EXP) != (GOT)) { \ - GREATEST_FPRINTF(GREATEST_STDOUT, "\nExpected: "); \ - GREATEST_FPRINTF(GREATEST_STDOUT, FMT, EXP); \ - GREATEST_FPRINTF(GREATEST_STDOUT, "\n Got: "); \ - GREATEST_FPRINTF(GREATEST_STDOUT, FMT, GOT); \ - GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ - GREATEST_FAILm(MSG); \ - } \ - } while (0) - -/* Fail if EXP is not equal to GOT, printing enum IDs. */ -#define GREATEST_ASSERT_ENUM_EQm(MSG, EXP, GOT, ENUM_STR) \ - do { \ - int greatest_EXP = (int)(EXP); \ - int greatest_GOT = (int)(GOT); \ - greatest_enum_str_fun *greatest_ENUM_STR = ENUM_STR; \ - if (greatest_EXP != greatest_GOT) { \ - GREATEST_FPRINTF(GREATEST_STDOUT, "\nExpected: %s", \ - greatest_ENUM_STR(greatest_EXP)); \ - GREATEST_FPRINTF(GREATEST_STDOUT, "\n Got: %s\n", \ - greatest_ENUM_STR(greatest_GOT)); \ - GREATEST_FAILm(MSG); \ - } \ - } while (0) \ - -/* Fail if GOT not in range of EXP +|- TOL. */ -#define GREATEST_ASSERT_IN_RANGEm(MSG, EXP, GOT, TOL) \ - do { \ - GREATEST_FLOAT greatest_EXP = (EXP); \ - GREATEST_FLOAT greatest_GOT = (GOT); \ - GREATEST_FLOAT greatest_TOL = (TOL); \ - greatest_info.assertions++; \ - if ((greatest_EXP > greatest_GOT && \ - greatest_EXP - greatest_GOT > greatest_TOL) || \ - (greatest_EXP < greatest_GOT && \ - greatest_GOT - greatest_EXP > greatest_TOL)) { \ - GREATEST_FPRINTF(GREATEST_STDOUT, \ - "\nExpected: " GREATEST_FLOAT_FMT \ - " +/- " GREATEST_FLOAT_FMT \ - "\n Got: " GREATEST_FLOAT_FMT \ - "\n", \ - greatest_EXP, greatest_TOL, greatest_GOT); \ - GREATEST_FAILm(MSG); \ - } \ - } while (0) - -/* Fail if EXP is not equal to GOT, according to strcmp. */ -#define GREATEST_ASSERT_STR_EQm(MSG, EXP, GOT) \ - do { \ - GREATEST_ASSERT_EQUAL_Tm(MSG, EXP, GOT, \ - &greatest_type_info_string, NULL); \ - } while (0) \ - -/* Fail if EXP is not equal to GOT, according to strncmp. */ -#define GREATEST_ASSERT_STRN_EQm(MSG, EXP, GOT, SIZE) \ - do { \ - size_t size = SIZE; \ - GREATEST_ASSERT_EQUAL_Tm(MSG, EXP, GOT, \ - &greatest_type_info_string, &size); \ - } while (0) \ - -/* Fail if EXP is not equal to GOT, according to memcmp. */ -#define GREATEST_ASSERT_MEM_EQm(MSG, EXP, GOT, SIZE) \ - do { \ - greatest_memory_cmp_env env; \ - env.exp = (const unsigned char *)EXP; \ - env.got = (const unsigned char *)GOT; \ - env.size = SIZE; \ - GREATEST_ASSERT_EQUAL_Tm(MSG, env.exp, env.got, \ - &greatest_type_info_memory, &env); \ - } while (0) \ - -/* Fail if EXP is not equal to GOT, according to a comparison - * callback in TYPE_INFO. If they are not equal, optionally use a - * print callback in TYPE_INFO to print them. */ -#define GREATEST_ASSERT_EQUAL_Tm(MSG, EXP, GOT, TYPE_INFO, UDATA) \ - do { \ - greatest_type_info *type_info = (TYPE_INFO); \ - greatest_info.assertions++; \ - if (!greatest_do_assert_equal_t(EXP, GOT, \ - type_info, UDATA)) { \ - if (type_info == NULL || type_info->equal == NULL) { \ - GREATEST_FAILm("type_info->equal callback missing!"); \ - } else { \ - GREATEST_FAILm(MSG); \ - } \ - } \ - } while (0) \ - -/* Pass. */ -#define GREATEST_PASSm(MSG) \ - do { \ - greatest_info.msg = MSG; \ - return GREATEST_TEST_RES_PASS; \ - } while (0) - -/* Fail. */ -#define GREATEST_FAILm(MSG) \ - do { \ - greatest_info.fail_file = __FILE__; \ - greatest_info.fail_line = __LINE__; \ - greatest_info.msg = MSG; \ - if (GREATEST_ABORT_ON_FAIL()) { abort(); } \ - return GREATEST_TEST_RES_FAIL; \ - } while (0) - -/* Optional GREATEST_FAILm variant that longjmps. */ -#if GREATEST_USE_LONGJMP -#define GREATEST_FAIL_WITH_LONGJMP() GREATEST_FAIL_WITH_LONGJMPm(NULL) -#define GREATEST_FAIL_WITH_LONGJMPm(MSG) \ - do { \ - greatest_info.fail_file = __FILE__; \ - greatest_info.fail_line = __LINE__; \ - greatest_info.msg = MSG; \ - longjmp(greatest_info.jump_dest, GREATEST_TEST_RES_FAIL); \ - } while (0) -#endif - -/* Skip the current test. */ -#define GREATEST_SKIPm(MSG) \ - do { \ - greatest_info.msg = MSG; \ - return GREATEST_TEST_RES_SKIP; \ - } while (0) - -/* Check the result of a subfunction using ASSERT, etc. */ -#define GREATEST_CHECK_CALL(RES) \ - do { \ - enum greatest_test_res greatest_RES = RES; \ - if (greatest_RES != GREATEST_TEST_RES_PASS) { \ - return greatest_RES; \ - } \ - } while (0) \ - -#if GREATEST_USE_TIME -#define GREATEST_SET_TIME(NAME) \ - NAME = clock(); \ - if (NAME == (clock_t) -1) { \ - GREATEST_FPRINTF(GREATEST_STDOUT, \ - "clock error: %s\n", #NAME); \ - exit(EXIT_FAILURE); \ - } - -#define GREATEST_CLOCK_DIFF(C1, C2) \ - GREATEST_FPRINTF(GREATEST_STDOUT, " (%lu ticks, %.3f sec)", \ - (long unsigned int) (C2) - (long unsigned int)(C1), \ - (double)((C2) - (C1)) / (1.0 * (double)CLOCKS_PER_SEC)) -#else -#define GREATEST_SET_TIME(UNUSED) -#define GREATEST_CLOCK_DIFF(UNUSED1, UNUSED2) -#endif - -#if GREATEST_USE_LONGJMP -#define GREATEST_SAVE_CONTEXT() \ - /* setjmp returns 0 (GREATEST_TEST_RES_PASS) on first call * \ - * so the test runs, then RES_FAIL from FAIL_WITH_LONGJMP. */ \ - ((enum greatest_test_res)(setjmp(greatest_info.jump_dest))) -#else -#define GREATEST_SAVE_CONTEXT() \ - /*a no-op, since setjmp/longjmp aren't being used */ \ - GREATEST_TEST_RES_PASS -#endif - -/* Run every suite / test function run within BODY in pseudo-random - * order, seeded by SEED. (The top 3 bits of the seed are ignored.) - * - * This should be called like: - * GREATEST_SHUFFLE_TESTS(seed, { - * GREATEST_RUN_TEST(some_test); - * GREATEST_RUN_TEST(some_other_test); - * GREATEST_RUN_TEST(yet_another_test); - * }); - * - * Note that the body of the second argument will be evaluated - * multiple times. */ -#define GREATEST_SHUFFLE_SUITES(SD, BODY) GREATEST_SHUFFLE(0, SD, BODY) -#define GREATEST_SHUFFLE_TESTS(SD, BODY) GREATEST_SHUFFLE(1, SD, BODY) -#define GREATEST_SHUFFLE(ID, SD, BODY) \ - do { \ - struct greatest_prng *prng = &greatest_info.prng[ID]; \ - greatest_prng_init_first_pass(ID); \ - do { \ - prng->count = 0; \ - if (prng->initialized) { greatest_prng_step(ID); } \ - BODY; \ - if (!prng->initialized) { \ - if (!greatest_prng_init_second_pass(ID, SD)) { break; } \ - } else if (prng->count_run == prng->count_ceil) { \ - break; \ - } \ - } while (!GREATEST_FAILURE_ABORT()); \ - prng->count_run = prng->random_order = prng->initialized = 0; \ - } while(0) - -/* Include several function definitions in the main test file. */ -#define GREATEST_MAIN_DEFS() \ - \ -/* Is FILTER a subset of NAME? */ \ -static int greatest_name_match(const char *name, const char *filter, \ - int res_if_none) { \ - size_t offset = 0; \ - size_t filter_len = filter ? strlen(filter) : 0; \ - if (filter_len == 0) { return res_if_none; } /* no filter */ \ - if (greatest_info.exact_name_match && strlen(name) != filter_len) { \ - return 0; /* ignore substring matches */ \ - } \ - while (name[offset] != '\0') { \ - if (name[offset] == filter[0]) { \ - if (0 == strncmp(&name[offset], filter, filter_len)) { \ - return 1; \ - } \ - } \ - offset++; \ - } \ - \ - return 0; \ -} \ - \ -static void greatest_buffer_test_name(const char *name) { \ - struct greatest_run_info *g = &greatest_info; \ - size_t len = strlen(name), size = sizeof(g->name_buf); \ - memset(g->name_buf, 0x00, size); \ - (void)strncat(g->name_buf, name, size - 1); \ - if (g->name_suffix && (len + 1 < size)) { \ - g->name_buf[len] = '_'; \ - strncat(&g->name_buf[len+1], g->name_suffix, size-(len+2)); \ - } \ -} \ - \ -/* Before running a test, check the name filtering and \ - * test shuffling state, if applicable, and then call setup hooks. */ \ -int greatest_test_pre(const char *name) { \ - struct greatest_run_info *g = &greatest_info; \ - int match; \ - greatest_buffer_test_name(name); \ - match = greatest_name_match(g->name_buf, g->test_filter, 1) && \ - !greatest_name_match(g->name_buf, g->test_exclude, 0); \ - if (GREATEST_LIST_ONLY()) { /* just listing test names */ \ - if (match) { \ - GREATEST_FPRINTF(GREATEST_STDOUT, " %s\n", g->name_buf); \ - } \ - goto clear; \ - } \ - if (match && (!GREATEST_FIRST_FAIL() || g->suite.failed == 0)) { \ - struct greatest_prng *p = &g->prng[1]; \ - if (p->random_order) { \ - p->count++; \ - if (!p->initialized || ((p->count - 1) != p->state)) { \ - goto clear; /* don't run this test yet */ \ - } \ - } \ - if (g->running_test) { \ - fprintf(stderr, "Error: Test run inside another test.\n"); \ - return 0; \ - } \ - GREATEST_SET_TIME(g->suite.pre_test); \ - if (g->setup) { g->setup(g->setup_udata); } \ - p->count_run++; \ - g->running_test = 1; \ - return 1; /* test should be run */ \ - } else { \ - goto clear; /* skipped */ \ - } \ -clear: \ - g->name_suffix = NULL; \ - return 0; \ -} \ - \ -static void greatest_do_pass(void) { \ - struct greatest_run_info *g = &greatest_info; \ - if (GREATEST_IS_VERBOSE()) { \ - GREATEST_FPRINTF(GREATEST_STDOUT, "PASS %s: %s", \ - g->name_buf, g->msg ? g->msg : ""); \ - } else { \ - GREATEST_FPRINTF(GREATEST_STDOUT, "."); \ - } \ - g->suite.passed++; \ -} \ - \ -static void greatest_do_fail(void) { \ - struct greatest_run_info *g = &greatest_info; \ - if (GREATEST_IS_VERBOSE()) { \ - GREATEST_FPRINTF(GREATEST_STDOUT, \ - "FAIL %s: %s (%s:%u)", g->name_buf, \ - g->msg ? g->msg : "", g->fail_file, g->fail_line); \ - } else { \ - GREATEST_FPRINTF(GREATEST_STDOUT, "F"); \ - g->col++; /* add linebreak if in line of '.'s */ \ - if (g->col != 0) { \ - GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ - g->col = 0; \ - } \ - GREATEST_FPRINTF(GREATEST_STDOUT, "FAIL %s: %s (%s:%u)\n", \ - g->name_buf, g->msg ? g->msg : "", \ - g->fail_file, g->fail_line); \ - } \ - g->suite.failed++; \ -} \ - \ -static void greatest_do_skip(void) { \ - struct greatest_run_info *g = &greatest_info; \ - if (GREATEST_IS_VERBOSE()) { \ - GREATEST_FPRINTF(GREATEST_STDOUT, "SKIP %s: %s", \ - g->name_buf, g->msg ? g->msg : ""); \ - } else { \ - GREATEST_FPRINTF(GREATEST_STDOUT, "s"); \ - } \ - g->suite.skipped++; \ -} \ - \ -void greatest_test_post(int res) { \ - GREATEST_SET_TIME(greatest_info.suite.post_test); \ - if (greatest_info.teardown) { \ - void *udata = greatest_info.teardown_udata; \ - greatest_info.teardown(udata); \ - } \ - \ - greatest_info.running_test = 0; \ - if (res <= GREATEST_TEST_RES_FAIL) { \ - greatest_do_fail(); \ - } else if (res >= GREATEST_TEST_RES_SKIP) { \ - greatest_do_skip(); \ - } else if (res == GREATEST_TEST_RES_PASS) { \ - greatest_do_pass(); \ - } \ - greatest_info.name_suffix = NULL; \ - greatest_info.suite.tests_run++; \ - greatest_info.col++; \ - if (GREATEST_IS_VERBOSE()) { \ - GREATEST_CLOCK_DIFF(greatest_info.suite.pre_test, \ - greatest_info.suite.post_test); \ - GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ - } else if (greatest_info.col % greatest_info.width == 0) { \ - GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ - greatest_info.col = 0; \ - } \ - fflush(GREATEST_STDOUT); \ -} \ - \ -static void report_suite(void) { \ - if (greatest_info.suite.tests_run > 0) { \ - GREATEST_FPRINTF(GREATEST_STDOUT, \ - "\n%u test%s - %u passed, %u failed, %u skipped", \ - greatest_info.suite.tests_run, \ - greatest_info.suite.tests_run == 1 ? "" : "s", \ - greatest_info.suite.passed, \ - greatest_info.suite.failed, \ - greatest_info.suite.skipped); \ - GREATEST_CLOCK_DIFF(greatest_info.suite.pre_suite, \ - greatest_info.suite.post_suite); \ - GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ - } \ -} \ - \ -static void update_counts_and_reset_suite(void) { \ - greatest_info.setup = NULL; \ - greatest_info.setup_udata = NULL; \ - greatest_info.teardown = NULL; \ - greatest_info.teardown_udata = NULL; \ - greatest_info.passed += greatest_info.suite.passed; \ - greatest_info.failed += greatest_info.suite.failed; \ - greatest_info.skipped += greatest_info.suite.skipped; \ - greatest_info.tests_run += greatest_info.suite.tests_run; \ - memset(&greatest_info.suite, 0, sizeof(greatest_info.suite)); \ - greatest_info.col = 0; \ -} \ - \ -static int greatest_suite_pre(const char *suite_name) { \ - struct greatest_prng *p = &greatest_info.prng[0]; \ - if (!greatest_name_match(suite_name, greatest_info.suite_filter, 1) \ - || (GREATEST_FAILURE_ABORT())) { return 0; } \ - if (p->random_order) { \ - p->count++; \ - if (!p->initialized || ((p->count - 1) != p->state)) { \ - return 0; /* don't run this suite yet */ \ - } \ - } \ - p->count_run++; \ - update_counts_and_reset_suite(); \ - GREATEST_FPRINTF(GREATEST_STDOUT, "\n* Suite %s:\n", suite_name); \ - GREATEST_SET_TIME(greatest_info.suite.pre_suite); \ - return 1; \ -} \ - \ -static void greatest_suite_post(void) { \ - GREATEST_SET_TIME(greatest_info.suite.post_suite); \ - report_suite(); \ -} \ - \ -static void greatest_run_suite(greatest_suite_cb *suite_cb, \ - const char *suite_name) { \ - if (greatest_suite_pre(suite_name)) { \ - suite_cb(); \ - greatest_suite_post(); \ - } \ -} \ - \ -int greatest_do_assert_equal_t(const void *expd, const void *got, \ - greatest_type_info *type_info, void *udata) { \ - int eq = 0; \ - if (type_info == NULL || type_info->equal == NULL) { return 0; } \ - eq = type_info->equal(expd, got, udata); \ - if (!eq) { \ - if (type_info->print != NULL) { \ - GREATEST_FPRINTF(GREATEST_STDOUT, "\nExpected: "); \ - (void)type_info->print(expd, udata); \ - GREATEST_FPRINTF(GREATEST_STDOUT, "\n Got: "); \ - (void)type_info->print(got, udata); \ - GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ - } \ - } \ - return eq; \ -} \ - \ -static void greatest_usage(const char *name) { \ - GREATEST_FPRINTF(GREATEST_STDOUT, \ - "Usage: %s [-hlfavex] [-s SUITE] [-t TEST] [-x EXCLUDE]\n" \ - " -h, --help print this Help\n" \ - " -l List suites and tests, then exit (dry run)\n" \ - " -f Stop runner after first failure\n" \ - " -a Abort on first failure (implies -f)\n" \ - " -v Verbose output\n" \ - " -s SUITE only run suites containing substring SUITE\n" \ - " -t TEST only run tests containing substring TEST\n" \ - " -e only run exact name match for -s or -t\n" \ - " -x EXCLUDE exclude tests containing substring EXCLUDE\n", \ - name); \ -} \ - \ -static void greatest_parse_options(int argc, char **argv) { \ - int i = 0; \ - for (i = 1; i < argc; i++) { \ - if (argv[i][0] == '-') { \ - char f = argv[i][1]; \ - if ((f == 's' || f == 't' || f == 'x') && argc <= i + 1) { \ - greatest_usage(argv[0]); exit(EXIT_FAILURE); \ - } \ - switch (f) { \ - case 's': /* suite name filter */ \ - greatest_set_suite_filter(argv[i + 1]); i++; break; \ - case 't': /* test name filter */ \ - greatest_set_test_filter(argv[i + 1]); i++; break; \ - case 'x': /* test name exclusion */ \ - greatest_set_test_exclude(argv[i + 1]); i++; break; \ - case 'e': /* exact name match */ \ - greatest_set_exact_name_match(); break; \ - case 'f': /* first fail flag */ \ - greatest_stop_at_first_fail(); break; \ - case 'a': /* abort() on fail flag */ \ - greatest_abort_on_fail(); break; \ - case 'l': /* list only (dry run) */ \ - greatest_list_only(); break; \ - case 'v': /* first fail flag */ \ - greatest_info.verbosity++; break; \ - case 'h': /* help */ \ - greatest_usage(argv[0]); exit(EXIT_SUCCESS); \ - default: \ - case '-': \ - if (0 == strncmp("--help", argv[i], 6)) { \ - greatest_usage(argv[0]); exit(EXIT_SUCCESS); \ - } else if (0 == strcmp("--", argv[i])) { \ - return; /* ignore following arguments */ \ - } \ - GREATEST_FPRINTF(GREATEST_STDOUT, \ - "Unknown argument '%s'\n", argv[i]); \ - greatest_usage(argv[0]); \ - exit(EXIT_FAILURE); \ - } \ - } \ - } \ -} \ - \ -int greatest_all_passed(void) { return (greatest_info.failed == 0); } \ - \ -void greatest_set_test_filter(const char *filter) { \ - greatest_info.test_filter = filter; \ -} \ - \ -void greatest_set_test_exclude(const char *filter) { \ - greatest_info.test_exclude = filter; \ -} \ - \ -void greatest_set_suite_filter(const char *filter) { \ - greatest_info.suite_filter = filter; \ -} \ - \ -void greatest_set_exact_name_match(void) { \ - greatest_info.exact_name_match = 1; \ -} \ - \ -void greatest_stop_at_first_fail(void) { \ - greatest_set_flag(GREATEST_FLAG_FIRST_FAIL); \ -} \ - \ -void greatest_abort_on_fail(void) { \ - greatest_set_flag(GREATEST_FLAG_ABORT_ON_FAIL); \ -} \ - \ -void greatest_list_only(void) { \ - greatest_set_flag(GREATEST_FLAG_LIST_ONLY); \ -} \ - \ -void greatest_get_report(struct greatest_report_t *report) { \ - if (report) { \ - report->passed = greatest_info.passed; \ - report->failed = greatest_info.failed; \ - report->skipped = greatest_info.skipped; \ - report->assertions = greatest_info.assertions; \ - } \ -} \ - \ -unsigned int greatest_get_verbosity(void) { \ - return greatest_info.verbosity; \ -} \ - \ -void greatest_set_verbosity(unsigned int verbosity) { \ - greatest_info.verbosity = (unsigned char)verbosity; \ -} \ - \ -void greatest_set_flag(greatest_flag_t flag) { \ - greatest_info.flags = (unsigned char)(greatest_info.flags | flag); \ -} \ - \ -void greatest_set_test_suffix(const char *suffix) { \ - greatest_info.name_suffix = suffix; \ -} \ - \ -void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata) { \ - greatest_info.setup = cb; \ - greatest_info.setup_udata = udata; \ -} \ - \ -void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, void *udata) { \ - greatest_info.teardown = cb; \ - greatest_info.teardown_udata = udata; \ -} \ - \ -static int greatest_string_equal_cb(const void *expd, const void *got, \ - void *udata) { \ - size_t *size = (size_t *)udata; \ - return (size != NULL \ - ? (0 == strncmp((const char *)expd, (const char *)got, *size)) \ - : (0 == strcmp((const char *)expd, (const char *)got))); \ -} \ - \ -static int greatest_string_printf_cb(const void *t, void *udata) { \ - (void)udata; /* note: does not check \0 termination. */ \ - return GREATEST_FPRINTF(GREATEST_STDOUT, "%s", (const char *)t); \ -} \ - \ -greatest_type_info greatest_type_info_string = { \ - greatest_string_equal_cb, greatest_string_printf_cb, \ -}; \ - \ -static int greatest_memory_equal_cb(const void *expd, const void *got, \ - void *udata) { \ - greatest_memory_cmp_env *env = (greatest_memory_cmp_env *)udata; \ - return (0 == memcmp(expd, got, env->size)); \ -} \ - \ -/* Hexdump raw memory, with differences highlighted */ \ -static int greatest_memory_printf_cb(const void *t, void *udata) { \ - greatest_memory_cmp_env *env = (greatest_memory_cmp_env *)udata; \ - const unsigned char *buf = (const unsigned char *)t; \ - unsigned char diff_mark = ' '; \ - FILE *out = GREATEST_STDOUT; \ - size_t i, line_i, line_len = 0; \ - int len = 0; /* format hexdump with differences highlighted */ \ - for (i = 0; i < env->size; i+= line_len) { \ - diff_mark = ' '; \ - line_len = env->size - i; \ - if (line_len > 16) { line_len = 16; } \ - for (line_i = i; line_i < i + line_len; line_i++) { \ - if (env->exp[line_i] != env->got[line_i]) diff_mark = 'X'; \ - } \ - len += GREATEST_FPRINTF(out, "\n%04x %c ", \ - (unsigned int)i, diff_mark); \ - for (line_i = i; line_i < i + line_len; line_i++) { \ - int m = env->exp[line_i] == env->got[line_i]; /* match? */ \ - len += GREATEST_FPRINTF(out, "%02x%c", \ - buf[line_i], m ? ' ' : '<'); \ - } \ - for (line_i = 0; line_i < 16 - line_len; line_i++) { \ - len += GREATEST_FPRINTF(out, " "); \ - } \ - GREATEST_FPRINTF(out, " "); \ - for (line_i = i; line_i < i + line_len; line_i++) { \ - unsigned char c = buf[line_i]; \ - len += GREATEST_FPRINTF(out, "%c", isprint(c) ? c : '.'); \ - } \ - } \ - len += GREATEST_FPRINTF(out, "\n"); \ - return len; \ -} \ - \ -void greatest_prng_init_first_pass(int id) { \ - greatest_info.prng[id].random_order = 1; \ - greatest_info.prng[id].count_run = 0; \ -} \ - \ -int greatest_prng_init_second_pass(int id, unsigned long seed) { \ - struct greatest_prng *p = &greatest_info.prng[id]; \ - if (p->count == 0) { return 0; } \ - p->count_ceil = p->count; \ - for (p->m = 1; p->m < p->count; p->m <<= 1) {} \ - p->state = seed & 0x1fffffff; /* only use lower 29 bits */ \ - p->a = 4LU * p->state; /* to avoid overflow when */ \ - p->a = (p->a ? p->a : 4) | 1; /* multiplied by 4 */ \ - p->c = 2147483647; /* and so p->c ((2 ** 31) - 1) is */ \ - p->initialized = 1; /* always relatively prime to p->a. */ \ - fprintf(stderr, "init_second_pass: a %lu, c %lu, state %lu\n", \ - p->a, p->c, p->state); \ - return 1; \ -} \ - \ -/* Step the pseudorandom number generator until its state reaches \ - * another test ID between 0 and the test count. \ - * This use a linear congruential pseudorandom number generator, \ - * with the power-of-two ceiling of the test count as the modulus, the \ - * masked seed as the multiplier, and a prime as the increment. For \ - * each generated value < the test count, run the corresponding test. \ - * This will visit all IDs 0 <= X < mod once before repeating, \ - * with a starting position chosen based on the initial seed. \ - * For details, see: Knuth, The Art of Computer Programming \ - * Volume. 2, section 3.2.1. */ \ -void greatest_prng_step(int id) { \ - struct greatest_prng *p = &greatest_info.prng[id]; \ - do { \ - p->state = ((p->a * p->state) + p->c) & (p->m - 1); \ - } while (p->state >= p->count_ceil); \ -} \ - \ -void GREATEST_INIT(void) { \ - /* Suppress unused function warning if features aren't used */ \ - (void)greatest_run_suite; \ - (void)greatest_parse_options; \ - (void)greatest_prng_step; \ - (void)greatest_prng_init_first_pass; \ - (void)greatest_prng_init_second_pass; \ - (void)greatest_set_test_suffix; \ - \ - memset(&greatest_info, 0, sizeof(greatest_info)); \ - greatest_info.width = GREATEST_DEFAULT_WIDTH; \ - GREATEST_SET_TIME(greatest_info.begin); \ -} \ - \ -/* Report passes, failures, skipped tests, the number of \ - * assertions, and the overall run time. */ \ -void GREATEST_PRINT_REPORT(void) { \ - if (!GREATEST_LIST_ONLY()) { \ - update_counts_and_reset_suite(); \ - GREATEST_SET_TIME(greatest_info.end); \ - GREATEST_FPRINTF(GREATEST_STDOUT, \ - "\nTotal: %u test%s", \ - greatest_info.tests_run, \ - greatest_info.tests_run == 1 ? "" : "s"); \ - GREATEST_CLOCK_DIFF(greatest_info.begin, \ - greatest_info.end); \ - GREATEST_FPRINTF(GREATEST_STDOUT, ", %u assertion%s\n", \ - greatest_info.assertions, \ - greatest_info.assertions == 1 ? "" : "s"); \ - GREATEST_FPRINTF(GREATEST_STDOUT, \ - "Pass: %u, fail: %u, skip: %u.\n", \ - greatest_info.passed, \ - greatest_info.failed, greatest_info.skipped); \ - } \ -} \ - \ -greatest_type_info greatest_type_info_memory = { \ - greatest_memory_equal_cb, greatest_memory_printf_cb, \ -}; \ - \ -greatest_run_info greatest_info - -/* Handle command-line arguments, etc. */ -#define GREATEST_MAIN_BEGIN() \ - do { \ - GREATEST_INIT(); \ - greatest_parse_options(argc, argv); \ - } while (0) - -/* Report results, exit with exit status based on results. */ -#define GREATEST_MAIN_END() \ - do { \ - GREATEST_PRINT_REPORT(); \ - return (greatest_all_passed() ? EXIT_SUCCESS : EXIT_FAILURE); \ - } while (0) - -/* Make abbreviations without the GREATEST_ prefix for the - * most commonly used symbols. */ -#if GREATEST_USE_ABBREVS -#define TEST GREATEST_TEST -#define SUITE GREATEST_SUITE -#define SUITE_EXTERN GREATEST_SUITE_EXTERN -#define RUN_TEST GREATEST_RUN_TEST -#define RUN_TEST1 GREATEST_RUN_TEST1 -#define RUN_SUITE GREATEST_RUN_SUITE -#define IGNORE_TEST GREATEST_IGNORE_TEST -#define ASSERT GREATEST_ASSERT -#define ASSERTm GREATEST_ASSERTm -#define ASSERT_FALSE GREATEST_ASSERT_FALSE -#define ASSERT_EQ GREATEST_ASSERT_EQ -#define ASSERT_NEQ GREATEST_ASSERT_NEQ -#define ASSERT_GT GREATEST_ASSERT_GT -#define ASSERT_GTE GREATEST_ASSERT_GTE -#define ASSERT_LT GREATEST_ASSERT_LT -#define ASSERT_LTE GREATEST_ASSERT_LTE -#define ASSERT_EQ_FMT GREATEST_ASSERT_EQ_FMT -#define ASSERT_IN_RANGE GREATEST_ASSERT_IN_RANGE -#define ASSERT_EQUAL_T GREATEST_ASSERT_EQUAL_T -#define ASSERT_STR_EQ GREATEST_ASSERT_STR_EQ -#define ASSERT_STRN_EQ GREATEST_ASSERT_STRN_EQ -#define ASSERT_MEM_EQ GREATEST_ASSERT_MEM_EQ -#define ASSERT_ENUM_EQ GREATEST_ASSERT_ENUM_EQ -#define ASSERT_FALSEm GREATEST_ASSERT_FALSEm -#define ASSERT_EQm GREATEST_ASSERT_EQm -#define ASSERT_NEQm GREATEST_ASSERT_NEQm -#define ASSERT_GTm GREATEST_ASSERT_GTm -#define ASSERT_GTEm GREATEST_ASSERT_GTEm -#define ASSERT_LTm GREATEST_ASSERT_LTm -#define ASSERT_LTEm GREATEST_ASSERT_LTEm -#define ASSERT_EQ_FMTm GREATEST_ASSERT_EQ_FMTm -#define ASSERT_IN_RANGEm GREATEST_ASSERT_IN_RANGEm -#define ASSERT_EQUAL_Tm GREATEST_ASSERT_EQUAL_Tm -#define ASSERT_STR_EQm GREATEST_ASSERT_STR_EQm -#define ASSERT_STRN_EQm GREATEST_ASSERT_STRN_EQm -#define ASSERT_MEM_EQm GREATEST_ASSERT_MEM_EQm -#define ASSERT_ENUM_EQm GREATEST_ASSERT_ENUM_EQm -#define PASS GREATEST_PASS -#define FAIL GREATEST_FAIL -#define SKIP GREATEST_SKIP -#define PASSm GREATEST_PASSm -#define FAILm GREATEST_FAILm -#define SKIPm GREATEST_SKIPm -#define SET_SETUP GREATEST_SET_SETUP_CB -#define SET_TEARDOWN GREATEST_SET_TEARDOWN_CB -#define CHECK_CALL GREATEST_CHECK_CALL -#define SHUFFLE_TESTS GREATEST_SHUFFLE_TESTS -#define SHUFFLE_SUITES GREATEST_SHUFFLE_SUITES - -#ifdef GREATEST_VA_ARGS -#define RUN_TESTp GREATEST_RUN_TESTp -#endif - -#if GREATEST_USE_LONGJMP -#define ASSERT_OR_LONGJMP GREATEST_ASSERT_OR_LONGJMP -#define ASSERT_OR_LONGJMPm GREATEST_ASSERT_OR_LONGJMPm -#define FAIL_WITH_LONGJMP GREATEST_FAIL_WITH_LONGJMP -#define FAIL_WITH_LONGJMPm GREATEST_FAIL_WITH_LONGJMPm -#endif - -#endif /* USE_ABBREVS */ - -#if defined(__cplusplus) && !defined(GREATEST_NO_EXTERN_CPLUSPLUS) -} -#endif - -#endif \ No newline at end of file diff --git a/framework/3rd/minicoro/minicoro.h b/framework/3rd/minicoro/minicoro.h deleted file mode 100644 index 9403a2d..0000000 --- a/framework/3rd/minicoro/minicoro.h +++ /dev/null @@ -1,1789 +0,0 @@ -/* -Minimal asymmetric stackful cross-platform coroutine library in pure C. -minicoro - v0.1.2 - 13/Feb/2021 -Eduardo Bart - edub4rt@gmail.com -https://github.com/edubart/minicoro - -Minicoro is single file library for using asymmetric coroutines in C. -The API is inspired by Lua coroutines but with C use in mind. - -# Features - -- Stackful asymmetric coroutines. -- Supports nesting coroutines (resuming a coroutine from another coroutine). -- Supports custom allocators. -- Storage system to allow passing values between yield and resume. -- Customizable stack size. -- Coroutine API design inspired by Lua with C use in mind. -- Yield across any C function. -- Made to work in multithread applications. -- Cross platform. -- Minimal, self contained and no external dependencies. -- Readable sources and documented. -- Implemented via assembly, ucontext or fibers. -- Lightweight and very efficient. -- Works in most C89 compilers. -- Error prone API, returning proper error codes on misuse. -- Support running with Valgrind, ASan (AddressSanitizer) and TSan (ThreadSanitizer). - -# Supported Platforms - -Most platforms are supported through different methods: - -| Platform | Assembly Method | Fallback Method | -|--------------|------------------|-------------------| -| Android | ARM/ARM64 | N/A | -| Windows | x86_64 | Windows fibers | -| Linux | x86_64/i686 | ucontext | -| Mac OS X | x86_64 | ucontext | -| Browser | N/A | Emscripten fibers | -| Raspberry Pi | ARM | ucontext | -| RISC-V | rv64/rv32 | ucontext | - -The assembly method is used by default if supported by the compiler and CPU, -otherwise ucontext or fiber method is used as a fallback. - -The assembly method is very efficient, it just take a few cycles -to create, resume, yield or destroy a coroutine. - -# Caveats - -- Don't use coroutines with C++ exceptions, this is not supported. -- When using C++ RAII (i.e. destructors) you must resume the coroutine until it dies to properly execute all destructors. -- To use in multithread applications, you must compile with C compiler that supports `thread_local` qualifier. -- Some unsupported sanitizers for C may trigger false warnings when using coroutines. -- The `mco_coro` object is not thread safe, you should lock each coroutine into a thread. -- Stack space is fixed, it cannot grow. By default it has about 56KB of space, this can be changed on coroutine creation. -- Take care to not cause stack overflows (run out of stack space), otherwise your program may crash or not, the behavior is undefined. -- On WebAssembly you must compile with emscripten flag `-s ASYNCIFY=1`. - -# Introduction - -A coroutine represents an independent "green" thread of execution. -Unlike threads in multithread systems, however, -a coroutine only suspends its execution by explicitly calling a yield function. - -You create a coroutine by calling `mco_create`. -Its sole argument is a `mco_desc` structure with a description for the coroutine. -The `mco_create` function only creates a new coroutine and returns a handle to it, it does not start the coroutine. - -You execute a coroutine by calling `mco_resume`. -When calling a resume function the coroutine starts its execution by calling its body function. -After the coroutine starts running, it runs until it terminates or yields. - -A coroutine yields by calling `mco_yield`. -When a coroutine yields, the corresponding resume returns immediately, -even if the yield happens inside nested function calls (that is, not in the main function). -The next time you resume the same coroutine, it continues its execution from the point where it yielded. - -To associate a persistent value with the coroutine, -you can optionally set `user_data` on its creation and later retrieve with `mco_get_user_data`. - -To pass values between resume and yield, -you can optionally use `mco_push` and `mco_pop` APIs, -they are intended to pass temporary values using a LIFO style buffer. -The storage system can also be used to send and receive initial values on coroutine creation or before it finishes. - -# Usage - -To use minicoro, do the following in one .c file: - -```c -#define MINICORO_IMPL -#include "minicoro.h" -``` - -You can do `#include "minicoro.h"` in other parts of the program just like any other header. - -## Minimal Example - -The following simple example demonstrates on how to use the library: - -```c -#define MINICORO_IMPL -#include "minicoro.h" -#include - -// Coroutine entry function. -void coro_entry(mco_coro* co) { - printf("coroutine 1\n"); - mco_yield(co); - printf("coroutine 2\n"); -} - -int main() { - // First initialize a `desc` object through `mco_desc_init`. - mco_desc desc = mco_desc_init(coro_entry, 0); - // Configure `desc` fields when needed (e.g. customize user_data or allocation functions). - desc.user_data = NULL; - // Call `mco_create` with the output coroutine pointer and `desc` pointer. - mco_coro* co; - mco_result res = mco_create(&co, &desc); - assert(res == MCO_SUCCESS); - // The coroutine should be now in suspended state. - assert(mco_status(co) == MCO_SUSPENDED); - // Call `mco_resume` to start for the first time, switching to its context. - res = mco_resume(co); // Should print "coroutine 1". - assert(res == MCO_SUCCESS); - // We get back from coroutine context in suspended state (because it's unfinished). - assert(mco_status(co) == MCO_SUSPENDED); - // Call `mco_resume` to resume for a second time. - res = mco_resume(co); // Should print "coroutine 2". - assert(res == MCO_SUCCESS); - // The coroutine finished and should be now dead. - assert(mco_status(co) == MCO_DEAD); - // Call `mco_destroy` to destroy the coroutine. - res = mco_destroy(co); - assert(res == MCO_SUCCESS); - return 0; -} -``` - -_NOTE_: In case you don't want to use the minicoro allocator system you should -allocate a coroutine object yourself using `mco_desc.coro_size` and call `mco_init`, -then later to destroy call `mco_deinit` and deallocate it. - -## Yielding from anywhere - -You can yield the current running coroutine from anywhere -without having to pass `mco_coro` pointers around, -to this just use `mco_yield(mco_running())`. - -## Passing data between yield and resume - -The library has the storage interface to assist passing data between yield and resume. -It's usage is straightforward, -use `mco_push` to send data before a `mco_resume` or `mco_yield`, -then later use `mco_pop` after a `mco_resume` or `mco_yield` to receive data. -Take care to not mismatch a push and pop, otherwise these functions will return -an error. - -## Error handling - -The library return error codes in most of its API in case of misuse or system error, -the user is encouraged to handle them properly. - -## Library customization - -The following can be defined to change the library behavior: - -- `MCO_API` - Public API qualifier. Default is `extern`. -- `MCO_MIN_STACK_SIZE` - Minimum stack size when creating a coroutine. Default is 32768. -- `MCO_DEFAULT_STORAGE_SIZE` - Size of coroutine storage buffer. Default is 1024. -- `MCO_DEFAULT_STACK_SIZE` - Default stack size when creating a coroutine. Default is 57344. -- `MCO_MALLOC` - Default allocation function. Default is `malloc`. -- `MCO_FREE` - Default deallocation function. Default is `free`. -- `MCO_DEBUG` - Enable debug mode, logging any runtime error to stdout. Defined automatically unless `NDEBUG` or `MCO_NO_DEBUG` is defined. -- `MCO_NO_DEBUG` - Disable debug mode. -- `MCO_NO_MULTITHREAD` - Disable multithread usage. Multithread is supported when `thread_local` is supported. -- `MCO_NO_DEFAULT_ALLOCATORS` - Disable the default allocator using `MCO_MALLOC` and `MCO_FREE`. -- `MCO_ZERO_MEMORY` - Zero memory of stack for new coroutines and when poping storage, intended for garbage collected environments. -- `MCO_USE_ASM` - Force use of assembly context switch implementation. -- `MCO_USE_UCONTEXT` - Force use of ucontext context switch implementation. -- `MCO_USE_FIBERS` - Force use of fibers context switch implementation. -- `MCO_USE_VALGRIND` - Define if you want run with valgrind to fix accessing memory errors. - -# License - -Your choice of either Public Domain or MIT No Attribution, see end of file. -*/ - - -#ifndef MINICORO_H -#define MINICORO_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* Public API qualifier. */ -#ifndef MCO_API -#define MCO_API extern -#endif - -/* Size of coroutine storage buffer. */ -#ifndef MCO_DEFAULT_STORAGE_SIZE -#define MCO_DEFAULT_STORAGE_SIZE 1024 -#endif - -#include /* for size_t */ - -/* ---------------------------------------------------------------------------------------------- */ - -/* Coroutine states. */ -typedef enum mco_state { - MCO_DEAD = 0, /* The coroutine has finished normally or was uninitialized before finishing. */ - MCO_NORMAL, /* The coroutine is active but not running (that is, it has resumed another coroutine). */ - MCO_RUNNING, /* The coroutine is active and running. */ - MCO_SUSPENDED, /* The coroutine is suspended (in a call to yield, or it has not started running yet). */ -} mco_state; - -/* Coroutine result codes. */ -typedef enum mco_result { - MCO_SUCCESS = 0, - MCO_GENERIC_ERROR, - MCO_INVALID_POINTER, - MCO_INVALID_COROUTINE, - MCO_NOT_SUSPENDED, - MCO_NOT_RUNNING, - MCO_MAKE_CONTEXT_ERROR, - MCO_SWITCH_CONTEXT_ERROR, - MCO_NOT_ENOUGH_SPACE, - MCO_OUT_OF_MEMORY, - MCO_INVALID_ARGUMENTS, - MCO_INVALID_OPERATION, -} mco_result; - -/* Coroutine structure. */ -typedef struct mco_coro mco_coro; -struct mco_coro { - void* context; - mco_state state; - void (*func)(mco_coro* co); - mco_coro* prev_co; - void* user_data; - void* allocator_data; - void (*free_cb)(void* ptr, void* allocator_data); - void* stack_base; /* Stack base address, can be used to scan memory in a garbage collector. */ - size_t stack_size; - unsigned char* storage; - size_t bytes_stored; - size_t storage_size; - void* asan_prev_stack; /* Used by address sanitizer. */ - void* tsan_prev_fiber; /* Used by thread sanitizer. */ - void* tsan_fiber; /* Used by thread sanitizer. */ -}; - -/* Structure used to initialize a coroutine. */ -typedef struct mco_desc { - void (*func)(mco_coro* co); /* Entry point function for the coroutine. */ - void* user_data; /* Coroutine user data, can be get with `mco_get_user_data`. */ - /* Custom allocation interface. */ - void* (*malloc_cb)(size_t size, void* allocator_data); /* Custom allocation function. */ - void (*free_cb)(void* ptr, void* allocator_data); /* Custom deallocation function. */ - void* allocator_data; /* User data pointer passed to `malloc`/`free` allocation functions. */ - size_t storage_size; /* Coroutine storage size, to be used with the storage APIs. */ - /* These must be initialized only through `mco_init_desc`. */ - size_t coro_size; /* Coroutine structure size. */ - size_t stack_size; /* Coroutine stack size. */ -} mco_desc; - -/* Coroutine functions. */ -MCO_API mco_desc mco_desc_init(void (*func)(mco_coro* co), size_t stack_size); /* Initialize description of a coroutine. When stack size is 0 then MCO_DEFAULT_STACK_SIZE is used. */ -MCO_API mco_result mco_init(mco_coro* co, mco_desc* desc); /* Initialize the coroutine. */ -MCO_API mco_result mco_uninit(mco_coro* co); /* Uninitialize the coroutine, may fail if it's not dead or suspended. */ -MCO_API mco_result mco_create(mco_coro** out_co, mco_desc* desc); /* Allocates and initializes a new coroutine. */ -MCO_API mco_result mco_destroy(mco_coro* co); /* Uninitialize and deallocate the coroutine, may fail if it's not dead or suspended. */ -MCO_API mco_result mco_resume(mco_coro* co); /* Starts or continues the execution of the coroutine. */ -MCO_API mco_result mco_yield(mco_coro* co); /* Suspends the execution of a coroutine. */ -MCO_API mco_state mco_status(mco_coro* co); /* Returns the status of the coroutine. */ -MCO_API void* mco_get_user_data(mco_coro* co); /* Get coroutine user data supplied on coroutine creation. */ - -/* Storage interface functions, used to pass values between yield and resume. */ -MCO_API mco_result mco_push(mco_coro* co, const void* src, size_t len); /* Push bytes to the coroutine storage. Use to send values between yield and resume. */ -MCO_API mco_result mco_pop(mco_coro* co, void* dest, size_t len); /* Pop bytes from the coroutine storage. Use to get values between yield and resume. */ -MCO_API mco_result mco_peek(mco_coro* co, void* dest, size_t len); /* Like `mco_pop` but it does not consumes the storage. */ -MCO_API size_t mco_get_bytes_stored(mco_coro* co); /* Get the available bytes that can be retrieved with a `mco_pop`. */ -MCO_API size_t mco_get_storage_size(mco_coro* co); /* Get the total storage size. */ - -/* Misc functions. */ -MCO_API mco_coro* mco_running(void); /* Returns the running coroutine for the current thread. */ -MCO_API const char* mco_result_description(mco_result res); /* Get the description of a result. */ - -#ifdef __cplusplus -} -#endif - -#endif /* MINICORO_H */ - -#ifdef MINICORO_IMPL - -#ifdef __cplusplus -extern "C" { -#endif - -/* ---------------------------------------------------------------------------------------------- */ - -/* Minimum stack size when creating a coroutine. */ -#ifndef MCO_MIN_STACK_SIZE -#define MCO_MIN_STACK_SIZE 32768 -#endif - -/* Default stack size when creating a coroutine. */ -#ifndef MCO_DEFAULT_STACK_SIZE -#define MCO_DEFAULT_STACK_SIZE 57344 /* Don't use multiples of 64K to avoid D-cache aliasing conflicts. */ -#endif - -/* Detect implementation based on OS, arch and compiler. */ -#if !defined(MCO_USE_UCONTEXT) && !defined(MCO_USE_FIBERS) && !defined(MCO_USE_ASM) - #if defined(_WIN32) - #if (defined(__GNUC__) && defined(__x86_64__)) || (defined(_MSC_VER) && defined(_M_X64)) - #define MCO_USE_ASM - #else - #define MCO_USE_FIBERS - #endif - #elif defined(__EMSCRIPTEN__) - #define MCO_USE_FIBERS - #else - #if __GNUC__ >= 3 /* Assembly extension supported. */ - #if defined(__x86_64__) || \ - defined(__i386) || defined(__i386__) || \ - defined(__ARM_EABI__) || defined(__aarch64__) || \ - defined(__riscv) - #define MCO_USE_ASM - #else - #define MCO_USE_UCONTEXT - #endif - #else - #define MCO_USE_UCONTEXT - #endif - #endif -#endif - -#define _MCO_UNUSED(x) (void)(x) - -#if !defined(MCO_NO_DEBUG) && !defined(NDEBUG) && !defined(MCO_DEBUG) -#define MCO_DEBUG -#endif - -#ifndef MCO_LOG - #ifdef MCO_DEBUG - #include - #define MCO_LOG(s) puts(s) - #else - #define MCO_LOG(s) - #endif -#endif - -#ifndef MCO_ASSERT - #ifdef MCO_DEBUG - #include - #define MCO_ASSERT(c) assert(c) - #else - #define MCO_ASSERT(c) - #endif -#endif - -#ifndef MCO_THREAD_LOCAL - #ifdef MCO_NO_MULTITHREAD - #define MCO_THREAD_LOCAL - #else - #ifdef thread_local - #define MCO_THREAD_LOCAL thread_local - #elif __STDC_VERSION__ >= 201112 && !defined(__STDC_NO_THREADS__) - #define MCO_THREAD_LOCAL _Thread_local - #elif defined(_WIN32) && (defined(_MSC_VER) || defined(__ICL) || defined(__DMC__) || defined(__BORLANDC__)) - #define MCO_THREAD_LOCAL __declspec(thread) - #elif defined(__GNUC__) || defined(__SUNPRO_C) || defined(__xlC__) - #define MCO_THREAD_LOCAL __thread - #else /* No thread local support, `mco_running` will be thread unsafe. */ - #define MCO_THREAD_LOCAL - #define MCO_NO_MULTITHREAD - #endif - #endif -#endif - -#ifndef MCO_FORCE_INLINE - #ifdef _MSC_VER - #define MCO_FORCE_INLINE __forceinline - #elif defined(__GNUC__) - #if defined(__STRICT_ANSI__) - #define MCO_FORCE_INLINE __inline__ __attribute__((always_inline)) - #else - #define MCO_FORCE_INLINE inline __attribute__((always_inline)) - #endif - #elif defined(__BORLANDC__) || defined(__DMC__) || defined(__SC__) || defined(__WATCOMC__) || defined(__LCC__) || defined(__DECC) - #define MCO_FORCE_INLINE __inline - #else /* No inline support. */ - #define MCO_FORCE_INLINE - #endif -#endif - -#ifndef MCO_NO_DEFAULT_ALLOCATORS -#ifndef MCO_MALLOC - #include - #define MCO_MALLOC malloc - #define MCO_FREE free -#endif -static void* mco_malloc(size_t size, void* allocator_data) { - _MCO_UNUSED(allocator_data); - return MCO_MALLOC(size); -} -static void mco_free(void* ptr, void* allocator_data) { - _MCO_UNUSED(allocator_data); - MCO_FREE(ptr); -} -#endif /* MCO_NO_DEFAULT_ALLOCATORS */ - -#if defined(__has_feature) - #if __has_feature(address_sanitizer) - #define _MCO_USE_ASAN - #endif - #if __has_feature(thread_sanitizer) - #define _MCO_USE_TSAN - #endif -#endif -#if defined(__SANITIZE_ADDRESS__) - #define _MCO_USE_ASAN -#endif -#if defined(__SANITIZE_THREAD__) - #define _MCO_USE_TSAN -#endif -#ifdef _MCO_USE_ASAN -void __sanitizer_start_switch_fiber(void** fake_stack_save, const void *bottom, size_t size); -void __sanitizer_finish_switch_fiber(void* fake_stack_save, const void **bottom_old, size_t *size_old); -#endif -#ifdef _MCO_USE_TSAN -void* __tsan_get_current_fiber(void); -void* __tsan_create_fiber(unsigned flags); -void __tsan_destroy_fiber(void* fiber); -void __tsan_switch_to_fiber(void* fiber, unsigned flags); -#endif - -#include /* For memcpy and memset. */ - -/* Utility for aligning addresses. */ -static MCO_FORCE_INLINE size_t _mco_align_forward(size_t addr, size_t align) { - return (addr + (align-1)) & ~(align-1); -} - -/* Variable holding the current running coroutine per thread. */ -static MCO_THREAD_LOCAL mco_coro* mco_current_co = NULL; - -static MCO_FORCE_INLINE void _mco_prepare_jumpin(mco_coro* co) { - /* Set the old coroutine to normal state and update it. */ - mco_coro* prev_co = mco_running(); /* Must access through `mco_running`. */ - MCO_ASSERT(co->prev_co == NULL); - co->prev_co = prev_co; - if(prev_co) { - MCO_ASSERT(prev_co->state == MCO_RUNNING); - prev_co->state = MCO_NORMAL; - } - mco_current_co = co; -#ifdef _MCO_USE_ASAN - if(prev_co) { - void* bottom_old = NULL; - size_t size_old = 0; - __sanitizer_finish_switch_fiber(prev_co->asan_prev_stack, (const void**)&bottom_old, &size_old); - prev_co->asan_prev_stack = NULL; - } - __sanitizer_start_switch_fiber(&co->asan_prev_stack, co->stack_base, co->stack_size); -#endif -#ifdef _MCO_USE_TSAN - co->tsan_prev_fiber = __tsan_get_current_fiber(); - __tsan_switch_to_fiber(co->tsan_fiber, 0); -#endif -} - -static MCO_FORCE_INLINE void _mco_prepare_jumpout(mco_coro* co) { - /* Switch back to the previous running coroutine. */ - MCO_ASSERT(mco_running() == co); - mco_coro* prev_co = co->prev_co; - co->prev_co = NULL; - if(prev_co) { - MCO_ASSERT(prev_co->state == MCO_NORMAL); - prev_co->state = MCO_RUNNING; - } - mco_current_co = prev_co; -#ifdef _MCO_USE_ASAN - void* bottom_old = NULL; - size_t size_old = 0; - __sanitizer_finish_switch_fiber(co->asan_prev_stack, (const void**)&bottom_old, &size_old); - co->asan_prev_stack = NULL; - if(prev_co) { - __sanitizer_start_switch_fiber(&prev_co->asan_prev_stack, bottom_old, size_old); - } -#endif -#ifdef _MCO_USE_TSAN - void* tsan_prev_fiber = co->tsan_prev_fiber; - co->tsan_prev_fiber = NULL; - __tsan_switch_to_fiber(tsan_prev_fiber, 0); -#endif -} - -static void _mco_jumpin(mco_coro* co); -static void _mco_jumpout(mco_coro* co); - -static void _mco_main(mco_coro* co) { - co->func(co); /* Run the coroutine function. */ - co->state = MCO_DEAD; /* Coroutine finished successfully, set state to dead. */ - _mco_jumpout(co); /* Jump back to the old context .*/ -} - -/* ---------------------------------------------------------------------------------------------- */ - -#if defined(MCO_USE_UCONTEXT) || defined(MCO_USE_ASM) - -/* -Some of the following assembly code is taken from LuaCoco by Mike Pall. -See https://coco.luajit.org/index.html - -MIT license - -Copyright (C) 2004-2016 Mike Pall. All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifdef MCO_USE_ASM - -#if defined(__x86_64__) || defined(_M_X64) - -#ifdef _WIN32 - -typedef struct _mco_ctxbuf { - void *rip, *rsp, *rbp, *rbx, *r12, *r13, *r14, *r15, *rdi, *rsi; - void* xmm[20]; /* xmm6, xmm7, xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15 */ - void* fiber_storage; - void* dealloc_stack; - void* stack_limit; - void* stack_base; -} _mco_ctxbuf; - -#ifdef __GNUC__ -#define _MCO_ASM_BLOB __attribute__((section(".text#"))) -#elif defined(_MSC_VER) -#define _MCO_ASM_BLOB __declspec(allocate(".text")) -#pragma section(".text") -#endif - -_MCO_ASM_BLOB static unsigned char _mco_wrap_main_code[] = { - 0x4c, 0x89, 0xe9, /* mov %r13,%rcx */ - 0x41, 0xff, 0xe4, /* jmpq *%r12 */ - 0xc3, /* retq */ - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 /* nop */ -}; - -_MCO_ASM_BLOB static unsigned char _mco_switch_code[] = { - 0x48, 0x8d, 0x05, 0x52, 0x01, 0x00, 0x00, /* lea 0x152(%rip),%rax */ - 0x48, 0x89, 0x01, /* mov %rax,(%rcx) */ - 0x48, 0x89, 0x61, 0x08, /* mov %rsp,0x8(%rcx) */ - 0x48, 0x89, 0x69, 0x10, /* mov %rbp,0x10(%rcx) */ - 0x48, 0x89, 0x59, 0x18, /* mov %rbx,0x18(%rcx) */ - 0x4c, 0x89, 0x61, 0x20, /* mov %r12,0x20(%rcx) */ - 0x4c, 0x89, 0x69, 0x28, /* mov %r13,0x28(%rcx) */ - 0x4c, 0x89, 0x71, 0x30, /* mov %r14,0x30(%rcx) */ - 0x4c, 0x89, 0x79, 0x38, /* mov %r15,0x38(%rcx) */ - 0x48, 0x89, 0x79, 0x40, /* mov %rdi,0x40(%rcx) */ - 0x48, 0x89, 0x71, 0x48, /* mov %rsi,0x48(%rcx) */ - 0x66, 0x0f, 0xd6, 0x71, 0x50, /* movq %xmm6,0x50(%rcx) */ - 0x66, 0x0f, 0xd6, 0x79, 0x60, /* movq %xmm7,0x60(%rcx) */ - 0x66, 0x44, 0x0f, 0xd6, 0x41, 0x70, /* movq %xmm8,0x70(%rcx) */ - 0x66, 0x44, 0x0f, 0xd6, 0x89, 0x80, 0x00, 0x00, 0x00, /* movq %xmm9,0x80(%rcx) */ - 0x66, 0x44, 0x0f, 0xd6, 0x91, 0x90, 0x00, 0x00, 0x00, /* movq %xmm10,0x90(%rcx) */ - 0x66, 0x44, 0x0f, 0xd6, 0x99, 0xa0, 0x00, 0x00, 0x00, /* movq %xmm11,0xa0(%rcx) */ - 0x66, 0x44, 0x0f, 0xd6, 0xa1, 0xb0, 0x00, 0x00, 0x00, /* movq %xmm12,0xb0(%rcx) */ - 0x66, 0x44, 0x0f, 0xd6, 0xa9, 0xc0, 0x00, 0x00, 0x00, /* movq %xmm13,0xc0(%rcx) */ - 0x66, 0x44, 0x0f, 0xd6, 0xb1, 0xd0, 0x00, 0x00, 0x00, /* movq %xmm14,0xd0(%rcx) */ - 0x66, 0x44, 0x0f, 0xd6, 0xb9, 0xe0, 0x00, 0x00, 0x00, /* movq %xmm15,0xe0(%rcx) */ - 0x65, 0x4c, 0x8b, 0x14, 0x25, 0x30, 0x00, 0x00, 0x00, /* mov %gs:0x30,%r10 */ - 0x49, 0x8b, 0x42, 0x20, /* mov 0x20(%r10),%rax */ - 0x48, 0x89, 0x81, 0xf0, 0x00, 0x00, 0x00, /* mov %rax,0xf0(%rcx) */ - 0x49, 0x8b, 0x82, 0x78, 0x14, 0x00, 0x00, /* mov 0x1478(%r10),%rax */ - 0x48, 0x89, 0x81, 0xf8, 0x00, 0x00, 0x00, /* mov %rax,0xf8(%rcx) */ - 0x49, 0x8b, 0x42, 0x10, /* mov 0x10(%r10),%rax */ - 0x48, 0x89, 0x81, 0x00, 0x01, 0x00, 0x00, /* mov %rax,0x100(%rcx) */ - 0x49, 0x8b, 0x42, 0x08, /* mov 0x8(%r10),%rax */ - 0x48, 0x89, 0x81, 0x08, 0x01, 0x00, 0x00, /* mov %rax,0x108(%rcx) */ - 0x48, 0x8b, 0x82, 0x08, 0x01, 0x00, 0x00, /* mov 0x108(%rdx),%rax */ - 0x49, 0x89, 0x42, 0x08, /* mov %rax,0x8(%r10) */ - 0x48, 0x8b, 0x82, 0x00, 0x01, 0x00, 0x00, /* mov 0x100(%rdx),%rax */ - 0x49, 0x89, 0x42, 0x10, /* mov %rax,0x10(%r10) */ - 0x48, 0x8b, 0x82, 0xf8, 0x00, 0x00, 0x00, /* mov 0xf8(%rdx),%rax */ - 0x49, 0x89, 0x82, 0x78, 0x14, 0x00, 0x00, /* mov %rax,0x1478(%r10) */ - 0x48, 0x8b, 0x82, 0xf0, 0x00, 0x00, 0x00, /* mov 0xf0(%rdx),%rax */ - 0x49, 0x89, 0x42, 0x20, /* mov %rax,0x20(%r10) */ - 0xf3, 0x44, 0x0f, 0x7e, 0xba, 0xe0, 0x00, 0x00, 0x00, /* movq 0xe0(%rdx),%xmm15 */ - 0xf3, 0x44, 0x0f, 0x7e, 0xb2, 0xd0, 0x00, 0x00, 0x00, /* movq 0xd0(%rdx),%xmm14 */ - 0xf3, 0x44, 0x0f, 0x7e, 0xaa, 0xc0, 0x00, 0x00, 0x00, /* movq 0xc0(%rdx),%xmm13 */ - 0xf3, 0x44, 0x0f, 0x7e, 0xa2, 0xb0, 0x00, 0x00, 0x00, /* movq 0xb0(%rdx),%xmm12 */ - 0xf3, 0x44, 0x0f, 0x7e, 0x9a, 0xa0, 0x00, 0x00, 0x00, /* movq 0xa0(%rdx),%xmm11 */ - 0xf3, 0x44, 0x0f, 0x7e, 0x92, 0x90, 0x00, 0x00, 0x00, /* movq 0x90(%rdx),%xmm10 */ - 0xf3, 0x44, 0x0f, 0x7e, 0x8a, 0x80, 0x00, 0x00, 0x00, /* movq 0x80(%rdx),%xmm9 */ - 0xf3, 0x44, 0x0f, 0x7e, 0x42, 0x70, /* movq 0x70(%rdx),%xmm8 */ - 0xf3, 0x0f, 0x7e, 0x7a, 0x60, /* movq 0x60(%rdx),%xmm7 */ - 0xf3, 0x0f, 0x7e, 0x72, 0x50, /* movq 0x50(%rdx),%xmm6 */ - 0x48, 0x8b, 0x72, 0x48, /* mov 0x48(%rdx),%rsi */ - 0x48, 0x8b, 0x7a, 0x40, /* mov 0x40(%rdx),%rdi */ - 0x4c, 0x8b, 0x7a, 0x38, /* mov 0x38(%rdx),%r15 */ - 0x4c, 0x8b, 0x72, 0x30, /* mov 0x30(%rdx),%r14 */ - 0x4c, 0x8b, 0x6a, 0x28, /* mov 0x28(%rdx),%r13 */ - 0x4c, 0x8b, 0x62, 0x20, /* mov 0x20(%rdx),%r12 */ - 0x48, 0x8b, 0x5a, 0x18, /* mov 0x18(%rdx),%rbx */ - 0x48, 0x8b, 0x6a, 0x10, /* mov 0x10(%rdx),%rbp */ - 0x48, 0x8b, 0x62, 0x08, /* mov 0x8(%rdx),%rsp */ - 0xff, 0x22, /* jmpq *(%rdx) */ - 0xc3, /* retq */ - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, /* nop */ -}; - -void (*_mco_wrap_main)(void) = (void(*)(void))(void*)_mco_wrap_main_code; -void (*_mco_switch)(_mco_ctxbuf* from, _mco_ctxbuf* to) = (void(*)(_mco_ctxbuf* from, _mco_ctxbuf* to))(void*)_mco_switch_code; - -static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) { - stack_size = stack_size - 32; /* Reserve 32 bytes for the shadow space. */ - void** stack_high_ptr = (void**)((size_t)stack_base + stack_size - sizeof(size_t)); - stack_high_ptr[0] = (void*)(0xdeaddeaddeaddead); /* Dummy return address. */ - ctx->rip = (void*)(_mco_wrap_main); - ctx->rsp = (void*)(stack_high_ptr); - ctx->r12 = (void*)(_mco_main); - ctx->r13 = (void*)(co); - void* stack_top = (void*)((size_t)stack_base + stack_size); - ctx->stack_base = stack_top; - ctx->stack_limit = stack_base; - ctx->dealloc_stack = stack_base; - return MCO_SUCCESS; -} - -#else /* not _WIN32 */ - -typedef struct _mco_ctxbuf { - void *rip, *rsp, *rbp, *rbx, *r12, *r13, *r14, *r15; -} _mco_ctxbuf; - -void _mco_wrap_main(void); -int _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to); - -__asm__( - ".text\n" -#ifdef __MACH__ /* Mac OS X assembler */ - ".globl __mco_wrap_main\n" - "__mco_wrap_main:\n" -#else /* Linux assembler */ - ".globl _mco_wrap_main\n" - ".type _mco_wrap_main @function\n" - ".hidden _mco_wrap_main\n" - "_mco_wrap_main:\n" -#endif - " movq %r13, %rdi\n" - " jmpq *%r12\n" -#ifndef __MACH__ - ".size _mco_wrap_main, .-_mco_wrap_main\n" -#endif -); - -__asm__( - ".text\n" -#ifdef __MACH__ /* Mac OS assembler */ - ".globl __mco_switch\n" - "__mco_switch:\n" -#else /* Linux assembler */ - ".globl _mco_switch\n" - ".type _mco_switch @function\n" - ".hidden _mco_switch\n" - "_mco_switch:\n" -#endif - " leaq 0x3d(%rip), %rax\n" - " movq %rax, (%rdi)\n" - " movq %rsp, 8(%rdi)\n" - " movq %rbp, 16(%rdi)\n" - " movq %rbx, 24(%rdi)\n" - " movq %r12, 32(%rdi)\n" - " movq %r13, 40(%rdi)\n" - " movq %r14, 48(%rdi)\n" - " movq %r15, 56(%rdi)\n" - " movq 56(%rsi), %r15\n" - " movq 48(%rsi), %r14\n" - " movq 40(%rsi), %r13\n" - " movq 32(%rsi), %r12\n" - " movq 24(%rsi), %rbx\n" - " movq 16(%rsi), %rbp\n" - " movq 8(%rsi), %rsp\n" - " jmpq *(%rsi)\n" - " ret\n" -#ifndef __MACH__ - ".size _mco_switch, .-_mco_switch\n" -#endif -); - -static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) { - stack_size = stack_size - 128; /* Reserve 128 bytes for the Red Zone space (System V AMD64 ABI). */ - void** stack_high_ptr = (void**)((size_t)stack_base + stack_size - sizeof(size_t)); - stack_high_ptr[0] = (void*)(0xdeaddeaddeaddead); /* Dummy return address. */ - ctx->rip = (void*)(_mco_wrap_main); - ctx->rsp = (void*)(stack_high_ptr); - ctx->r12 = (void*)(_mco_main); - ctx->r13 = (void*)(co); - return MCO_SUCCESS; -} - -#endif /* not _WIN32 */ - -#elif defined(__riscv) - -typedef struct _mco_ctxbuf { - void* s[12]; /* s0-s11 */ - void* ra; - void* pc; - void* sp; -#ifdef __riscv_flen -#if __riscv_flen == 64 - double fs[12]; /* fs0-fs11 */ -#elif __riscv_flen == 32 - float fs[12]; /* fs0-fs11 */ -#endif -#endif /* __riscv_flen */ -} _mco_ctxbuf; - -void _mco_wrap_main(void); -int _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to); - -__asm__( - ".text\n" - ".globl _mco_wrap_main\n" - ".type _mco_wrap_main @function\n" - ".hidden _mco_wrap_main\n" - "_mco_wrap_main:\n" - " mv a0, s0\n" - " jr s1\n" - ".size _mco_wrap_main, .-_mco_wrap_main\n" -); - -__asm__( - ".text\n" - ".globl _mco_switch\n" - ".type _mco_switch @function\n" - ".hidden _mco_switch\n" - "_mco_switch:\n" - #if __riscv_xlen == 64 - " sd s0, 0x00(a0)\n" - " sd s1, 0x08(a0)\n" - " sd s2, 0x10(a0)\n" - " sd s3, 0x18(a0)\n" - " sd s4, 0x20(a0)\n" - " sd s5, 0x28(a0)\n" - " sd s6, 0x30(a0)\n" - " sd s7, 0x38(a0)\n" - " sd s8, 0x40(a0)\n" - " sd s9, 0x48(a0)\n" - " sd s10, 0x50(a0)\n" - " sd s11, 0x58(a0)\n" - " sd ra, 0x60(a0)\n" - " sd ra, 0x68(a0)\n" /* pc */ - " sd sp, 0x70(a0)\n" - #ifdef __riscv_flen - #if __riscv_flen == 64 - " fsd fs0, 0x78(a0)\n" - " fsd fs1, 0x80(a0)\n" - " fsd fs2, 0x88(a0)\n" - " fsd fs3, 0x90(a0)\n" - " fsd fs4, 0x98(a0)\n" - " fsd fs5, 0xa0(a0)\n" - " fsd fs6, 0xa8(a0)\n" - " fsd fs7, 0xb0(a0)\n" - " fsd fs8, 0xb8(a0)\n" - " fsd fs9, 0xc0(a0)\n" - " fsd fs10, 0xc8(a0)\n" - " fsd fs11, 0xd0(a0)\n" - " fld fs0, 0x78(a1)\n" - " fld fs1, 0x80(a1)\n" - " fld fs2, 0x88(a1)\n" - " fld fs3, 0x90(a1)\n" - " fld fs4, 0x98(a1)\n" - " fld fs5, 0xa0(a1)\n" - " fld fs6, 0xa8(a1)\n" - " fld fs7, 0xb0(a1)\n" - " fld fs8, 0xb8(a1)\n" - " fld fs9, 0xc0(a1)\n" - " fld fs10, 0xc8(a1)\n" - " fld fs11, 0xd0(a1)\n" - #else - #error "Unsupported RISC-V FLEN" - #endif - #endif /* __riscv_flen */ - " ld s0, 0x00(a1)\n" - " ld s1, 0x08(a1)\n" - " ld s2, 0x10(a1)\n" - " ld s3, 0x18(a1)\n" - " ld s4, 0x20(a1)\n" - " ld s5, 0x28(a1)\n" - " ld s6, 0x30(a1)\n" - " ld s7, 0x38(a1)\n" - " ld s8, 0x40(a1)\n" - " ld s9, 0x48(a1)\n" - " ld s10, 0x50(a1)\n" - " ld s11, 0x58(a1)\n" - " ld ra, 0x60(a1)\n" - " ld a2, 0x68(a1)\n" /* pc */ - " ld sp, 0x70(a1)\n" - " jr a2\n" - #elif __riscv_xlen == 32 - " sw s0, 0x00(a0)\n" - " sw s1, 0x04(a0)\n" - " sw s2, 0x08(a0)\n" - " sw s3, 0x0c(a0)\n" - " sw s4, 0x10(a0)\n" - " sw s5, 0x14(a0)\n" - " sw s6, 0x18(a0)\n" - " sw s7, 0x1c(a0)\n" - " sw s8, 0x20(a0)\n" - " sw s9, 0x24(a0)\n" - " sw s10, 0x28(a0)\n" - " sw s11, 0x2c(a0)\n" - " sw ra, 0x30(a0)\n" - " sw ra, 0x34(a0)\n" /* pc */ - " sw sp, 0x38(a0)\n" - #ifdef __riscv_flen - #if __riscv_flen == 64 - " fsd fs0, 0x3c(a0)\n" - " fsd fs1, 0x44(a0)\n" - " fsd fs2, 0x4c(a0)\n" - " fsd fs3, 0x54(a0)\n" - " fsd fs4, 0x5c(a0)\n" - " fsd fs5, 0x64(a0)\n" - " fsd fs6, 0x6c(a0)\n" - " fsd fs7, 0x74(a0)\n" - " fsd fs8, 0x7c(a0)\n" - " fsd fs9, 0x84(a0)\n" - " fsd fs10, 0x8c(a0)\n" - " fsd fs11, 0x94(a0)\n" - " fld fs0, 0x3c(a1)\n" - " fld fs1, 0x44(a1)\n" - " fld fs2, 0x4c(a1)\n" - " fld fs3, 0x54(a1)\n" - " fld fs4, 0x5c(a1)\n" - " fld fs5, 0x64(a1)\n" - " fld fs6, 0x6c(a1)\n" - " fld fs7, 0x74(a1)\n" - " fld fs8, 0x7c(a1)\n" - " fld fs9, 0x84(a1)\n" - " fld fs10, 0x8c(a1)\n" - " fld fs11, 0x94(a1)\n" - #elif __riscv_flen == 32 - " fsw fs0, 0x3c(a0)\n" - " fsw fs1, 0x40(a0)\n" - " fsw fs2, 0x44(a0)\n" - " fsw fs3, 0x48(a0)\n" - " fsw fs4, 0x4c(a0)\n" - " fsw fs5, 0x50(a0)\n" - " fsw fs6, 0x54(a0)\n" - " fsw fs7, 0x58(a0)\n" - " fsw fs8, 0x5c(a0)\n" - " fsw fs9, 0x60(a0)\n" - " fsw fs10, 0x64(a0)\n" - " fsw fs11, 0x68(a0)\n" - " flw fs0, 0x3c(a1)\n" - " flw fs1, 0x40(a1)\n" - " flw fs2, 0x44(a1)\n" - " flw fs3, 0x48(a1)\n" - " flw fs4, 0x4c(a1)\n" - " flw fs5, 0x50(a1)\n" - " flw fs6, 0x54(a1)\n" - " flw fs7, 0x58(a1)\n" - " flw fs8, 0x5c(a1)\n" - " flw fs9, 0x60(a1)\n" - " flw fs10, 0x64(a1)\n" - " flw fs11, 0x68(a1)\n" - #else - #error "Unsupported RISC-V FLEN" - #endif - #endif /* __riscv_flen */ - " lw s0, 0x00(a1)\n" - " lw s1, 0x04(a1)\n" - " lw s2, 0x08(a1)\n" - " lw s3, 0x0c(a1)\n" - " lw s4, 0x10(a1)\n" - " lw s5, 0x14(a1)\n" - " lw s6, 0x18(a1)\n" - " lw s7, 0x1c(a1)\n" - " lw s8, 0x20(a1)\n" - " lw s9, 0x24(a1)\n" - " lw s10, 0x28(a1)\n" - " lw s11, 0x2c(a1)\n" - " lw ra, 0x30(a1)\n" - " lw a2, 0x34(a1)\n" /* pc */ - " lw sp, 0x38(a1)\n" - " jr a2\n" - #else - #error "Unsupported RISC-V XLEN" - #endif /* __riscv_xlen */ - ".size _mco_switch, .-_mco_switch\n" -); - -static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) { - ctx->s[0] = (void*)(co); - ctx->s[1] = (void*)(_mco_main); - ctx->pc = (void*)(_mco_wrap_main); -#if __riscv_xlen == 64 - ctx->ra = (void*)(0xdeaddeaddeaddead); -#elif __riscv_xlen == 32 - ctx->ra = (void*)(0xdeaddead); -#endif - ctx->sp = (void*)((size_t)stack_base + stack_size); - return MCO_SUCCESS; -} - -#elif defined(__i386) || defined(__i386__) - -typedef struct _mco_ctxbuf { - void *eip, *esp, *ebp, *ebx, *esi, *edi; -} _mco_ctxbuf; - -void _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to); - -__asm__( - ".text\n" - ".globl _mco_switch\n" - ".type _mco_switch @function\n" - ".hidden _mco_switch\n" - "_mco_switch:\n" - " call 1f\n" - " 1:\n" - " popl %ecx\n" - " addl $(2f-1b), %ecx\n" - " movl 4(%esp), %eax\n" - " movl 8(%esp), %edx\n" - " movl %ecx, (%eax)\n" - " movl %esp, 4(%eax)\n" - " movl %ebp, 8(%eax)\n" - " movl %ebx, 12(%eax)\n" - " movl %esi, 16(%eax)\n" - " movl %edi, 20(%eax)\n" - " movl 20(%edx), %edi\n" - " movl 16(%edx), %esi\n" - " movl 12(%edx), %ebx\n" - " movl 8(%edx), %ebp\n" - " movl 4(%edx), %esp\n" - " jmp *(%edx)\n" - " 2:\n" - " ret\n" - ".size _mco_switch, .-_mco_switch\n" -); - -static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) { - void** stack_high_ptr = (void**)((size_t)stack_base + stack_size - 16 - 1*sizeof(size_t)); - stack_high_ptr[0] = (void*)(0xdeaddead); /* Dummy return address. */ - stack_high_ptr[1] = (void*)(co); - ctx->eip = (void*)(_mco_main); - ctx->esp = (void*)(stack_high_ptr); - return MCO_SUCCESS; -} - -#elif defined(__ARM_EABI__) - -typedef struct _mco_ctxbuf { -#ifndef __SOFTFP__ - void* f[16]; -#endif - void *d[4]; /* d8-d15 */ - void *r[4]; /* r4-r11 */ - void *lr; - void *sp; -} _mco_ctxbuf; - -void _mco_wrap_main(void); -int _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to); - -__asm__( - ".text\n" - ".globl _mco_switch\n" - ".type _mco_switch #function\n" - ".hidden _mco_switch\n" - "_mco_switch:\n" -#ifndef __SOFTFP__ - " vstmia r0!, {d8-d15}\n" -#endif - " stmia r0, {r4-r11, lr}\n" - " str sp, [r0, #9*4]\n" -#ifndef __SOFTFP__ - " vldmia r1!, {d8-d15}\n" -#endif - " ldr sp, [r1, #9*4]\n" - " ldmia r1, {r4-r11, pc}\n" - ".size _mco_switch, .-_mco_switch\n" -); - -__asm__( - ".text\n" - ".globl _mco_wrap_main\n" - ".type _mco_wrap_main #function\n" - ".hidden _mco_wrap_main\n" - "_mco_wrap_main:\n" - " mov r0, r4\n" - " mov ip, r5\n" - " mov lr, r6\n" - " bx ip\n" - ".size _mco_wrap_main, .-_mco_wrap_main\n" -); - -static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) { - ctx->d[0] = (void*)(co); - ctx->d[1] = (void*)(_mco_main); - ctx->d[2] = (void*)(0xdeaddead); /* Dummy return address. */ - ctx->lr = (void*)(_mco_wrap_main); - ctx->sp = (void*)((size_t)stack_base + stack_size); - return MCO_SUCCESS; -} - -#elif defined(__aarch64__) - -typedef struct _mco_ctxbuf { - void *x[12]; /* x19-x30 */ - void *sp; - void *lr; - void *d[8]; /* d8-d15 */ -} _mco_ctxbuf; - -void _mco_wrap_main(void); -int _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to); - -__asm__( - ".text\n" - ".globl _mco_switch\n" - ".type _mco_switch #function\n" - ".hidden _mco_switch\n" - "_mco_switch:\n" - " mov x10, sp\n" - " mov x11, x30\n" - " stp x19, x20, [x0, #(0*16)]\n" - " stp x21, x22, [x0, #(1*16)]\n" - " stp d8, d9, [x0, #(7*16)]\n" - " stp x23, x24, [x0, #(2*16)]\n" - " stp d10, d11, [x0, #(8*16)]\n" - " stp x25, x26, [x0, #(3*16)]\n" - " stp d12, d13, [x0, #(9*16)]\n" - " stp x27, x28, [x0, #(4*16)]\n" - " stp d14, d15, [x0, #(10*16)]\n" - " stp x29, x30, [x0, #(5*16)]\n" - " stp x10, x11, [x0, #(6*16)]\n" - " ldp x19, x20, [x1, #(0*16)]\n" - " ldp x21, x22, [x1, #(1*16)]\n" - " ldp d8, d9, [x1, #(7*16)]\n" - " ldp x23, x24, [x1, #(2*16)]\n" - " ldp d10, d11, [x1, #(8*16)]\n" - " ldp x25, x26, [x1, #(3*16)]\n" - " ldp d12, d13, [x1, #(9*16)]\n" - " ldp x27, x28, [x1, #(4*16)]\n" - " ldp d14, d15, [x1, #(10*16)]\n" - " ldp x29, x30, [x1, #(5*16)]\n" - " ldp x10, x11, [x1, #(6*16)]\n" - " mov sp, x10\n" - " br x11\n" - ".size _mco_switch, .-_mco_switch\n" -); - -__asm__( - ".text\n" - ".globl _mco_wrap_main\n" - ".type _mco_wrap_main #function\n" - ".hidden _mco_wrap_main\n" - "_mco_wrap_main:\n" - " mov x0, x19\n" - " mov x30, x21\n" - " br x20\n" - ".size _mco_wrap_main, .-_mco_wrap_main\n" -); - -static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) { - ctx->x[0] = (void*)(co); - ctx->x[1] = (void*)(_mco_main); - ctx->x[2] = (void*)(0xdeaddeaddeaddead); /* Dummy return address. */ - ctx->sp = (void*)((size_t)stack_base + stack_size); - ctx->lr = (void*)(_mco_wrap_main); - return MCO_SUCCESS; -} - -#else - -#error "Unsupported architecture for assembly method." - -#endif /* ARCH */ - -#elif defined(MCO_USE_UCONTEXT) - -#include - -typedef ucontext_t _mco_ctxbuf; - -#if defined(_LP64) || defined(__LP64__) -static void _mco_wrap_main(unsigned int lo, unsigned int hi) { - mco_coro* co = (mco_coro*)(((size_t)lo) | (((size_t)hi) << 32)); /* Extract coroutine pointer. */ - _mco_main(co); -} -#else -static void _mco_wrap_main(unsigned int lo) { - mco_coro* co = (mco_coro*)((size_t)lo); /* Extract coroutine pointer. */ - _mco_main(co); -} -#endif - -static MCO_FORCE_INLINE void _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to) { - int res = swapcontext(from, to); - _MCO_UNUSED(res); - MCO_ASSERT(res == 0); -} - -static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) { - /* Initialize ucontext. */ - if(getcontext(ctx) != 0) { - MCO_LOG("failed to get ucontext"); - return MCO_MAKE_CONTEXT_ERROR; - } - ctx->uc_link = NULL; /* We never exit from _mco_wrap_main. */ - ctx->uc_stack.ss_sp = stack_base; - ctx->uc_stack.ss_size = stack_size; - unsigned int lo = (unsigned int)((size_t)co); -#if defined(_LP64) || defined(__LP64__) - unsigned int hi = (unsigned int)(((size_t)co)>>32); - makecontext(ctx, (void (*)(void))_mco_wrap_main, 2, lo, hi); -#else - makecontext(ctx, (void (*)(void))_mco_wrap_main, 1, lo); -#endif - return MCO_SUCCESS; -} - -#endif /* defined(MCO_USE_UCONTEXT) */ - -#ifdef MCO_USE_VALGRIND -#include -#endif - -typedef struct _mco_context { -#ifdef MCO_USE_VALGRIND - unsigned int valgrind_stack_id; -#endif - _mco_ctxbuf ctx; - _mco_ctxbuf back_ctx; -} _mco_context; - -static void _mco_jumpin(mco_coro* co) { - _mco_context* context = (_mco_context*)co->context; - _mco_prepare_jumpin(co); - _mco_switch(&context->back_ctx, &context->ctx); /* Do the context switch. */ -} - -static void _mco_jumpout(mco_coro* co) { - _mco_context* context = (_mco_context*)co->context; - _mco_prepare_jumpout(co); - _mco_switch(&context->ctx, &context->back_ctx); /* Do the context switch. */ -} - -static mco_result _mco_create_context(mco_coro* co, mco_desc* desc) { - /* Determine the context and stack address. */ - size_t co_addr = (size_t)co; - size_t context_addr = _mco_align_forward(co_addr + sizeof(mco_coro), 16); - size_t storage_addr = _mco_align_forward(context_addr + sizeof(_mco_context), 16); - size_t stack_addr = _mco_align_forward(storage_addr + desc->storage_size, 16); - /* Initialize context. */ - _mco_context* context = (_mco_context*)context_addr; - memset(context, 0, sizeof(_mco_context)); - /* Initialize storage. */ - unsigned char* storage = (unsigned char*)storage_addr; - memset(storage, 0, desc->storage_size); - /* Initialize stack. */ - void *stack_base = (void*)stack_addr; - size_t stack_size = desc->stack_size; -#ifdef MCO_ZERO_MEMORY - memset(stack_base, 0, stack_size); -#endif - /* Make the context. */ - mco_result res = _mco_makectx(co, &context->ctx, stack_base, stack_size); - if(res != MCO_SUCCESS) { - return res; - } -#ifdef MCO_USE_VALGRIND - context->valgrind_stack_id = VALGRIND_STACK_REGISTER(stack_addr, stack_addr + stack_size); -#endif - co->context = context; - co->stack_base = stack_base; - co->stack_size = stack_size; - co->storage = storage; - co->storage_size = desc->storage_size; - return MCO_SUCCESS; -} - -static void _mco_destroy_context(mco_coro* co) { -#ifdef MCO_USE_VALGRIND - _mco_context* context = (_mco_context*)co->context; - if(context && context->valgrind_stack_id != 0) { - VALGRIND_STACK_DEREGISTER(context->valgrind_stack_id); - context->valgrind_stack_id = 0; - } -#else - _MCO_UNUSED(co); -#endif -} - -static MCO_FORCE_INLINE void _mco_init_desc_sizes(mco_desc* desc, size_t stack_size) { - desc->coro_size = _mco_align_forward(sizeof(mco_coro), 16) + - _mco_align_forward(sizeof(_mco_context), 16) + - _mco_align_forward(desc->storage_size, 16) + - stack_size + 16; - desc->stack_size = stack_size; /* This is just a hint, it won't be the real one. */ -} - -#endif /* defined(MCO_USE_UCONTEXT) || defined(MCO_USE_ASM) */ - -/* ---------------------------------------------------------------------------------------------- */ - -#ifdef MCO_USE_FIBERS - -#ifdef _WIN32 - -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0400 -#endif -#include - -typedef struct _mco_context { - void* fib; - void* back_fib; -} _mco_context; - -static void _mco_jumpin(mco_coro* co) { - void *cur_fib = GetCurrentFiber(); - if(!cur_fib || cur_fib == (void*)0x1e00) { /* See http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx */ - cur_fib = ConvertThreadToFiber(NULL); - } - MCO_ASSERT(cur_fib != NULL); - _mco_context* context = (_mco_context*)co->context; - context->back_fib = cur_fib; - _mco_prepare_jumpin(co); - SwitchToFiber(context->fib); -} - -static void CALLBACK _mco_wrap_main(void* co) { - _mco_main((mco_coro*)co); -} - -static void _mco_jumpout(mco_coro* co) { - _mco_context* context = (_mco_context*)co->context; - void* back_fib = context->back_fib; - MCO_ASSERT(back_fib != NULL); - context->back_fib = NULL; - _mco_prepare_jumpout(co); - SwitchToFiber(back_fib); -} - -/* Reverse engineered Fiber struct, used to get stack base. */ -typedef struct _mco_fiber { - LPVOID param; /* fiber param */ - void* except; /* saved exception handlers list */ - void* stack_base; /* top of fiber stack */ - void* stack_limit; /* fiber stack low-water mark */ - void* stack_allocation; /* base of the fiber stack allocation */ - CONTEXT context; /* fiber context */ - DWORD flags; /* fiber flags */ - LPFIBER_START_ROUTINE start; /* start routine */ - void **fls_slots; /* fiber storage slots */ -} _mco_fiber; - -static mco_result _mco_create_context(mco_coro* co, mco_desc* desc) { - /* Determine the context address. */ - size_t co_addr = (size_t)co; - size_t context_addr = _mco_align_forward(co_addr + sizeof(mco_coro), 16); - size_t storage_addr = _mco_align_forward(context_addr + sizeof(_mco_context), 16); - /* Initialize context. */ - _mco_context* context = (_mco_context*)context_addr; - memset(context, 0, sizeof(_mco_context)); - /* Initialize storage. */ - unsigned char* storage = (unsigned char*)storage_addr; - memset(storage, 0, desc->storage_size); - /* Create the fiber. */ - _mco_fiber* fib = (_mco_fiber*)CreateFiberEx(desc->stack_size, desc->stack_size, FIBER_FLAG_FLOAT_SWITCH, _mco_wrap_main, co); - if(!fib) { - MCO_LOG("failed to create fiber"); - return MCO_MAKE_CONTEXT_ERROR; - } - context->fib = fib; - co->context = context; - co->stack_base = fib->stack_base; - co->stack_size = desc->stack_size; - co->storage = storage; - co->storage_size = desc->storage_size; - return MCO_SUCCESS; -} - -static void _mco_destroy_context(mco_coro* co) { - _mco_context* context = (_mco_context*)co->context; - if(context && context->fib) { - DeleteFiber(context->fib); - context->fib = NULL; - } -} - -static MCO_FORCE_INLINE void _mco_init_desc_sizes(mco_desc* desc, size_t stack_size) { - desc->coro_size = _mco_align_forward(sizeof(mco_coro), 16) + - _mco_align_forward(sizeof(_mco_context), 16) + - _mco_align_forward(desc->storage_size, 16) + - 16; - desc->stack_size = stack_size; -} - -#elif defined(__EMSCRIPTEN__) - -#include - -#ifndef MCO_ASYNCFY_STACK_SIZE -#define MCO_ASYNCFY_STACK_SIZE 16384 -#endif - -typedef struct _mco_context { - emscripten_fiber_t fib; - emscripten_fiber_t* back_fib; -} _mco_context; - -static emscripten_fiber_t* running_fib = NULL; -static unsigned char main_asyncify_stack[MCO_ASYNCFY_STACK_SIZE]; -static emscripten_fiber_t main_fib; - -static void _mco_wrap_main(void* co) { - _mco_main((mco_coro*)co); -} - -static void _mco_jumpin(mco_coro* co) { - _mco_context* context = (_mco_context*)co->context; - emscripten_fiber_t* back_fib = running_fib; - if(!back_fib) { - back_fib = &main_fib; - emscripten_fiber_init_from_current_context(back_fib, main_asyncify_stack, MCO_ASYNCFY_STACK_SIZE); - } - running_fib = &context->fib; - context->back_fib = back_fib; - _mco_prepare_jumpin(co); - emscripten_fiber_swap(back_fib, &context->fib); /* Do the context switch. */ -} - -static void _mco_jumpout(mco_coro* co) { - _mco_context* context = (_mco_context*)co->context; - running_fib = context->back_fib; - _mco_prepare_jumpout(co); - emscripten_fiber_swap(&context->fib, context->back_fib); /* Do the context switch. */ -} - -static mco_result _mco_create_context(mco_coro* co, mco_desc* desc) { - if(emscripten_has_asyncify() != 1) { - MCO_LOG("failed to create fiber because ASYNCIFY is not enabled"); - return MCO_MAKE_CONTEXT_ERROR; - } - /* Determine the context address. */ - size_t co_addr = (size_t)co; - size_t context_addr = _mco_align_forward(co_addr + sizeof(mco_coro), 16); - size_t storage_addr = _mco_align_forward(context_addr + sizeof(_mco_context), 16); - size_t stack_addr = _mco_align_forward(storage_addr + desc->storage_size, 16); - size_t asyncify_stack_addr = _mco_align_forward(stack_addr + desc->stack_size, 16); - /* Initialize context. */ - _mco_context* context = (_mco_context*)context_addr; - memset(context, 0, sizeof(_mco_context)); - /* Initialize storage. */ - unsigned char* storage = (unsigned char*)storage_addr; - memset(storage, 0, desc->storage_size); - /* Initialize stack. */ - void *stack_base = (void*)stack_addr; - size_t stack_size = asyncify_stack_addr - stack_addr; - void *asyncify_stack_base = (void*)asyncify_stack_addr; - size_t asyncify_stack_size = co_addr + desc->coro_size - asyncify_stack_addr; -#ifdef MCO_ZERO_MEMORY - memset(stack_base, 0, stack_size); - memset(asyncify_stack_base, 0, asyncify_stack_size); -#endif - /* Create the fiber. */ - emscripten_fiber_init(&context->fib, _mco_wrap_main, co, stack_base, stack_size, asyncify_stack_base, asyncify_stack_size); - co->context = context; - co->stack_base = stack_base; - co->stack_size = stack_size; - co->storage = storage; - co->storage_size = desc->storage_size; - return MCO_SUCCESS; -} - -static void _mco_destroy_context(mco_coro* co) { - /* Nothing to do. */ - _MCO_UNUSED(co); -} - -static MCO_FORCE_INLINE void _mco_init_desc_sizes(mco_desc* desc, size_t stack_size) { - desc->coro_size = _mco_align_forward(sizeof(mco_coro), 16) + - _mco_align_forward(sizeof(_mco_context), 16) + - _mco_align_forward(desc->storage_size, 16) + - _mco_align_forward(stack_size, 16) + - _mco_align_forward(MCO_ASYNCFY_STACK_SIZE, 16) + - 16; - desc->stack_size = stack_size; /* This is just a hint, it won't be the real one. */ -} - -#else - -#error "Unsupported architecture for fibers method." - -#endif - -#endif /* MCO_USE_FIBERS */ - -/* ---------------------------------------------------------------------------------------------- */ - -mco_desc mco_desc_init(void (*func)(mco_coro* co), size_t stack_size) { - if(stack_size != 0) { - /* Stack size should be at least `MCO_MIN_STACK_SIZE`. */ - if(stack_size < MCO_MIN_STACK_SIZE) { - stack_size = MCO_MIN_STACK_SIZE; - } - } else { - stack_size = MCO_DEFAULT_STACK_SIZE; - } - stack_size = _mco_align_forward(stack_size, 16); /* Stack size should be aligned to 16 bytes. */ - mco_desc desc; - memset(&desc, 0, sizeof(mco_desc)); -#ifndef MCO_NO_DEFAULT_ALLOCATORS - /* Set default allocators. */ - desc.malloc_cb = mco_malloc; - desc.free_cb = mco_free; -#endif - desc.func = func; - desc.storage_size = MCO_DEFAULT_STORAGE_SIZE; - _mco_init_desc_sizes(&desc, stack_size); - return desc; -} - -static mco_result _mco_validate_desc(mco_desc* desc) { - if(!desc) { - MCO_LOG("coroutine description is NULL"); - return MCO_INVALID_ARGUMENTS; - } - if(!desc->func) { - MCO_LOG("coroutine function in invalid"); - return MCO_INVALID_ARGUMENTS; - } - if(desc->stack_size < MCO_MIN_STACK_SIZE) { - MCO_LOG("coroutine stack size is too small"); - return MCO_INVALID_ARGUMENTS; - } - if(desc->coro_size < sizeof(mco_coro)) { - MCO_LOG("coroutine size is invalid"); - return MCO_INVALID_ARGUMENTS; - } - return MCO_SUCCESS; -} - -mco_result mco_init(mco_coro* co, mco_desc* desc) { - if(!co) { - MCO_LOG("attempt to initialize an invalid coroutine"); - return MCO_INVALID_COROUTINE; - } - memset(co, 0, sizeof(mco_coro)); - /* Validate coroutine description. */ - mco_result res = _mco_validate_desc(desc); - if(res != MCO_SUCCESS) - return res; - /* Create the coroutine. */ - res = _mco_create_context(co, desc); - if(res != MCO_SUCCESS) - return res; - co->state = MCO_SUSPENDED; /* We initialize in suspended state. */ - co->free_cb = desc->free_cb; - co->allocator_data = desc->allocator_data; - co->func = desc->func; - co->user_data = desc->user_data; -#ifdef _MCO_USE_TSAN - co->tsan_fiber = __tsan_create_fiber(0); -#endif - return MCO_SUCCESS; -} - -mco_result mco_uninit(mco_coro* co) { - if(!co) { - MCO_LOG("attempt to uninitialize an invalid coroutine"); - return MCO_INVALID_COROUTINE; - } - /* Cannot uninitialize while running. */ - if(!(co->state == MCO_SUSPENDED || co->state == MCO_DEAD)) { - MCO_LOG("attempt to uninitialize a coroutine that is not dead or suspended"); - return MCO_INVALID_OPERATION; - } - /* The coroutine is now dead and cannot be used anymore. */ - co->state = MCO_DEAD; -#ifdef _MCO_USE_TSAN - if(co->tsan_fiber != NULL) { - __tsan_destroy_fiber(co->tsan_fiber); - co->tsan_fiber = NULL; - } -#endif - _mco_destroy_context(co); - return MCO_SUCCESS; -} - -mco_result mco_create(mco_coro** out_co, mco_desc* desc) { - /* Validate input. */ - if(!out_co) { - MCO_LOG("coroutine output pointer is NULL"); - return MCO_INVALID_POINTER; - } - if(!desc || !desc->malloc_cb || !desc->free_cb) { - *out_co = NULL; - MCO_LOG("coroutine allocator description is not set"); - return MCO_INVALID_ARGUMENTS; - } - /* Allocate the coroutine. */ - mco_coro* co = (mco_coro*)desc->malloc_cb(desc->coro_size, desc->allocator_data); - if(!co) { - MCO_LOG("coroutine allocation failed"); - *out_co = NULL; - return MCO_OUT_OF_MEMORY; - } - /* Initialize the coroutine. */ - mco_result res = mco_init(co, desc); - if(res != MCO_SUCCESS) { - desc->free_cb(co, desc->allocator_data); - *out_co = NULL; - return res; - } - *out_co = co; - return MCO_SUCCESS; -} - -mco_result mco_destroy(mco_coro* co) { - if(!co) { - MCO_LOG("attempt to destroy an invalid coroutine"); - return MCO_INVALID_COROUTINE; - } - /* Uninitialize the coroutine first. */ - mco_result res = mco_uninit(co); - if(res != MCO_SUCCESS) - return res; - /* Free the coroutine. */ - if(!co->free_cb) { - MCO_LOG("attempt destroy a coroutine that has no free callback"); - return MCO_INVALID_POINTER; - } - co->free_cb(co, co->allocator_data); - return MCO_SUCCESS; -} - -mco_result mco_resume(mco_coro* co) { - if(!co) { - MCO_LOG("attempt to resume an invalid coroutine"); - return MCO_INVALID_COROUTINE; - } - if(co->state != MCO_SUSPENDED) { /* Can only resume coroutines that are suspended. */ - MCO_LOG("attempt to resume a coroutine that is not suspended"); - return MCO_NOT_SUSPENDED; - } - co->state = MCO_RUNNING; /* The coroutine is now running. */ - _mco_jumpin(co); - return MCO_SUCCESS; -} - -mco_result mco_yield(mco_coro* co) { - if(!co) { - MCO_LOG("attempt to yield an invalid coroutine"); - return MCO_INVALID_COROUTINE; - } - if(co->state != MCO_RUNNING) { /* Can only yield coroutines that are running. */ - MCO_LOG("attempt to yield a coroutine that is not running"); - return MCO_NOT_RUNNING; - } - co->state = MCO_SUSPENDED; /* The coroutine is now suspended. */ - _mco_jumpout(co); - return MCO_SUCCESS; -} - -mco_state mco_status(mco_coro* co) { - if(co != NULL) { - return co->state; - } - return MCO_DEAD; -} - -void* mco_get_user_data(mco_coro* co) { - if(co != NULL) { - return co->user_data; - } - return NULL; -} - -mco_result mco_push(mco_coro* co, const void* src, size_t len) { - if(!co) { - MCO_LOG("attempt to use an invalid coroutine"); - return MCO_INVALID_COROUTINE; - } else if(len > 0) { - size_t bytes_stored = co->bytes_stored + len; - if(bytes_stored > co->storage_size) { - MCO_LOG("attempt to push bytes too many bytes into coroutine storage"); - return MCO_NOT_ENOUGH_SPACE; - } - if(!src) { - MCO_LOG("attempt push a null pointer into coroutine storage"); - return MCO_INVALID_POINTER; - } - memcpy(&co->storage[co->bytes_stored], src, len); - co->bytes_stored = bytes_stored; - } - return MCO_SUCCESS; -} - -mco_result mco_pop(mco_coro* co, void* dest, size_t len) { - if(!co) { - MCO_LOG("attempt to use an invalid coroutine"); - return MCO_INVALID_COROUTINE; - } else if(len > 0) { - if(len > co->bytes_stored) { - MCO_LOG("attempt to pop too many bytes from coroutine storage"); - return MCO_NOT_ENOUGH_SPACE; - } - size_t bytes_stored = co->bytes_stored - len; - if(dest) { - memcpy(dest, &co->storage[bytes_stored], len); - } - co->bytes_stored = bytes_stored; -#ifdef MCO_ZERO_MEMORY - /* Clear garbage in the discarded storage. */ - memset(&co->storage[bytes_stored], 0, len); -#endif - } - return MCO_SUCCESS; -} - -mco_result mco_peek(mco_coro* co, void* dest, size_t len) { - if(!co) { - MCO_LOG("attempt to use an invalid coroutine"); - return MCO_INVALID_COROUTINE; - } else if(len > 0) { - if(len > co->bytes_stored) { - MCO_LOG("attempt to peek too many bytes from coroutine storage"); - return MCO_NOT_ENOUGH_SPACE; - } - if(!dest) { - MCO_LOG("attempt peek into a null pointer"); - return MCO_INVALID_POINTER; - } - memcpy(dest, &co->storage[co->bytes_stored - len], len); - } - return MCO_SUCCESS; -} - -size_t mco_get_bytes_stored(mco_coro* co) { - if(co == NULL) { - return 0; - } - return co->bytes_stored; -} - -size_t mco_get_storage_size(mco_coro* co) { - if(co == NULL) { - return 0; - } - return co->storage_size; -} - -#ifdef MCO_NO_MULTITHREAD -mco_coro* mco_running(void) { - return mco_current_co; -} -#else -static mco_coro* _mco_running(void) { - return mco_current_co; -} -mco_coro* mco_running(void) { - /* - Compilers aggressively optimize the use of TLS by caching loads. - Since fiber code can migrate between threads it’s possible for the load to be stale. - To prevent this from happening we avoid inline functions. - */ - mco_coro* (*volatile func)(void) = _mco_running; - return func(); -} -#endif - -const char* mco_result_description(mco_result res) { - switch(res) { - case MCO_SUCCESS: - return "No error"; - case MCO_GENERIC_ERROR: - return "Generic error"; - case MCO_INVALID_POINTER: - return "Invalid pointer"; - case MCO_INVALID_COROUTINE: - return "Invalid coroutine"; - case MCO_NOT_SUSPENDED: - return "Coroutine not suspended"; - case MCO_NOT_RUNNING: - return "Coroutine not running"; - case MCO_MAKE_CONTEXT_ERROR: - return "Make context error"; - case MCO_SWITCH_CONTEXT_ERROR: - return "Switch context error"; - case MCO_NOT_ENOUGH_SPACE: - return "Not enough space"; - case MCO_OUT_OF_MEMORY: - return "Out of memory"; - case MCO_INVALID_ARGUMENTS: - return "Invalid arguments"; - case MCO_INVALID_OPERATION: - return "Invalid operation"; - default: - return "Unknown error"; - } -} - -#ifdef __cplusplus -} -#endif - -#endif /* MINICORO_IMPL */ - -/* -This software is available as a choice of the following licenses. Choose -whichever you prefer. - -=============================================================================== -ALTERNATIVE 1 - Public Domain (www.unlicense.org) -=============================================================================== -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. - -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -For more information, please refer to - -=============================================================================== -ALTERNATIVE 2 - MIT No Attribution -=============================================================================== -Copyright (c) 2021 Eduardo Bart (https://github.com/edubart/minicoro) - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ \ No newline at end of file diff --git a/framework/3rd/termbox b/framework/3rd/termbox new file mode 160000 index 0000000..d961a81 --- /dev/null +++ b/framework/3rd/termbox @@ -0,0 +1 @@ +Subproject commit d961a8122210010e7c2c8f201e61170c13d319b4 diff --git a/framework/3rd/termbox/.gitignore b/framework/3rd/termbox/.gitignore deleted file mode 100755 index afcccde..0000000 --- a/framework/3rd/termbox/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -bin -obj -src/demo/*.o -src/demo/keyboard -src/demo/output -src/demo/paint -src/demo/truecolor diff --git a/framework/3rd/termbox/makefile b/framework/3rd/termbox/makefile deleted file mode 100755 index aa2270b..0000000 --- a/framework/3rd/termbox/makefile +++ /dev/null @@ -1,40 +0,0 @@ -NAME=termbox -CC=gcc -FLAGS+=-std=c99 -pedantic -Wall -Werror -g - -OS:=$(shell uname -s) -ifeq ($(OS),Linux) - FLAGS+=-D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE -endif - -BIND=bin -SRCD=src -OBJD=obj -INCL=-I$(SRCD) - -SRCS=$(SRCD)/termbox.c -SRCS+=$(SRCD)/input.c -SRCS+=$(SRCD)/memstream.c -SRCS+=$(SRCD)/ringbuffer.c -SRCS+=$(SRCD)/term.c -SRCS+=$(SRCD)/utf8.c - -OBJS:=$(patsubst $(SRCD)/%.c,$(OBJD)/$(SRCD)/%.o,$(SRCS)) - -.PHONY:all -all:$(BIND)/$(NAME).a - -$(OBJD)/%.o:%.c - @echo "building source object $@" - @mkdir -p $(@D) - @$(CC) $(INCL) $(FLAGS) -c -o $@ $< - -$(BIND)/$(NAME).a:$(OBJS) - @echo "compiling $@" - @mkdir -p $(BIND) - @ar rvs $(BIND)/$(NAME).a $(OBJS) - -clean: - @echo "cleaning workspace" - @rm -rf $(BIND) - @rm -rf $(OBJD) diff --git a/framework/3rd/termbox/readme.md b/framework/3rd/termbox/readme.md deleted file mode 100755 index d20e68d..0000000 --- a/framework/3rd/termbox/readme.md +++ /dev/null @@ -1,57 +0,0 @@ -# Termbox -[Termbox](https://github.com/nsf/termbox) -was a promising Text User Interface (TUI) library. -Unfortunately, its original author -[changed his mind](https://github.com/nsf/termbox/issues/37#issuecomment-261075481) -about consoles and despite the -[community's efforts](https://github.com/nsf/termbox/pull/104#issuecomment-300308156) -to keep the library's development going, preferred to let it die. Before it happened, -[some people](https://wiki.musl-libc.org/alternatives.html) -already noticed the robustness of the initial architecture -[became compromised](https://github.com/nsf/termbox/commit/66c3f91b14e24510319bce6b5cc2fecf8cf5abff#commitcomment-3790714) -in a nonsensical refactoring frenzy. Now, the author refuses to merge features -like true-color support, invoking some -[spurious correlations](https://github.com/nsf/termbox/pull/104#issuecomment-300292223) -we will discuss no further. - -## The new Termbox-next -This fork was made to restore the codebase to its original quality (before -[66c3f91](https://github.com/nsf/termbox/commit/66c3f91b14e24510319bce6b5cc2fecf8cf5abff)) -while providing all the functionnalities of the current implementation. -This was achieved by branching at -[a2e217f](https://github.com/nsf/termbox/commit/a2e217f0fb97e6bbd589136ea3945f9d5a123d81) -and cherry-picking all the commits up to -[d63b83a](https://github.com/nsf/termbox/commit/d63b83af04e0fd6da836bb8f37e5cec72a1dc95a) -if they weren't harmful. - -## Changes -A lot of things changed during the process: - - *waf*, the original build system, was completely removed from the - project and replaced by make. - - anything related to python was removed as well - -## Getting started -Termbox's interface only consists of 12 functions: -``` -tb_init() // initialization -tb_shutdown() // shutdown - -tb_width() // width of the terminal screen -tb_height() // height of the terminal screen - -tb_clear() // clear buffer -tb_present() // sync internal buffer with terminal - -tb_put_cell() -tb_change_cell() -tb_blit() // drawing functions - -tb_select_input_mode() // change input mode -tb_peek_event() // peek a keyboard event -tb_poll_event() // wait for a keyboard event -``` -See src/termbox.h header file for full detail. - -## TL;DR -`make` to build a static version of the lib under bin/termbox.a -`cd src/demo && make` to build the example programs in src/demo/ diff --git a/framework/3rd/termbox/src/demo/keyboard.c b/framework/3rd/termbox/src/demo/keyboard.c deleted file mode 100755 index 3c25c5a..0000000 --- a/framework/3rd/termbox/src/demo/keyboard.c +++ /dev/null @@ -1,827 +0,0 @@ -#include -#include -#include -#include -#include "termbox.h" - -struct key -{ - unsigned char x; - unsigned char y; - uint32_t ch; -}; - -#define STOP {0,0,0} -struct key K_ESC[] = {{1, 1, 'E'}, {2, 1, 'S'}, {3, 1, 'C'}, STOP}; -struct key K_F1[] = {{6, 1, 'F'}, {7, 1, '1'}, STOP}; -struct key K_F2[] = {{9, 1, 'F'}, {10, 1, '2'}, STOP}; -struct key K_F3[] = {{12, 1, 'F'}, {13, 1, '3'}, STOP}; -struct key K_F4[] = {{15, 1, 'F'}, {16, 1, '4'}, STOP}; -struct key K_F5[] = {{19, 1, 'F'}, {20, 1, '5'}, STOP}; -struct key K_F6[] = {{22, 1, 'F'}, {23, 1, '6'}, STOP}; -struct key K_F7[] = {{25, 1, 'F'}, {26, 1, '7'}, STOP}; -struct key K_F8[] = {{28, 1, 'F'}, {29, 1, '8'}, STOP}; -struct key K_F9[] = {{33, 1, 'F'}, {34, 1, '9'}, STOP}; -struct key K_F10[] = {{36, 1, 'F'}, {37, 1, '1'}, {38, 1, '0'}, STOP}; -struct key K_F11[] = {{40, 1, 'F'}, {41, 1, '1'}, {42, 1, '1'}, STOP}; -struct key K_F12[] = {{44, 1, 'F'}, {45, 1, '1'}, {46, 1, '2'}, STOP}; -struct key K_PRN[] = {{50, 1, 'P'}, {51, 1, 'R'}, {52, 1, 'N'}, STOP}; -struct key K_SCR[] = {{54, 1, 'S'}, {55, 1, 'C'}, {56, 1, 'R'}, STOP}; -struct key K_BRK[] = {{58, 1, 'B'}, {59, 1, 'R'}, {60, 1, 'K'}, STOP}; -struct key K_LED1[] = {{66, 1, '-'}, STOP}; -struct key K_LED2[] = {{70, 1, '-'}, STOP}; -struct key K_LED3[] = {{74, 1, '-'}, STOP}; - -struct key K_TILDE[] = {{1, 4, '`'}, STOP}; -struct key K_TILDE_SHIFT[] = {{1, 4, '~'}, STOP}; -struct key K_1[] = {{4, 4, '1'}, STOP}; -struct key K_1_SHIFT[] = {{4, 4, '!'}, STOP}; -struct key K_2[] = {{7, 4, '2'}, STOP}; -struct key K_2_SHIFT[] = {{7, 4, '@'}, STOP}; -struct key K_3[] = {{10, 4, '3'}, STOP}; -struct key K_3_SHIFT[] = {{10, 4, '#'}, STOP}; -struct key K_4[] = {{13, 4, '4'}, STOP}; -struct key K_4_SHIFT[] = {{13, 4, '$'}, STOP}; -struct key K_5[] = {{16, 4, '5'}, STOP}; -struct key K_5_SHIFT[] = {{16, 4, '%'}, STOP}; -struct key K_6[] = {{19, 4, '6'}, STOP}; -struct key K_6_SHIFT[] = {{19, 4, '^'}, STOP}; -struct key K_7[] = {{22, 4, '7'}, STOP}; -struct key K_7_SHIFT[] = {{22, 4, '&'}, STOP}; -struct key K_8[] = {{25, 4, '8'}, STOP}; -struct key K_8_SHIFT[] = {{25, 4, '*'}, STOP}; -struct key K_9[] = {{28, 4, '9'}, STOP}; -struct key K_9_SHIFT[] = {{28, 4, '('}, STOP}; -struct key K_0[] = {{31, 4, '0'}, STOP}; -struct key K_0_SHIFT[] = {{31, 4, ')'}, STOP}; -struct key K_MINUS[] = {{34, 4, '-'}, STOP}; -struct key K_MINUS_SHIFT[] = {{34, 4, '_'}, STOP}; -struct key K_EQUALS[] = {{37, 4, '='}, STOP}; -struct key K_EQUALS_SHIFT[] = {{37, 4, '+'}, STOP}; -struct key K_BACKSLASH[] = {{40, 4, '\\'}, STOP}; -struct key K_BACKSLASH_SHIFT[] = {{40, 4, '|'}, STOP}; -struct key K_BACKSPACE[] = {{44, 4, 0x2190}, {45, 4, 0x2500}, {46, 4, 0x2500}, STOP}; -struct key K_INS[] = {{50, 4, 'I'}, {51, 4, 'N'}, {52, 4, 'S'}, STOP}; -struct key K_HOM[] = {{54, 4, 'H'}, {55, 4, 'O'}, {56, 4, 'M'}, STOP}; -struct key K_PGU[] = {{58, 4, 'P'}, {59, 4, 'G'}, {60, 4, 'U'}, STOP}; -struct key K_K_NUMLOCK[] = {{65, 4, 'N'}, STOP}; -struct key K_K_SLASH[] = {{68, 4, '/'}, STOP}; -struct key K_K_STAR[] = {{71, 4, '*'}, STOP}; -struct key K_K_MINUS[] = {{74, 4, '-'}, STOP}; - -struct key K_TAB[] = {{1, 6, 'T'}, {2, 6, 'A'}, {3, 6, 'B'}, STOP}; -struct key K_q[] = {{6, 6, 'q'}, STOP}; -struct key K_Q[] = {{6, 6, 'Q'}, STOP}; -struct key K_w[] = {{9, 6, 'w'}, STOP}; -struct key K_W[] = {{9, 6, 'W'}, STOP}; -struct key K_e[] = {{12, 6, 'e'}, STOP}; -struct key K_E[] = {{12, 6, 'E'}, STOP}; -struct key K_r[] = {{15, 6, 'r'}, STOP}; -struct key K_R[] = {{15, 6, 'R'}, STOP}; -struct key K_t[] = {{18, 6, 't'}, STOP}; -struct key K_T[] = {{18, 6, 'T'}, STOP}; -struct key K_y[] = {{21, 6, 'y'}, STOP}; -struct key K_Y[] = {{21, 6, 'Y'}, STOP}; -struct key K_u[] = {{24, 6, 'u'}, STOP}; -struct key K_U[] = {{24, 6, 'U'}, STOP}; -struct key K_i[] = {{27, 6, 'i'}, STOP}; -struct key K_I[] = {{27, 6, 'I'}, STOP}; -struct key K_o[] = {{30, 6, 'o'}, STOP}; -struct key K_O[] = {{30, 6, 'O'}, STOP}; -struct key K_p[] = {{33, 6, 'p'}, STOP}; -struct key K_P[] = {{33, 6, 'P'}, STOP}; -struct key K_LSQB[] = {{36, 6, '['}, STOP}; -struct key K_LCUB[] = {{36, 6, '{'}, STOP}; -struct key K_RSQB[] = {{39, 6, ']'}, STOP}; -struct key K_RCUB[] = {{39, 6, '}'}, STOP}; -struct key K_ENTER[] = -{ - {43, 6, 0x2591}, {44, 6, 0x2591}, {45, 6, 0x2591}, {46, 6, 0x2591}, - {43, 7, 0x2591}, {44, 7, 0x2591}, {45, 7, 0x21B5}, {46, 7, 0x2591}, - {41, 8, 0x2591}, {42, 8, 0x2591}, {43, 8, 0x2591}, {44, 8, 0x2591}, - {45, 8, 0x2591}, {46, 8, 0x2591}, STOP -}; -struct key K_DEL[] = {{50, 6, 'D'}, {51, 6, 'E'}, {52, 6, 'L'}, STOP}; -struct key K_END[] = {{54, 6, 'E'}, {55, 6, 'N'}, {56, 6, 'D'}, STOP}; -struct key K_PGD[] = {{58, 6, 'P'}, {59, 6, 'G'}, {60, 6, 'D'}, STOP}; -struct key K_K_7[] = {{65, 6, '7'}, STOP}; -struct key K_K_8[] = {{68, 6, '8'}, STOP}; -struct key K_K_9[] = {{71, 6, '9'}, STOP}; -struct key K_K_PLUS[] = {{74, 6, ' '}, {74, 7, '+'}, {74, 8, ' '}, STOP}; - -struct key K_CAPS[] = {{1, 8, 'C'}, {2, 8, 'A'}, {3, 8, 'P'}, {4, 8, 'S'}, STOP}; -struct key K_a[] = {{7, 8, 'a'}, STOP}; -struct key K_A[] = {{7, 8, 'A'}, STOP}; -struct key K_s[] = {{10, 8, 's'}, STOP}; -struct key K_S[] = {{10, 8, 'S'}, STOP}; -struct key K_d[] = {{13, 8, 'd'}, STOP}; -struct key K_D[] = {{13, 8, 'D'}, STOP}; -struct key K_f[] = {{16, 8, 'f'}, STOP}; -struct key K_F[] = {{16, 8, 'F'}, STOP}; -struct key K_g[] = {{19, 8, 'g'}, STOP}; -struct key K_G[] = {{19, 8, 'G'}, STOP}; -struct key K_h[] = {{22, 8, 'h'}, STOP}; -struct key K_H[] = {{22, 8, 'H'}, STOP}; -struct key K_j[] = {{25, 8, 'j'}, STOP}; -struct key K_J[] = {{25, 8, 'J'}, STOP}; -struct key K_k[] = {{28, 8, 'k'}, STOP}; -struct key K_K[] = {{28, 8, 'K'}, STOP}; -struct key K_l[] = {{31, 8, 'l'}, STOP}; -struct key K_L[] = {{31, 8, 'L'}, STOP}; -struct key K_SEMICOLON[] = {{34, 8, ';'}, STOP}; -struct key K_PARENTHESIS[] = {{34, 8, ':'}, STOP}; -struct key K_QUOTE[] = {{37, 8, '\''}, STOP}; -struct key K_DOUBLEQUOTE[] = {{37, 8, '"'}, STOP}; -struct key K_K_4[] = {{65, 8, '4'}, STOP}; -struct key K_K_5[] = {{68, 8, '5'}, STOP}; -struct key K_K_6[] = {{71, 8, '6'}, STOP}; - -struct key K_LSHIFT[] = {{1, 10, 'S'}, {2, 10, 'H'}, {3, 10, 'I'}, {4, 10, 'F'}, {5, 10, 'T'}, STOP}; -struct key K_z[] = {{9, 10, 'z'}, STOP}; -struct key K_Z[] = {{9, 10, 'Z'}, STOP}; -struct key K_x[] = {{12, 10, 'x'}, STOP}; -struct key K_X[] = {{12, 10, 'X'}, STOP}; -struct key K_c[] = {{15, 10, 'c'}, STOP}; -struct key K_C[] = {{15, 10, 'C'}, STOP}; -struct key K_v[] = {{18, 10, 'v'}, STOP}; -struct key K_V[] = {{18, 10, 'V'}, STOP}; -struct key K_b[] = {{21, 10, 'b'}, STOP}; -struct key K_B[] = {{21, 10, 'B'}, STOP}; -struct key K_n[] = {{24, 10, 'n'}, STOP}; -struct key K_N[] = {{24, 10, 'N'}, STOP}; -struct key K_m[] = {{27, 10, 'm'}, STOP}; -struct key K_M[] = {{27, 10, 'M'}, STOP}; -struct key K_COMMA[] = {{30, 10, ','}, STOP}; -struct key K_LANB[] = {{30, 10, '<'}, STOP}; -struct key K_PERIOD[] = {{33, 10, '.'}, STOP}; -struct key K_RANB[] = {{33, 10, '>'}, STOP}; -struct key K_SLASH[] = {{36, 10, '/'}, STOP}; -struct key K_QUESTION[] = {{36, 10, '?'}, STOP}; -struct key K_RSHIFT[] = {{42, 10, 'S'}, {43, 10, 'H'}, {44, 10, 'I'}, {45, 10, 'F'}, {46, 10, 'T'}, STOP}; -struct key K_ARROW_UP[] = {{54, 10, '('}, {55, 10, 0x2191}, {56, 10, ')'}, STOP}; -struct key K_K_1[] = {{65, 10, '1'}, STOP}; -struct key K_K_2[] = {{68, 10, '2'}, STOP}; -struct key K_K_3[] = {{71, 10, '3'}, STOP}; -struct key K_K_ENTER[] = {{74, 10, 0x2591}, {74, 11, 0x2591}, {74, 12, 0x2591}, STOP}; - -struct key K_LCTRL[] = {{1, 12, 'C'}, {2, 12, 'T'}, {3, 12, 'R'}, {4, 12, 'L'}, STOP}; -struct key K_LWIN[] = {{6, 12, 'W'}, {7, 12, 'I'}, {8, 12, 'N'}, STOP}; -struct key K_LALT[] = {{10, 12, 'A'}, {11, 12, 'L'}, {12, 12, 'T'}, STOP}; -struct key K_SPACE[] = -{ - {14, 12, ' '}, {15, 12, ' '}, {16, 12, ' '}, {17, 12, ' '}, {18, 12, ' '}, - {19, 12, 'S'}, {20, 12, 'P'}, {21, 12, 'A'}, {22, 12, 'C'}, {23, 12, 'E'}, - {24, 12, ' '}, {25, 12, ' '}, {26, 12, ' '}, {27, 12, ' '}, {28, 12, ' '}, - STOP -}; -struct key K_RALT[] = {{30, 12, 'A'}, {31, 12, 'L'}, {32, 12, 'T'}, STOP}; -struct key K_RWIN[] = {{34, 12, 'W'}, {35, 12, 'I'}, {36, 12, 'N'}, STOP}; -struct key K_RPROP[] = {{38, 12, 'P'}, {39, 12, 'R'}, {40, 12, 'O'}, {41, 12, 'P'}, STOP}; -struct key K_RCTRL[] = {{43, 12, 'C'}, {44, 12, 'T'}, {45, 12, 'R'}, {46, 12, 'L'}, STOP}; -struct key K_ARROW_LEFT[] = {{50, 12, '('}, {51, 12, 0x2190}, {52, 12, ')'}, STOP}; -struct key K_ARROW_DOWN[] = {{54, 12, '('}, {55, 12, 0x2193}, {56, 12, ')'}, STOP}; -struct key K_ARROW_RIGHT[] = {{58, 12, '('}, {59, 12, 0x2192}, {60, 12, ')'}, STOP}; -struct key K_K_0[] = {{65, 12, ' '}, {66, 12, '0'}, {67, 12, ' '}, {68, 12, ' '}, STOP}; -struct key K_K_PERIOD[] = {{71, 12, '.'}, STOP}; - -struct combo -{ - struct key* keys[6]; -}; - -struct combo combos[] = -{ - {{K_TILDE, K_2, K_LCTRL, K_RCTRL, 0}}, - {{K_A, K_LCTRL, K_RCTRL, 0}}, - {{K_B, K_LCTRL, K_RCTRL, 0}}, - {{K_C, K_LCTRL, K_RCTRL, 0}}, - {{K_D, K_LCTRL, K_RCTRL, 0}}, - {{K_E, K_LCTRL, K_RCTRL, 0}}, - {{K_F, K_LCTRL, K_RCTRL, 0}}, - {{K_G, K_LCTRL, K_RCTRL, 0}}, - {{K_H, K_BACKSPACE, K_LCTRL, K_RCTRL, 0}}, - {{K_I, K_TAB, K_LCTRL, K_RCTRL, 0}}, - {{K_J, K_LCTRL, K_RCTRL, 0}}, - {{K_K, K_LCTRL, K_RCTRL, 0}}, - {{K_L, K_LCTRL, K_RCTRL, 0}}, - {{K_M, K_ENTER, K_K_ENTER, K_LCTRL, K_RCTRL, 0}}, - {{K_N, K_LCTRL, K_RCTRL, 0}}, - {{K_O, K_LCTRL, K_RCTRL, 0}}, - {{K_P, K_LCTRL, K_RCTRL, 0}}, - {{K_Q, K_LCTRL, K_RCTRL, 0}}, - {{K_R, K_LCTRL, K_RCTRL, 0}}, - {{K_S, K_LCTRL, K_RCTRL, 0}}, - {{K_T, K_LCTRL, K_RCTRL, 0}}, - {{K_U, K_LCTRL, K_RCTRL, 0}}, - {{K_V, K_LCTRL, K_RCTRL, 0}}, - {{K_W, K_LCTRL, K_RCTRL, 0}}, - {{K_X, K_LCTRL, K_RCTRL, 0}}, - {{K_Y, K_LCTRL, K_RCTRL, 0}}, - {{K_Z, K_LCTRL, K_RCTRL, 0}}, - {{K_LSQB, K_ESC, K_3, K_LCTRL, K_RCTRL, 0}}, - {{K_4, K_BACKSLASH, K_LCTRL, K_RCTRL, 0}}, - {{K_RSQB, K_5, K_LCTRL, K_RCTRL, 0}}, - {{K_6, K_LCTRL, K_RCTRL, 0}}, - {{K_7, K_SLASH, K_MINUS_SHIFT, K_LCTRL, K_RCTRL, 0}}, - {{K_SPACE, 0}}, - {{K_1_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, - {{K_DOUBLEQUOTE, K_LSHIFT, K_RSHIFT, 0}}, - {{K_3_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, - {{K_4_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, - {{K_5_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, - {{K_7_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, - {{K_QUOTE, 0}}, - {{K_9_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, - {{K_0_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, - {{K_8_SHIFT, K_K_STAR, K_LSHIFT, K_RSHIFT, 0}}, - {{K_EQUALS_SHIFT, K_K_PLUS, K_LSHIFT, K_RSHIFT, 0}}, - {{K_COMMA, 0}}, - {{K_MINUS, K_K_MINUS, 0}}, - {{K_PERIOD, K_K_PERIOD, 0}}, - {{K_SLASH, K_K_SLASH, 0}}, - {{K_0, K_K_0, 0}}, - {{K_1, K_K_1, 0}}, - {{K_2, K_K_2, 0}}, - {{K_3, K_K_3, 0}}, - {{K_4, K_K_4, 0}}, - {{K_5, K_K_5, 0}}, - {{K_6, K_K_6, 0}}, - {{K_7, K_K_7, 0}}, - {{K_8, K_K_8, 0}}, - {{K_9, K_K_9, 0}}, - {{K_PARENTHESIS, K_LSHIFT, K_RSHIFT, 0}}, - {{K_SEMICOLON, 0}}, - {{K_LANB, K_LSHIFT, K_RSHIFT, 0}}, - {{K_EQUALS, 0}}, - {{K_RANB, K_LSHIFT, K_RSHIFT, 0}}, - {{K_QUESTION, K_LSHIFT, K_RSHIFT, 0}}, - {{K_2_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, - {{K_A, K_LSHIFT, K_RSHIFT, 0}}, - {{K_B, K_LSHIFT, K_RSHIFT, 0}}, - {{K_C, K_LSHIFT, K_RSHIFT, 0}}, - {{K_D, K_LSHIFT, K_RSHIFT, 0}}, - {{K_E, K_LSHIFT, K_RSHIFT, 0}}, - {{K_F, K_LSHIFT, K_RSHIFT, 0}}, - {{K_G, K_LSHIFT, K_RSHIFT, 0}}, - {{K_H, K_LSHIFT, K_RSHIFT, 0}}, - {{K_I, K_LSHIFT, K_RSHIFT, 0}}, - {{K_J, K_LSHIFT, K_RSHIFT, 0}}, - {{K_K, K_LSHIFT, K_RSHIFT, 0}}, - {{K_L, K_LSHIFT, K_RSHIFT, 0}}, - {{K_M, K_LSHIFT, K_RSHIFT, 0}}, - {{K_N, K_LSHIFT, K_RSHIFT, 0}}, - {{K_O, K_LSHIFT, K_RSHIFT, 0}}, - {{K_P, K_LSHIFT, K_RSHIFT, 0}}, - {{K_Q, K_LSHIFT, K_RSHIFT, 0}}, - {{K_R, K_LSHIFT, K_RSHIFT, 0}}, - {{K_S, K_LSHIFT, K_RSHIFT, 0}}, - {{K_T, K_LSHIFT, K_RSHIFT, 0}}, - {{K_U, K_LSHIFT, K_RSHIFT, 0}}, - {{K_V, K_LSHIFT, K_RSHIFT, 0}}, - {{K_W, K_LSHIFT, K_RSHIFT, 0}}, - {{K_X, K_LSHIFT, K_RSHIFT, 0}}, - {{K_Y, K_LSHIFT, K_RSHIFT, 0}}, - {{K_Z, K_LSHIFT, K_RSHIFT, 0}}, - {{K_LSQB, 0}}, - {{K_BACKSLASH, 0}}, - {{K_RSQB, 0}}, - {{K_6_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, - {{K_MINUS_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, - {{K_TILDE, 0}}, - {{K_a, 0}}, - {{K_b, 0}}, - {{K_c, 0}}, - {{K_d, 0}}, - {{K_e, 0}}, - {{K_f, 0}}, - {{K_g, 0}}, - {{K_h, 0}}, - {{K_i, 0}}, - {{K_j, 0}}, - {{K_k, 0}}, - {{K_l, 0}}, - {{K_m, 0}}, - {{K_n, 0}}, - {{K_o, 0}}, - {{K_p, 0}}, - {{K_q, 0}}, - {{K_r, 0}}, - {{K_s, 0}}, - {{K_t, 0}}, - {{K_u, 0}}, - {{K_v, 0}}, - {{K_w, 0}}, - {{K_x, 0}}, - {{K_y, 0}}, - {{K_z, 0}}, - {{K_LCUB, K_LSHIFT, K_RSHIFT, 0}}, - {{K_BACKSLASH_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, - {{K_RCUB, K_LSHIFT, K_RSHIFT, 0}}, - {{K_TILDE_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, - {{K_8, K_BACKSPACE, K_LCTRL, K_RCTRL, 0}} -}; - -struct combo func_combos[] = -{ - {{K_F1, 0}}, - {{K_F2, 0}}, - {{K_F3, 0}}, - {{K_F4, 0}}, - {{K_F5, 0}}, - {{K_F6, 0}}, - {{K_F7, 0}}, - {{K_F8, 0}}, - {{K_F9, 0}}, - {{K_F10, 0}}, - {{K_F11, 0}}, - {{K_F12, 0}}, - {{K_INS, 0}}, - {{K_DEL, 0}}, - {{K_HOM, 0}}, - {{K_END, 0}}, - {{K_PGU, 0}}, - {{K_PGD, 0}}, - {{K_ARROW_UP, 0}}, - {{K_ARROW_DOWN, 0}}, - {{K_ARROW_LEFT, 0}}, - {{K_ARROW_RIGHT, 0}} -}; - -void print_tb(const char* str, int x, int y, uint32_t fg, uint32_t bg) -{ - while (*str) - { - uint32_t uni; - str += utf8_char_to_unicode(&uni, str); - tb_change_cell(x, y, uni, fg, bg); - x++; - } -} - -void printf_tb(int x, int y, uint32_t fg, uint32_t bg, const char* fmt, ...) -{ - char buf[4096]; - va_list vl; - va_start(vl, fmt); - vsnprintf(buf, sizeof(buf), fmt, vl); - va_end(vl); - print_tb(buf, x, y, fg, bg); -} - -void draw_key(struct key* k, uint32_t fg, uint32_t bg) -{ - while (k->x) - { - tb_change_cell(k->x + 2, k->y + 4, k->ch, fg, bg); - k++; - } -} - -void draw_keyboard() -{ - int i; - tb_change_cell(0, 0, 0x250C, TB_WHITE, TB_DEFAULT); - tb_change_cell(79, 0, 0x2510, TB_WHITE, TB_DEFAULT); - tb_change_cell(0, 23, 0x2514, TB_WHITE, TB_DEFAULT); - tb_change_cell(79, 23, 0x2518, TB_WHITE, TB_DEFAULT); - - for (i = 1; i < 79; ++i) - { - tb_change_cell(i, 0, 0x2500, TB_WHITE, TB_DEFAULT); - tb_change_cell(i, 23, 0x2500, TB_WHITE, TB_DEFAULT); - tb_change_cell(i, 17, 0x2500, TB_WHITE, TB_DEFAULT); - tb_change_cell(i, 4, 0x2500, TB_WHITE, TB_DEFAULT); - } - - for (i = 1; i < 23; ++i) - { - tb_change_cell(0, i, 0x2502, TB_WHITE, TB_DEFAULT); - tb_change_cell(79, i, 0x2502, TB_WHITE, TB_DEFAULT); - } - - tb_change_cell(0, 17, 0x251C, TB_WHITE, TB_DEFAULT); - tb_change_cell(79, 17, 0x2524, TB_WHITE, TB_DEFAULT); - tb_change_cell(0, 4, 0x251C, TB_WHITE, TB_DEFAULT); - tb_change_cell(79, 4, 0x2524, TB_WHITE, TB_DEFAULT); - - for (i = 5; i < 17; ++i) - { - tb_change_cell(1, i, 0x2588, TB_YELLOW, TB_YELLOW); - tb_change_cell(78, i, 0x2588, TB_YELLOW, TB_YELLOW); - } - - draw_key(K_ESC, TB_WHITE, TB_BLUE); - draw_key(K_F1, TB_WHITE, TB_BLUE); - draw_key(K_F2, TB_WHITE, TB_BLUE); - draw_key(K_F3, TB_WHITE, TB_BLUE); - draw_key(K_F4, TB_WHITE, TB_BLUE); - draw_key(K_F5, TB_WHITE, TB_BLUE); - draw_key(K_F6, TB_WHITE, TB_BLUE); - draw_key(K_F7, TB_WHITE, TB_BLUE); - draw_key(K_F8, TB_WHITE, TB_BLUE); - draw_key(K_F9, TB_WHITE, TB_BLUE); - draw_key(K_F10, TB_WHITE, TB_BLUE); - draw_key(K_F11, TB_WHITE, TB_BLUE); - draw_key(K_F12, TB_WHITE, TB_BLUE); - draw_key(K_PRN, TB_WHITE, TB_BLUE); - draw_key(K_SCR, TB_WHITE, TB_BLUE); - draw_key(K_BRK, TB_WHITE, TB_BLUE); - draw_key(K_LED1, TB_WHITE, TB_BLUE); - draw_key(K_LED2, TB_WHITE, TB_BLUE); - draw_key(K_LED3, TB_WHITE, TB_BLUE); - - draw_key(K_TILDE, TB_WHITE, TB_BLUE); - draw_key(K_1, TB_WHITE, TB_BLUE); - draw_key(K_2, TB_WHITE, TB_BLUE); - draw_key(K_3, TB_WHITE, TB_BLUE); - draw_key(K_4, TB_WHITE, TB_BLUE); - draw_key(K_5, TB_WHITE, TB_BLUE); - draw_key(K_6, TB_WHITE, TB_BLUE); - draw_key(K_7, TB_WHITE, TB_BLUE); - draw_key(K_8, TB_WHITE, TB_BLUE); - draw_key(K_9, TB_WHITE, TB_BLUE); - draw_key(K_0, TB_WHITE, TB_BLUE); - draw_key(K_MINUS, TB_WHITE, TB_BLUE); - draw_key(K_EQUALS, TB_WHITE, TB_BLUE); - draw_key(K_BACKSLASH, TB_WHITE, TB_BLUE); - draw_key(K_BACKSPACE, TB_WHITE, TB_BLUE); - draw_key(K_INS, TB_WHITE, TB_BLUE); - draw_key(K_HOM, TB_WHITE, TB_BLUE); - draw_key(K_PGU, TB_WHITE, TB_BLUE); - draw_key(K_K_NUMLOCK, TB_WHITE, TB_BLUE); - draw_key(K_K_SLASH, TB_WHITE, TB_BLUE); - draw_key(K_K_STAR, TB_WHITE, TB_BLUE); - draw_key(K_K_MINUS, TB_WHITE, TB_BLUE); - - draw_key(K_TAB, TB_WHITE, TB_BLUE); - draw_key(K_q, TB_WHITE, TB_BLUE); - draw_key(K_w, TB_WHITE, TB_BLUE); - draw_key(K_e, TB_WHITE, TB_BLUE); - draw_key(K_r, TB_WHITE, TB_BLUE); - draw_key(K_t, TB_WHITE, TB_BLUE); - draw_key(K_y, TB_WHITE, TB_BLUE); - draw_key(K_u, TB_WHITE, TB_BLUE); - draw_key(K_i, TB_WHITE, TB_BLUE); - draw_key(K_o, TB_WHITE, TB_BLUE); - draw_key(K_p, TB_WHITE, TB_BLUE); - draw_key(K_LSQB, TB_WHITE, TB_BLUE); - draw_key(K_RSQB, TB_WHITE, TB_BLUE); - draw_key(K_ENTER, TB_WHITE, TB_BLUE); - draw_key(K_DEL, TB_WHITE, TB_BLUE); - draw_key(K_END, TB_WHITE, TB_BLUE); - draw_key(K_PGD, TB_WHITE, TB_BLUE); - draw_key(K_K_7, TB_WHITE, TB_BLUE); - draw_key(K_K_8, TB_WHITE, TB_BLUE); - draw_key(K_K_9, TB_WHITE, TB_BLUE); - draw_key(K_K_PLUS, TB_WHITE, TB_BLUE); - - draw_key(K_CAPS, TB_WHITE, TB_BLUE); - draw_key(K_a, TB_WHITE, TB_BLUE); - draw_key(K_s, TB_WHITE, TB_BLUE); - draw_key(K_d, TB_WHITE, TB_BLUE); - draw_key(K_f, TB_WHITE, TB_BLUE); - draw_key(K_g, TB_WHITE, TB_BLUE); - draw_key(K_h, TB_WHITE, TB_BLUE); - draw_key(K_j, TB_WHITE, TB_BLUE); - draw_key(K_k, TB_WHITE, TB_BLUE); - draw_key(K_l, TB_WHITE, TB_BLUE); - draw_key(K_SEMICOLON, TB_WHITE, TB_BLUE); - draw_key(K_QUOTE, TB_WHITE, TB_BLUE); - draw_key(K_K_4, TB_WHITE, TB_BLUE); - draw_key(K_K_5, TB_WHITE, TB_BLUE); - draw_key(K_K_6, TB_WHITE, TB_BLUE); - - draw_key(K_LSHIFT, TB_WHITE, TB_BLUE); - draw_key(K_z, TB_WHITE, TB_BLUE); - draw_key(K_x, TB_WHITE, TB_BLUE); - draw_key(K_c, TB_WHITE, TB_BLUE); - draw_key(K_v, TB_WHITE, TB_BLUE); - draw_key(K_b, TB_WHITE, TB_BLUE); - draw_key(K_n, TB_WHITE, TB_BLUE); - draw_key(K_m, TB_WHITE, TB_BLUE); - draw_key(K_COMMA, TB_WHITE, TB_BLUE); - draw_key(K_PERIOD, TB_WHITE, TB_BLUE); - draw_key(K_SLASH, TB_WHITE, TB_BLUE); - draw_key(K_RSHIFT, TB_WHITE, TB_BLUE); - draw_key(K_ARROW_UP, TB_WHITE, TB_BLUE); - draw_key(K_K_1, TB_WHITE, TB_BLUE); - draw_key(K_K_2, TB_WHITE, TB_BLUE); - draw_key(K_K_3, TB_WHITE, TB_BLUE); - draw_key(K_K_ENTER, TB_WHITE, TB_BLUE); - - draw_key(K_LCTRL, TB_WHITE, TB_BLUE); - draw_key(K_LWIN, TB_WHITE, TB_BLUE); - draw_key(K_LALT, TB_WHITE, TB_BLUE); - draw_key(K_SPACE, TB_WHITE, TB_BLUE); - draw_key(K_RCTRL, TB_WHITE, TB_BLUE); - draw_key(K_RPROP, TB_WHITE, TB_BLUE); - draw_key(K_RWIN, TB_WHITE, TB_BLUE); - draw_key(K_RALT, TB_WHITE, TB_BLUE); - draw_key(K_ARROW_LEFT, TB_WHITE, TB_BLUE); - draw_key(K_ARROW_DOWN, TB_WHITE, TB_BLUE); - draw_key(K_ARROW_RIGHT, TB_WHITE, TB_BLUE); - draw_key(K_K_0, TB_WHITE, TB_BLUE); - draw_key(K_K_PERIOD, TB_WHITE, TB_BLUE); - - printf_tb(33, 1, TB_MAGENTA | TB_BOLD, TB_DEFAULT, "Keyboard demo!"); - printf_tb(21, 2, TB_MAGENTA, TB_DEFAULT, - "(press CTRL+X and then CTRL+Q to exit)"); - printf_tb(15, 3, TB_MAGENTA, TB_DEFAULT, - "(press CTRL+X and then CTRL+C to change input mode)"); - - int inputmode = tb_select_input_mode(0); - char inputmode_str[64]; - - if (inputmode & TB_INPUT_ESC) - { - sprintf(inputmode_str, "TB_INPUT_ESC"); - } - - if (inputmode & TB_INPUT_ALT) - { - sprintf(inputmode_str, "TB_INPUT_ALT"); - } - - if (inputmode & TB_INPUT_MOUSE) - { - sprintf(inputmode_str + 12, " | TB_INPUT_MOUSE"); - } - - printf_tb(3, 18, TB_WHITE, TB_DEFAULT, "Input mode: %s", inputmode_str); -} - -const char* funckeymap(int k) -{ - static const char* fcmap[] = - { - "CTRL+2, CTRL+~", - "CTRL+A", - "CTRL+B", - "CTRL+C", - "CTRL+D", - "CTRL+E", - "CTRL+F", - "CTRL+G", - "CTRL+H, BACKSPACE", - "CTRL+I, TAB", - "CTRL+J", - "CTRL+K", - "CTRL+L", - "CTRL+M, ENTER", - "CTRL+N", - "CTRL+O", - "CTRL+P", - "CTRL+Q", - "CTRL+R", - "CTRL+S", - "CTRL+T", - "CTRL+U", - "CTRL+V", - "CTRL+W", - "CTRL+X", - "CTRL+Y", - "CTRL+Z", - "CTRL+3, ESC, CTRL+[", - "CTRL+4, CTRL+\\", - "CTRL+5, CTRL+]", - "CTRL+6", - "CTRL+7, CTRL+/, CTRL+_", - "SPACE" - }; - static const char* fkmap[] = - { - "F1", - "F2", - "F3", - "F4", - "F5", - "F6", - "F7", - "F8", - "F9", - "F10", - "F11", - "F12", - "INSERT", - "DELETE", - "HOME", - "END", - "PGUP", - "PGDN", - "ARROW UP", - "ARROW DOWN", - "ARROW LEFT", - "ARROW RIGHT" - }; - - if (k == TB_KEY_CTRL_8) - { - return "CTRL+8, BACKSPACE 2"; // 0x7F - } - else if (k >= TB_KEY_ARROW_RIGHT && k <= 0xFFFF) - { - return fkmap[0xFFFF - k]; - } - else if (k <= TB_KEY_SPACE) - { - return fcmap[k]; - } - - return "UNKNOWN"; -} - -void pretty_print_press(struct tb_event* ev) -{ - char buf[7]; - buf[utf8_unicode_to_char(buf, ev->ch)] = '\0'; - printf_tb(3, 19, TB_WHITE, TB_DEFAULT, "Key: "); - printf_tb(8, 19, TB_YELLOW, TB_DEFAULT, "decimal: %d", ev->key); - printf_tb(8, 20, TB_GREEN, TB_DEFAULT, "hex: 0x%X", ev->key); - printf_tb(8, 21, TB_CYAN, TB_DEFAULT, "octal: 0%o", ev->key); - printf_tb(8, 22, TB_RED, TB_DEFAULT, "string: %s", funckeymap(ev->key)); - - printf_tb(54, 19, TB_WHITE, TB_DEFAULT, "Char: "); - printf_tb(60, 19, TB_YELLOW, TB_DEFAULT, "decimal: %d", ev->ch); - printf_tb(60, 20, TB_GREEN, TB_DEFAULT, "hex: 0x%X", ev->ch); - printf_tb(60, 21, TB_CYAN, TB_DEFAULT, "octal: 0%o", ev->ch); - printf_tb(60, 22, TB_RED, TB_DEFAULT, "string: %s", buf); - - printf_tb(54, 18, TB_WHITE, TB_DEFAULT, "Modifier: %s", - (ev->mod) ? "TB_MOD_ALT" : "none"); - -} - -void pretty_print_resize(struct tb_event* ev) -{ - printf_tb(3, 19, TB_WHITE, TB_DEFAULT, "Resize event: %d x %d", ev->w, ev->h); -} - -int counter = 0; - -void pretty_print_mouse(struct tb_event* ev) -{ - printf_tb(3, 19, TB_WHITE, TB_DEFAULT, "Mouse event: %d x %d", ev->x, ev->y); - char* btn = ""; - - switch (ev->key) - { - case TB_KEY_MOUSE_LEFT: - btn = "MouseLeft: %d"; - break; - - case TB_KEY_MOUSE_MIDDLE: - btn = "MouseMiddle: %d"; - break; - - case TB_KEY_MOUSE_RIGHT: - btn = "MouseRight: %d"; - break; - - case TB_KEY_MOUSE_WHEEL_UP: - btn = "MouseWheelUp: %d"; - break; - - case TB_KEY_MOUSE_WHEEL_DOWN: - btn = "MouseWheelDown: %d"; - break; - - case TB_KEY_MOUSE_RELEASE: - btn = "MouseRelease: %d"; - } - - counter++; - printf_tb(43, 19, TB_WHITE, TB_DEFAULT, "Key: "); - printf_tb(48, 19, TB_YELLOW, TB_DEFAULT, btn, counter); -} - -void dispatch_press(struct tb_event* ev) -{ - if (ev->mod & TB_MOD_ALT) - { - draw_key(K_LALT, TB_WHITE, TB_RED); - draw_key(K_RALT, TB_WHITE, TB_RED); - } - - struct combo* k = 0; - - if (ev->key >= TB_KEY_ARROW_RIGHT) - { - k = &func_combos[0xFFFF - ev->key]; - } - else if (ev->ch < 128) - { - if (ev->ch == 0 && ev->key < 128) - { - k = &combos[ev->key]; - } - else - { - k = &combos[ev->ch]; - } - } - - if (!k) - { - return; - } - - struct key** keys = k->keys; - - while (*keys) - { - draw_key(*keys, TB_WHITE, TB_RED); - keys++; - } -} - -int main(int argc, char** argv) -{ - (void) argc; - (void) argv; - int ret; - - ret = tb_init(); - - if (ret) - { - fprintf(stderr, "tb_init() failed with error code %d\n", ret); - return 1; - } - - tb_select_input_mode(TB_INPUT_ESC | TB_INPUT_MOUSE); - struct tb_event ev; - - tb_clear(); - draw_keyboard(); - tb_present(); - int inputmode = 0; - int ctrlxpressed = 0; - - while (tb_poll_event(&ev)) - { - switch (ev.type) - { - case TB_EVENT_KEY: - if (ev.key == TB_KEY_CTRL_Q && ctrlxpressed) - { - tb_shutdown(); - return 0; - } - - if (ev.key == TB_KEY_CTRL_C && ctrlxpressed) - { - static int chmap[] = - { - TB_INPUT_ESC | TB_INPUT_MOUSE, // 101 - TB_INPUT_ALT | TB_INPUT_MOUSE, // 110 - TB_INPUT_ESC, // 001 - TB_INPUT_ALT, // 010 - }; - inputmode++; - - if (inputmode >= 4) - { - inputmode = 0; - } - - tb_select_input_mode(chmap[inputmode]); - } - - if (ev.key == TB_KEY_CTRL_X) - { - ctrlxpressed = 1; - } - else - { - ctrlxpressed = 0; - } - - tb_clear(); - draw_keyboard(); - dispatch_press(&ev); - pretty_print_press(&ev); - tb_present(); - break; - - case TB_EVENT_RESIZE: - tb_clear(); - draw_keyboard(); - pretty_print_resize(&ev); - tb_present(); - break; - - case TB_EVENT_MOUSE: - tb_clear(); - draw_keyboard(); - pretty_print_mouse(&ev); - tb_present(); - break; - - default: - break; - } - } - - tb_shutdown(); - return 0; -} diff --git a/framework/3rd/termbox/src/demo/makefile b/framework/3rd/termbox/src/demo/makefile deleted file mode 100755 index 74ec8dd..0000000 --- a/framework/3rd/termbox/src/demo/makefile +++ /dev/null @@ -1,30 +0,0 @@ -CC=gcc -FLAGS=-std=c99 -pedantic -Wall -Werror -g -static -INCL=-I../ -BIND=../../bin - -%.o:%.c - @echo "building source object $@" - @$(CC) $(INCL) $(FLAGS) -c -o $@ $< - -all:keyboard output paint truecolor - -keyboard:keyboard.o - @echo "compiling $@" - @$(CC) $(INCL) $(FLAGS) -o $@ $@.o $(BIND)/termbox.a - -output:output.o - @echo "compiling $@" - @$(CC) $(INCL) $(FLAGS) -o $@ $@.o $(BIND)/termbox.a - -paint:paint.o - @echo "compiling $@" - @$(CC) $(INCL) $(FLAGS) -o $@ $@.o $(BIND)/termbox.a - -truecolor:truecolor.o - @echo "compiling $@" - @$(CC) $(INCL) $(FLAGS) -o $@ $@.o $(BIND)/termbox.a - -clean: - @echo "cleaning workspace" - @rm -rf *.o keyboard output paint truecolor diff --git a/framework/3rd/termbox/src/demo/output.c b/framework/3rd/termbox/src/demo/output.c deleted file mode 100755 index 447975e..0000000 --- a/framework/3rd/termbox/src/demo/output.c +++ /dev/null @@ -1,156 +0,0 @@ -#include -#include -#include "../termbox.h" - -static const char chars[] = "nnnnnnnnnbbbbbbbbbuuuuuuuuuBBBBBBBBB"; - -static const uint32_t all_attrs[] = -{ - 0, - TB_BOLD, - TB_UNDERLINE, - TB_BOLD | TB_UNDERLINE, -}; - -static int next_char(int current) -{ - current++; - - if (!chars[current]) - { - current = 0; - } - - return current; -} - -static void draw_line(int x, int y, uint32_t bg) -{ - int a, c; - int current_char = 0; - - for (a = 0; a < 4; a++) - { - for (c = TB_DEFAULT; c <= TB_WHITE; c++) - { - uint32_t fg = all_attrs[a] | c; - tb_change_cell(x, y, chars[current_char], fg, bg); - current_char = next_char(current_char); - x++; - } - } -} - -static void print_combinations_table(int sx, int sy, const uint32_t* attrs, - int attrs_n) -{ - int i, c; - - for (i = 0; i < attrs_n; i++) - { - for (c = TB_DEFAULT; c <= TB_WHITE; c++) - { - uint32_t bg = attrs[i] | c; - draw_line(sx, sy, bg); - sy++; - } - } -} - -static void draw_all() -{ - tb_clear(); - - tb_select_output_mode(TB_OUTPUT_NORMAL); - static const uint32_t col1[] = {0, TB_BOLD}; - static const uint32_t col2[] = {TB_REVERSE}; - print_combinations_table(1, 1, col1, 2); - print_combinations_table(2 + strlen(chars), 1, col2, 1); - tb_present(); - - tb_select_output_mode(TB_OUTPUT_GRAYSCALE); - int c, x, y; - - for (x = 0, y = 23; x < 24; ++x) - { - tb_change_cell(x, y, '@', x, 0); - tb_change_cell(x + 25, y, ' ', 0, x); - } - - tb_present(); - - tb_select_output_mode(TB_OUTPUT_216); - y++; - - for (c = 0, x = 0; c < 216; ++c, ++x) - { - if (!(x % 24)) - { - x = 0; - ++y; - } - - tb_change_cell(x, y, '@', c, 0); - tb_change_cell(x + 25, y, ' ', 0, c); - } - - tb_present(); - - tb_select_output_mode(TB_OUTPUT_256); - y++; - - for (c = 0, x = 0; c < 256; ++c, ++x) - { - if (!(x % 24)) - { - x = 0; - ++y; - } - - tb_change_cell(x, y, '+', c | ((y & 1) ? TB_UNDERLINE : 0), 0); - tb_change_cell(x + 25, y, ' ', 0, c); - } - - tb_present(); -} - -int main(int argc, char** argv) -{ - (void)argc; - (void)argv; - int ret = tb_init(); - - if (ret) - { - fprintf(stderr, "tb_init() failed with error code %d\n", ret); - return 1; - } - - draw_all(); - - struct tb_event ev; - - while (tb_poll_event(&ev)) - { - switch (ev.type) - { - case TB_EVENT_KEY: - switch (ev.key) - { - case TB_KEY_ESC: - goto done; - break; - } - - break; - - case TB_EVENT_RESIZE: - draw_all(); - break; - } - } - -done: - tb_shutdown(); - return 0; -} diff --git a/framework/3rd/termbox/src/demo/paint.c b/framework/3rd/termbox/src/demo/paint.c deleted file mode 100755 index 1f68c2d..0000000 --- a/framework/3rd/termbox/src/demo/paint.c +++ /dev/null @@ -1,183 +0,0 @@ -#include "../termbox.h" -#include -#include -#include - -static int curCol = 0; -static int curRune = 0; -static struct tb_cell* backbuf; -static int bbw = 0, bbh = 0; - -static const uint32_t runes[] = -{ - 0x20, // ' ' - 0x2591, // '░' - 0x2592, // '▒' - 0x2593, // '▓' - 0x2588, // '█' -}; - -#define len(a) (sizeof(a)/sizeof(a[0])) - -static const uint32_t colors[] = -{ - TB_BLACK, - TB_RED, - TB_GREEN, - TB_YELLOW, - TB_BLUE, - TB_MAGENTA, - TB_CYAN, - TB_WHITE, -}; - -void updateAndDrawButtons(int* current, int x, int y, int mx, int my, int n, - void (*attrFunc)(int, uint32_t*, uint32_t*, uint32_t*)) -{ - int lx = x; - int ly = y; - - for (int i = 0; i < n; i++) - { - if (lx <= mx && mx <= lx + 3 && ly <= my && my <= ly + 1) - { - *current = i; - } - - uint32_t r; - uint32_t fg, bg; - (*attrFunc)(i, &r, &fg, &bg); - tb_change_cell(lx + 0, ly + 0, r, fg, bg); - tb_change_cell(lx + 1, ly + 0, r, fg, bg); - tb_change_cell(lx + 2, ly + 0, r, fg, bg); - tb_change_cell(lx + 3, ly + 0, r, fg, bg); - tb_change_cell(lx + 0, ly + 1, r, fg, bg); - tb_change_cell(lx + 1, ly + 1, r, fg, bg); - tb_change_cell(lx + 2, ly + 1, r, fg, bg); - tb_change_cell(lx + 3, ly + 1, r, fg, bg); - lx += 4; - } - - lx = x; - ly = y; - - for (int i = 0; i < n; i++) - { - if (*current == i) - { - uint32_t fg = TB_RED | TB_BOLD; - uint32_t bg = TB_DEFAULT; - tb_change_cell(lx + 0, ly + 2, '^', fg, bg); - tb_change_cell(lx + 1, ly + 2, '^', fg, bg); - tb_change_cell(lx + 2, ly + 2, '^', fg, bg); - tb_change_cell(lx + 3, ly + 2, '^', fg, bg); - } - - lx += 4; - } -} - -void runeAttrFunc(int i, uint32_t* r, uint32_t* fg, uint32_t* bg) -{ - *r = runes[i]; - *fg = TB_DEFAULT; - *bg = TB_DEFAULT; -} - -void colorAttrFunc(int i, uint32_t* r, uint32_t* fg, uint32_t* bg) -{ - *r = ' '; - *fg = TB_DEFAULT; - *bg = colors[i]; -} - -void updateAndRedrawAll(int mx, int my) -{ - tb_clear(); - - if (mx != -1 && my != -1) - { - backbuf[bbw * my + mx].ch = runes[curRune]; - backbuf[bbw * my + mx].fg = colors[curCol]; - } - - memcpy(tb_cell_buffer(), backbuf, sizeof(struct tb_cell)*bbw * bbh); - int h = tb_height(); - updateAndDrawButtons(&curRune, 0, 0, mx, my, len(runes), runeAttrFunc); - updateAndDrawButtons(&curCol, 0, h - 3, mx, my, len(colors), colorAttrFunc); - tb_present(); -} - -void reallocBackBuffer(int w, int h) -{ - bbw = w; - bbh = h; - - if (backbuf) - { - free(backbuf); - } - - backbuf = calloc(sizeof(struct tb_cell), w * h); -} - -int main(int argv, char** argc) -{ - (void)argc; - (void)argv; - int code = tb_init(); - - if (code < 0) - { - fprintf(stderr, "termbox init failed, code: %d\n", code); - return -1; - } - - tb_select_input_mode(TB_INPUT_ESC | TB_INPUT_MOUSE); - int w = tb_width(); - int h = tb_height(); - reallocBackBuffer(w, h); - updateAndRedrawAll(-1, -1); - - for (;;) - { - struct tb_event ev; - int mx = -1; - int my = -1; - int t = tb_poll_event(&ev); - - if (t == -1) - { - tb_shutdown(); - fprintf(stderr, "termbox poll event error\n"); - return -1; - } - - switch (t) - { - case TB_EVENT_KEY: - if (ev.key == TB_KEY_ESC) - { - tb_shutdown(); - return 0; - } - - break; - - case TB_EVENT_MOUSE: - if (ev.key == TB_KEY_MOUSE_LEFT) - { - mx = ev.x; - my = ev.y; - } - - break; - - case TB_EVENT_RESIZE: - reallocBackBuffer(ev.w, ev.h); - break; - } - - updateAndRedrawAll(mx, my); - } -} diff --git a/framework/3rd/termbox/src/demo/truecolor.c b/framework/3rd/termbox/src/demo/truecolor.c deleted file mode 100755 index 33609fd..0000000 --- a/framework/3rd/termbox/src/demo/truecolor.c +++ /dev/null @@ -1,69 +0,0 @@ -#include "termbox.h" - -int main() -{ - tb_init(); - tb_select_output_mode(TB_OUTPUT_TRUECOLOR); - int w = tb_width(); - int h = tb_height(); - uint32_t bg = 0x000000, fg = 0x000000; - tb_clear(); - int z = 0; - - for (int y = 1; y < h; y++) - { - for (int x = 1; x < w; x++) - { - uint32_t ch; - utf8_char_to_unicode(&ch, "x"); - fg = 0; - - if (z % 2 == 0) - { - fg |= TB_BOLD; - } - - if (z % 3 == 0) - { - fg |= TB_UNDERLINE; - } - - if (z % 5 == 0) - { - fg |= TB_REVERSE; - } - - tb_change_cell(x, y, ch, fg, bg); - bg += 0x000101; - z++; - } - - bg += 0x080000; - - if (bg > 0xFFFFFF) - { - bg = 0; - } - } - - tb_present(); - - while (1) - { - struct tb_event ev; - int t = tb_poll_event(&ev); - - if (t == -1) - { - break; - } - - if (t == TB_EVENT_KEY) - { - break; - } - } - - tb_shutdown(); - return 0; -} diff --git a/framework/3rd/termbox/src/input.c b/framework/3rd/termbox/src/input.c deleted file mode 100755 index 2d3f84d..0000000 --- a/framework/3rd/termbox/src/input.c +++ /dev/null @@ -1,319 +0,0 @@ -#include -#include -#include -#include -#include - -#include "term.h" - -#define BUFFER_SIZE_MAX 16 - -// if s1 starts with s2 returns 1, else 0 -static int starts_with(const char* s1, const char* s2) -{ - // nice huh? - while (*s2) - { - if (*s1++ != *s2++) - { - return 0; - } - } - - return 1; -} - -static int parse_mouse_event(struct tb_event* event, const char* buf, int len) -{ - if ((len >= 6) && starts_with(buf, "\033[M")) - { - // X10 mouse encoding, the simplest one - // \033 [ M Cb Cx Cy - int b = buf[3] - 32; - - switch (b & 3) - { - case 0: - if ((b & 64) != 0) - { - event->key = TB_KEY_MOUSE_WHEEL_UP; - } - else - { - event->key = TB_KEY_MOUSE_LEFT; - } - - break; - - case 1: - if ((b & 64) != 0) - { - event->key = TB_KEY_MOUSE_WHEEL_DOWN; - } - else - { - event->key = TB_KEY_MOUSE_MIDDLE; - } - - break; - - case 2: - event->key = TB_KEY_MOUSE_RIGHT; - break; - - case 3: - event->key = TB_KEY_MOUSE_RELEASE; - break; - - default: - return -6; - } - - event->type = TB_EVENT_MOUSE; // TB_EVENT_KEY by default - - if ((b & 32) != 0) - { - event->mod |= TB_MOD_MOTION; - } - - // the coord is 1,1 for upper left - event->x = (uint8_t)buf[4] - 1 - 32; - event->y = (uint8_t)buf[5] - 1 - 32; - return 6; - } - else if (starts_with(buf, "\033[<") || starts_with(buf, "\033[")) - { - // xterm 1006 extended mode or urxvt 1015 extended mode - // xterm: \033 [ < Cb ; Cx ; Cy (M or m) - // urxvt: \033 [ Cb ; Cx ; Cy M - int i, mi = -1, starti = -1; - int isM, isU, s1 = -1, s2 = -1; - int n1 = 0, n2 = 0, n3 = 0; - - for (i = 0; i < len; i++) - { - // We search the first (s1) and the last (s2) ';' - if (buf[i] == ';') - { - if (s1 == -1) - { - s1 = i; - } - - s2 = i; - } - - // We search for the first 'm' or 'M' - if ((buf[i] == 'm' || buf[i] == 'M') && mi == -1) - { - mi = i; - break; - } - } - - if (mi == -1) - { - return 0; - } - - // whether it's a capital M or not - isM = (buf[mi] == 'M'); - - if (buf[2] == '<') - { - isU = 0; - starti = 3; - } - else - { - isU = 1; - starti = 2; - } - - if (s1 == -1 || s2 == -1 || s1 == s2) - { - return 0; - } - - n1 = strtoul(&buf[starti], NULL, 10); - n2 = strtoul(&buf[s1 + 1], NULL, 10); - n3 = strtoul(&buf[s2 + 1], NULL, 10); - - if (isU) - { - n1 -= 32; - } - - switch (n1 & 3) - { - case 0: - if ((n1 & 64) != 0) - { - event->key = TB_KEY_MOUSE_WHEEL_UP; - } - else - { - event->key = TB_KEY_MOUSE_LEFT; - } - - break; - - case 1: - if ((n1 & 64) != 0) - { - event->key = TB_KEY_MOUSE_WHEEL_DOWN; - } - else - { - event->key = TB_KEY_MOUSE_MIDDLE; - } - - break; - - case 2: - event->key = TB_KEY_MOUSE_RIGHT; - break; - - case 3: - event->key = TB_KEY_MOUSE_RELEASE; - break; - - default: - return mi + 1; - } - - if (!isM) - { - // on xterm mouse release is signaled by lowercase m - event->key = TB_KEY_MOUSE_RELEASE; - } - - event->type = TB_EVENT_MOUSE; // TB_EVENT_KEY by default - - if ((n1 & 32) != 0) - { - event->mod |= TB_MOD_MOTION; - } - - event->x = (uint8_t)n2 - 1; - event->y = (uint8_t)n3 - 1; - return mi + 1; - } - - return 0; -} - -// convert escape sequence to event, and return consumed bytes on success (failure == 0) -static int parse_escape_seq(struct tb_event* event, const char* buf, int len) -{ - int mouse_parsed = parse_mouse_event(event, buf, len); - - if (mouse_parsed != 0) - { - return mouse_parsed; - } - - // it's pretty simple here, find 'starts_with' match and return success, else return failure - int i; - - for (i = 0; keys[i]; i++) - { - if (starts_with(buf, keys[i])) - { - event->ch = 0; - event->key = 0xFFFF - i; - return strlen(keys[i]); - } - } - - return 0; -} - -bool extract_event(struct tb_event* event, struct ringbuffer* inbuf, - int inputmode) -{ - char buf[BUFFER_SIZE_MAX + 1]; - int nbytes = ringbuffer_data_size(inbuf); - - if (nbytes > BUFFER_SIZE_MAX) - { - nbytes = BUFFER_SIZE_MAX; - } - - if (nbytes == 0) - { - return false; - } - - ringbuffer_read(inbuf, buf, nbytes); - buf[nbytes] = '\0'; - - if (buf[0] == '\033') - { - int n = parse_escape_seq(event, buf, nbytes); - - if (n != 0) - { - bool success = true; - - if (n < 0) - { - success = false; - n = -n; - } - - ringbuffer_pop(inbuf, 0, n); - return success; - } - else - { - // it's not escape sequence, then it's ALT or ESC, check inputmode - if (inputmode & TB_INPUT_ESC) - { - // if we're in escape mode, fill ESC event, pop buffer, return success - event->ch = 0; - event->key = TB_KEY_ESC; - event->mod = 0; - ringbuffer_pop(inbuf, 0, 1); - return true; - } - else if (inputmode & TB_INPUT_ALT) - { - // if we're in alt mode, set ALT modifier to event and redo parsing - event->mod = TB_MOD_ALT; - ringbuffer_pop(inbuf, 0, 1); - return extract_event(event, inbuf, inputmode); - } - - assert(!"never got here"); - } - } - - // if we're here, this is not an escape sequence and not an alt sequence - // so, it's a FUNCTIONAL KEY or a UNICODE character - - // first of all check if it's a functional key*/ - if ((unsigned char)buf[0] <= TB_KEY_SPACE || - (unsigned char)buf[0] == TB_KEY_BACKSPACE2) - { - // fill event, pop buffer, return success - event->ch = 0; - event->key = (uint16_t)buf[0]; - ringbuffer_pop(inbuf, 0, 1); - return true; - } - - // feh... we got utf8 here - - // check if there is all bytes - if (nbytes >= utf8_char_length(buf[0])) - { - // everything ok, fill event, pop buffer, return success - utf8_char_to_unicode(&event->ch, buf); - event->key = 0; - ringbuffer_pop(inbuf, 0, utf8_char_length(buf[0])); - return true; - } - - return false; -} diff --git a/framework/3rd/termbox/src/memstream.c b/framework/3rd/termbox/src/memstream.c deleted file mode 100755 index d218b54..0000000 --- a/framework/3rd/termbox/src/memstream.c +++ /dev/null @@ -1,36 +0,0 @@ -#include -#include -#include -#include "memstream.h" - -void memstream_init(struct memstream* s, int fd, void* buffer, size_t len) -{ - s->file = fd; - s->data = buffer; - s->pos = 0; - s->capa = len; -} - -void memstream_flush(struct memstream* s) -{ - write(s->file, s->data, s->pos); - s->pos = 0; -} - -void memstream_write(struct memstream* s, void* source, size_t len) -{ - unsigned char* data = source; - - if (s->pos + len > s->capa) - { - memstream_flush(s); - } - - memcpy(s->data + s->pos, data, len); - s->pos += len; -} - -void memstream_puts(struct memstream* s, const char* str) -{ - memstream_write(s, (void*) str, strlen(str)); -} diff --git a/framework/3rd/termbox/src/memstream.h b/framework/3rd/termbox/src/memstream.h deleted file mode 100755 index c5d864a..0000000 --- a/framework/3rd/termbox/src/memstream.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef H_MEMSTREAM -#define H_MEMSTREAM - -#include -#include - -struct memstream -{ - size_t pos; - size_t capa; - int file; - unsigned char* data; -}; - -void memstream_init(struct memstream* s, int fd, void* buffer, size_t len); -void memstream_flush(struct memstream* s); -void memstream_write(struct memstream* s, void* source, size_t len); -void memstream_puts(struct memstream* s, const char* str); - -#endif diff --git a/framework/3rd/termbox/src/ringbuffer.c b/framework/3rd/termbox/src/ringbuffer.c deleted file mode 100755 index a8de0f6..0000000 --- a/framework/3rd/termbox/src/ringbuffer.c +++ /dev/null @@ -1,195 +0,0 @@ -#include "ringbuffer.h" -#include -#include -#include -#include // for ptrdiff_t - -int init_ringbuffer(struct ringbuffer* r, size_t size) -{ - r->buf = (char*)malloc(size); - - if (!r->buf) - { - return ERINGBUFFER_ALLOC_FAIL; - } - - r->size = size; - clear_ringbuffer(r); - - return 0; -} - -void free_ringbuffer(struct ringbuffer* r) -{ - free(r->buf); -} - -void clear_ringbuffer(struct ringbuffer* r) -{ - r->begin = 0; - r->end = 0; -} - -size_t ringbuffer_free_space(struct ringbuffer* r) -{ - if (r->begin == 0 && r->end == 0) - { - return r->size; - } - - if (r->begin < r->end) - { - return r->size - (r->end - r->begin) - 1; - } - else - { - return r->begin - r->end - 1; - } -} - -size_t ringbuffer_data_size(struct ringbuffer* r) -{ - if (r->begin == 0 && r->end == 0) - { - return 0; - } - - if (r->begin <= r->end) - { - return r->end - r->begin + 1; - } - else - { - return r->size - (r->begin - r->end) + 1; - } -} - - -void ringbuffer_push(struct ringbuffer* r, const void* data, size_t size) -{ - if (ringbuffer_free_space(r) < size) - { - return; - } - - if (r->begin == 0 && r->end == 0) - { - memcpy(r->buf, data, size); - r->begin = r->buf; - r->end = r->buf + size - 1; - return; - } - - r->end++; - - if (r->begin < r->end) - { - if ((size_t)(r->buf + (ptrdiff_t)r->size - r->begin) >= size) - { - // we can fit without cut - memcpy(r->end, data, size); - r->end += size - 1; - } - else - { - // make a cut - size_t s = r->buf + r->size - r->end; - memcpy(r->end, data, s); - size -= s; - memcpy(r->buf, (char*)data + s, size); - r->end = r->buf + size - 1; - } - } - else - { - memcpy(r->end, data, size); - r->end += size - 1; - } -} - -void ringbuffer_pop(struct ringbuffer* r, void* data, size_t size) -{ - if (ringbuffer_data_size(r) < size) - { - return; - } - - int need_clear = 0; - - if (ringbuffer_data_size(r) == size) - { - need_clear = 1; - } - - if (r->begin < r->end) - { - if (data) - { - memcpy(data, r->begin, size); - } - - r->begin += size; - } - else - { - if ((size_t)(r->buf + (ptrdiff_t)r->size - r->begin) >= size) - { - if (data) - { - memcpy(data, r->begin, size); - } - - r->begin += size; - } - else - { - size_t s = r->buf + r->size - r->begin; - - if (data) - { - memcpy(data, r->begin, s); - } - - size -= s; - - if (data) - { - memcpy((char*)data + s, r->buf, size); - } - - r->begin = r->buf + size; - } - } - - if (need_clear) - { - clear_ringbuffer(r); - } -} - -void ringbuffer_read(struct ringbuffer* r, void* data, size_t size) -{ - if (ringbuffer_data_size(r) < size) - { - return; - } - - if (r->begin < r->end) - { - memcpy(data, r->begin, size); - } - else - { - if ((size_t)(r->buf + (ptrdiff_t)r->size - r->begin) >= size) - { - memcpy(data, r->begin, size); - } - else - { - size_t s = r->buf + r->size - r->begin; - memcpy(data, r->begin, s); - size -= s; - memcpy((char*)data + s, r->buf, size); - } - } -} diff --git a/framework/3rd/termbox/src/ringbuffer.h b/framework/3rd/termbox/src/ringbuffer.h deleted file mode 100755 index 9a8b0d6..0000000 --- a/framework/3rd/termbox/src/ringbuffer.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef H_RINGBUFFER -#define H_RINGBUFFER - -#include - -#define ERINGBUFFER_ALLOC_FAIL -1 - -struct ringbuffer -{ - char* buf; - size_t size; - - char* begin; - char* end; -}; - -int init_ringbuffer(struct ringbuffer* r, size_t size); -void free_ringbuffer(struct ringbuffer* r); -void clear_ringbuffer(struct ringbuffer* r); -size_t ringbuffer_free_space(struct ringbuffer* r); -size_t ringbuffer_data_size(struct ringbuffer* r); -void ringbuffer_push(struct ringbuffer* r, const void* data, size_t size); -void ringbuffer_pop(struct ringbuffer* r, void* data, size_t size); -void ringbuffer_read(struct ringbuffer* r, void* data, size_t size); - -#endif diff --git a/framework/3rd/termbox/src/term.c b/framework/3rd/termbox/src/term.c deleted file mode 100755 index c94f394..0000000 --- a/framework/3rd/termbox/src/term.c +++ /dev/null @@ -1,412 +0,0 @@ -#include -#include -#include -#include -#include - -#include "term.h" -#define ENTER_MOUSE_SEQ "\x1b[?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h" -#define EXIT_MOUSE_SEQ "\x1b[?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l" - -#define EUNSUPPORTED_TERM -1 - -// rxvt-256color -static const char* rxvt_256color_keys[] = -{ - "\033[11~", "\033[12~", "\033[13~", "\033[14~", "\033[15~", "\033[17~", - "\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~", - "\033[2~", "\033[3~", "\033[7~", "\033[8~", "\033[5~", "\033[6~", - "\033[A", "\033[B", "\033[D", "\033[C", NULL -}; -static const char* rxvt_256color_funcs[] = -{ - "\0337\033[?47h", "\033[2J\033[?47l\0338", "\033[?25h", "\033[?25l", - "\033[H\033[2J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", - "\033=", "\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ, -}; - -// Eterm -static const char* eterm_keys[] = -{ - "\033[11~", "\033[12~", "\033[13~", "\033[14~", "\033[15~", "\033[17~", - "\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~", - "\033[2~", "\033[3~", "\033[7~", "\033[8~", "\033[5~", "\033[6~", - "\033[A", "\033[B", "\033[D", "\033[C", NULL -}; -static const char* eterm_funcs[] = -{ - "\0337\033[?47h", "\033[2J\033[?47l\0338", "\033[?25h", "\033[?25l", - "\033[H\033[2J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", - "", "", "", "", -}; - -// screen -static const char* screen_keys[] = -{ - "\033OP", "\033OQ", "\033OR", "\033OS", "\033[15~", "\033[17~", - "\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~", - "\033[2~", "\033[3~", "\033[1~", "\033[4~", "\033[5~", "\033[6~", - "\033OA", "\033OB", "\033OD", "\033OC", NULL -}; -static const char* screen_funcs[] = -{ - "\033[?1049h", "\033[?1049l", "\033[34h\033[?25h", "\033[?25l", - "\033[H\033[J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", - "\033[?1h\033=", "\033[?1l\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ, -}; - -// rxvt-unicode -static const char* rxvt_unicode_keys[] = -{ - "\033[11~", "\033[12~", "\033[13~", "\033[14~", "\033[15~", "\033[17~", - "\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~", - "\033[2~", "\033[3~", "\033[7~", "\033[8~", "\033[5~", "\033[6~", - "\033[A", "\033[B", "\033[D", "\033[C", NULL -}; -static const char* rxvt_unicode_funcs[] = -{ - "\033[?1049h", "\033[r\033[?1049l", "\033[?25h", "\033[?25l", - "\033[H\033[2J", "\033[m\033(B", "\033[4m", "\033[1m", "\033[5m", - "\033[7m", "\033=", "\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ, -}; - -// linux -static const char* linux_keys[] = -{ - "\033[[A", "\033[[B", "\033[[C", "\033[[D", "\033[[E", "\033[17~", - "\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~", - "\033[2~", "\033[3~", "\033[1~", "\033[4~", "\033[5~", "\033[6~", - "\033[A", "\033[B", "\033[D", "\033[C", NULL -}; -static const char* linux_funcs[] = -{ - "", "", "\033[?25h\033[?0c", "\033[?25l\033[?1c", "\033[H\033[J", - "\033[0;10m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "", "", "", "", -}; - -// xterm -static const char* xterm_keys[] = -{ - "\033OP", "\033OQ", "\033OR", "\033OS", "\033[15~", "\033[17~", "\033[18~", - "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~", "\033[2~", - "\033[3~", "\033OH", "\033OF", "\033[5~", "\033[6~", "\033OA", "\033OB", - "\033OD", "\033OC", NULL -}; -static const char* xterm_funcs[] = -{ - "\033[?1049h", "\033[?1049l", "\033[?12l\033[?25h", "\033[?25l", - "\033[H\033[2J", "\033(B\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", - "\033[?1h\033=", "\033[?1l\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ, -}; - -struct term -{ - const char* name; - const char** keys; - const char** funcs; -}; - -static struct term terms[] = -{ - {"rxvt-256color", rxvt_256color_keys, rxvt_256color_funcs}, - {"Eterm", eterm_keys, eterm_funcs}, - {"screen", screen_keys, screen_funcs}, - {"rxvt-unicode", rxvt_unicode_keys, rxvt_unicode_funcs}, - {"linux", linux_keys, linux_funcs}, - {"xterm", xterm_keys, xterm_funcs}, - {0, 0, 0}, -}; - -static int init_from_terminfo = 0; -const char** keys; -const char** funcs; - -static int try_compatible(const char* term, const char* name, - const char** tkeys, const char** tfuncs) -{ - if (strstr(term, name)) - { - keys = tkeys; - funcs = tfuncs; - return 0; - } - - return EUNSUPPORTED_TERM; -} - -static int init_term_builtin(void) -{ - int i; - const char* term = getenv("TERM"); - - if (term) - { - for (i = 0; terms[i].name; i++) - { - if (!strcmp(terms[i].name, term)) - { - keys = terms[i].keys; - funcs = terms[i].funcs; - return 0; - } - } - - // let's do some heuristic, maybe it's a compatible terminal - if (try_compatible(term, "xterm", xterm_keys, xterm_funcs) == 0) - { - return 0; - } - - if (try_compatible(term, "rxvt", rxvt_unicode_keys, rxvt_unicode_funcs) == 0) - { - return 0; - } - - if (try_compatible(term, "linux", linux_keys, linux_funcs) == 0) - { - return 0; - } - - if (try_compatible(term, "Eterm", eterm_keys, eterm_funcs) == 0) - { - return 0; - } - - if (try_compatible(term, "screen", screen_keys, screen_funcs) == 0) - { - return 0; - } - - // let's assume that 'cygwin' is xterm compatible - if (try_compatible(term, "cygwin", xterm_keys, xterm_funcs) == 0) - { - return 0; - } - } - - return EUNSUPPORTED_TERM; -} - -// terminfo -static char* read_file(const char* file) -{ - FILE* f = fopen(file, "rb"); - - if (!f) - { - return 0; - } - - struct stat st; - - if (fstat(fileno(f), &st) != 0) - { - fclose(f); - return 0; - } - - char* data = malloc(st.st_size); - - if (!data) - { - fclose(f); - return 0; - } - - if (fread(data, 1, st.st_size, f) != (size_t)st.st_size) - { - fclose(f); - free(data); - return 0; - } - - fclose(f); - return data; -} - -static char* terminfo_try_path(const char* path, const char* term) -{ - char tmp[4096]; - snprintf(tmp, sizeof(tmp), "%s/%c/%s", path, term[0], term); - tmp[sizeof(tmp) - 1] = '\0'; - char* data = read_file(tmp); - - if (data) - { - return data; - } - - // fallback to darwin specific dirs structure - snprintf(tmp, sizeof(tmp), "%s/%x/%s", path, term[0], term); - tmp[sizeof(tmp) - 1] = '\0'; - return read_file(tmp); -} - -static char* load_terminfo(void) -{ - char tmp[4096]; - const char* term = getenv("TERM"); - - if (!term) - { - return 0; - } - - // if TERMINFO is set, no other directory should be searched - const char* terminfo = getenv("TERMINFO"); - - if (terminfo) - { - return terminfo_try_path(terminfo, term); - } - - // next, consider ~/.terminfo - const char* home = getenv("HOME"); - - if (home) - { - snprintf(tmp, sizeof(tmp), "%s/.terminfo", home); - tmp[sizeof(tmp) - 1] = '\0'; - char* data = terminfo_try_path(tmp, term); - - if (data) - { - return data; - } - } - - // next, TERMINFO_DIRS - const char* dirs = getenv("TERMINFO_DIRS"); - - if (dirs) - { - snprintf(tmp, sizeof(tmp), "%s", dirs); - tmp[sizeof(tmp) - 1] = '\0'; - char* dir = strtok(tmp, ":"); - - while (dir) - { - const char* cdir = dir; - - if (strcmp(cdir, "") == 0) - { - cdir = "/usr/share/terminfo"; - } - - char* data = terminfo_try_path(cdir, term); - - if (data) - { - return data; - } - - dir = strtok(0, ":"); - } - } - - // fallback to /usr/share/terminfo - return terminfo_try_path("/usr/share/terminfo", term); -} - -#define TI_MAGIC 0432 -#define TI_ALT_MAGIC 542 -#define TI_HEADER_LENGTH 12 -#define TB_KEYS_NUM 22 - -static const char* terminfo_copy_string(char* data, int str, int table) -{ - const int16_t off = *(int16_t*)(data + str); - const char* src = data + table + off; - int len = strlen(src); - char* dst = malloc(len + 1); - strcpy(dst, src); - return dst; -} - -const int16_t ti_funcs[] = -{ - 28, 40, 16, 13, 5, 39, 36, 27, 26, 34, 89, 88, -}; - -const int16_t ti_keys[] = -{ - // apparently not a typo; 67 is F10 for whatever reason - 66, 68, 69, 70, 71, 72, 73, 74, 75, 67, 216, 217, 77, 59, 76, 164, 82, - 81, 87, 61, 79, 83, -}; - -int init_term(void) -{ - int i; - char* data = load_terminfo(); - - if (!data) - { - init_from_terminfo = 0; - return init_term_builtin(); - } - - int16_t* header = (int16_t*)data; - - const int number_sec_len = header[0] == TI_ALT_MAGIC ? 4 : 2; - - if ((header[1] + header[2]) % 2) - { - // old quirk to align everything on word boundaries - header[2] += 1; - } - - const int str_offset = TI_HEADER_LENGTH + - header[1] + header[2] + number_sec_len * header[3]; - const int table_offset = str_offset + 2 * header[4]; - - keys = malloc(sizeof(const char*) * (TB_KEYS_NUM + 1)); - - for (i = 0; i < TB_KEYS_NUM; i++) - { - keys[i] = terminfo_copy_string(data, - str_offset + 2 * ti_keys[i], table_offset); - } - - keys[i] = NULL; - - funcs = malloc(sizeof(const char*) * T_FUNCS_NUM); - - // the last two entries are reserved for mouse. because the table offset is - // not there, the two entries have to fill in manually - for (i = 0; i < T_FUNCS_NUM - 2; i++) - { - funcs[i] = terminfo_copy_string(data, - str_offset + 2 * ti_funcs[i], table_offset); - } - - funcs[T_FUNCS_NUM - 2] = ENTER_MOUSE_SEQ; - funcs[T_FUNCS_NUM - 1] = EXIT_MOUSE_SEQ; - init_from_terminfo = 1; - free(data); - return 0; -} - -void shutdown_term(void) -{ - if (init_from_terminfo) - { - int i; - - for (i = 0; i < TB_KEYS_NUM; i++) - { - free((void*)keys[i]); - } - - // the last two entries are reserved for mouse. because the table offset - // is not there, the two entries have to fill in manually and do not - // need to be freed. - for (i = 0; i < T_FUNCS_NUM - 2; i++) - { - free((void*)funcs[i]); - } - - free(keys); - free(funcs); - } -} diff --git a/framework/3rd/termbox/src/term.h b/framework/3rd/termbox/src/term.h deleted file mode 100755 index 8f4d93d..0000000 --- a/framework/3rd/termbox/src/term.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef H_TERM -#define H_TERM - -#include "termbox.h" -#include "ringbuffer.h" -#include - -#define EUNSUPPORTED_TERM -1 - -enum -{ - T_ENTER_CA, - T_EXIT_CA, - T_SHOW_CURSOR, - T_HIDE_CURSOR, - T_CLEAR_SCREEN, - T_SGR0, - T_UNDERLINE, - T_BOLD, - T_BLINK, - T_REVERSE, - T_ENTER_KEYPAD, - T_EXIT_KEYPAD, - T_ENTER_MOUSE, - T_EXIT_MOUSE, - T_FUNCS_NUM, -}; - -extern const char** keys; -extern const char** funcs; - -// true on success, false on failure -bool extract_event(struct tb_event* event, struct ringbuffer* inbuf, - int inputmode); -int init_term(void); -void shutdown_term(void); - -#endif diff --git a/framework/3rd/termbox/src/termbox.c b/framework/3rd/termbox/src/termbox.c deleted file mode 100755 index 72a4335..0000000 --- a/framework/3rd/termbox/src/termbox.c +++ /dev/null @@ -1,885 +0,0 @@ -#include "term.h" -#include "termbox.h" -#include "memstream.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct cellbuf -{ - int width; - int height; - struct tb_cell* cells; -}; - -#define CELL(buf, x, y) (buf)->cells[(y) * (buf)->width + (x)] -#define IS_CURSOR_HIDDEN(cx, cy) (cx == -1 || cy == -1) -#define LAST_COORD_INIT -1 - -static struct termios orig_tios; - -static struct cellbuf back_buffer; -static struct cellbuf front_buffer; -static unsigned char write_buffer_data[32 * 1024]; -static struct memstream write_buffer; - -static int termw = -1; -static int termh = -1; - -static int inputmode = TB_INPUT_ESC; -static int outputmode = TB_OUTPUT_NORMAL; - -static struct ringbuffer inbuf; - -static int out; -static FILE* in; - -static int out_fileno; -static int in_fileno; - -static int winch_fds[2]; - -static int lastx = LAST_COORD_INIT; -static int lasty = LAST_COORD_INIT; -static int cursor_x = -1; -static int cursor_y = -1; - -static uint32_t background = TB_DEFAULT; -static uint32_t foreground = TB_DEFAULT; - -static void write_cursor(int x, int y); -static void write_sgr(uint32_t fg, uint32_t bg); - -static void cellbuf_init(struct cellbuf* buf, int width, int height); -static void cellbuf_resize(struct cellbuf* buf, int width, int height); -static void cellbuf_clear(struct cellbuf* buf); -static void cellbuf_free(struct cellbuf* buf); - -static void update_size(void); -static void update_term_size(void); -static void send_attr(uint32_t fg, uint32_t bg); -static void send_char(int x, int y, uint32_t c); -static void send_clear(void); -static void sigwinch_handler(int xxx); -static int wait_fill_event(struct tb_event* event, struct timeval* timeout); - -// may happen in a different thread -static volatile int buffer_size_change_request; - -int tb_init_file(const char* name) -{ - out = open(name, O_WRONLY); - in = fopen(name, "r"); - - if (out == -1 || !in) - { - if (out != -1) - { - close(out); - } - - if (in) - { - fclose(in); - } - - return TB_EFAILED_TO_OPEN_TTY; - } - - out_fileno = out; - in_fileno = fileno(in); - - if (init_term() < 0) - { - close(out); - fclose(in); - - return TB_EUNSUPPORTED_TERMINAL; - } - - if (pipe(winch_fds) < 0) - { - close(out); - fclose(in); - - return TB_EPIPE_TRAP_ERROR; - } - - struct sigaction sa; - - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = sigwinch_handler; - sa.sa_flags = 0; - sigaction(SIGWINCH, &sa, 0); - tcgetattr(out_fileno, &orig_tios); - - struct termios tios; - - memcpy(&tios, &orig_tios, sizeof(tios)); - tios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP - | INLCR | IGNCR | ICRNL | IXON); - tios.c_oflag &= ~OPOST; - tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); - tios.c_cflag &= ~(CSIZE | PARENB); - tios.c_cflag |= CS8; - tios.c_cc[VMIN] = 0; - tios.c_cc[VTIME] = 0; - tcsetattr(out_fileno, TCSAFLUSH, &tios); - - memstream_init(&write_buffer, out_fileno, write_buffer_data, - sizeof(write_buffer_data)); - memstream_puts(&write_buffer, funcs[T_ENTER_CA]); - memstream_puts(&write_buffer, funcs[T_ENTER_KEYPAD]); - memstream_puts(&write_buffer, funcs[T_HIDE_CURSOR]); - send_clear(); - - update_term_size(); - cellbuf_init(&back_buffer, termw, termh); - cellbuf_init(&front_buffer, termw, termh); - cellbuf_clear(&back_buffer); - cellbuf_clear(&front_buffer); - init_ringbuffer(&inbuf, 4096); - return 0; -} - -int tb_init(void) -{ - return tb_init_file("/dev/tty"); -} - -void tb_shutdown(void) -{ - if (termw == -1) - { - fputs("tb_shutdown() should not be called twice.", stderr); - abort(); - } - - memstream_puts(&write_buffer, funcs[T_SHOW_CURSOR]); - memstream_puts(&write_buffer, funcs[T_SGR0]); - memstream_puts(&write_buffer, funcs[T_CLEAR_SCREEN]); - memstream_puts(&write_buffer, funcs[T_EXIT_CA]); - memstream_puts(&write_buffer, funcs[T_EXIT_KEYPAD]); - memstream_puts(&write_buffer, funcs[T_EXIT_MOUSE]); - memstream_flush(&write_buffer); - tcsetattr(out_fileno, TCSAFLUSH, &orig_tios); - - shutdown_term(); - close(out); - fclose(in); - close(winch_fds[0]); - close(winch_fds[1]); - - cellbuf_free(&back_buffer); - cellbuf_free(&front_buffer); - free_ringbuffer(&inbuf); - termw = termh = -1; -} - -void tb_present(void) -{ - int x, y, w, i; - struct tb_cell* back, *front; - - // invalidate cursor position - lastx = LAST_COORD_INIT; - lasty = LAST_COORD_INIT; - - if (buffer_size_change_request) - { - update_size(); - buffer_size_change_request = 0; - } - - for (y = 0; y < front_buffer.height; ++y) - { - for (x = 0; x < front_buffer.width;) - { - back = &CELL(&back_buffer, x, y); - front = &CELL(&front_buffer, x, y); - w = wcwidth(back->ch); - - if (w < 1) - { - w = 1; - } - - if (memcmp(back, front, sizeof(struct tb_cell)) == 0) - { - x += w; - continue; - } - - memcpy(front, back, sizeof(struct tb_cell)); - send_attr(back->fg, back->bg); - - if (w > 1 && x >= front_buffer.width - (w - 1)) - { - // Not enough room for wide ch, so send spaces - for (i = x; i < front_buffer.width; ++i) - { - send_char(i, y, ' '); - } - } - else - { - send_char(x, y, back->ch); - - for (i = 1; i < w; ++i) - { - front = &CELL(&front_buffer, x + i, y); - front->ch = 0; - front->fg = back->fg; - front->bg = back->bg; - } - } - - x += w; - } - } - - if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y)) - { - write_cursor(cursor_x, cursor_y); - } - - memstream_flush(&write_buffer); -} - -void tb_set_cursor(int cx, int cy) -{ - if (IS_CURSOR_HIDDEN(cursor_x, cursor_y) && !IS_CURSOR_HIDDEN(cx, cy)) - { - memstream_puts(&write_buffer, funcs[T_SHOW_CURSOR]); - } - - if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y) && IS_CURSOR_HIDDEN(cx, cy)) - { - memstream_puts(&write_buffer, funcs[T_HIDE_CURSOR]); - } - - cursor_x = cx; - cursor_y = cy; - - if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y)) - { - write_cursor(cursor_x, cursor_y); - } -} - -void tb_put_cell(int x, int y, const struct tb_cell* cell) -{ - if ((unsigned)x >= (unsigned)back_buffer.width) - { - return; - } - - if ((unsigned)y >= (unsigned)back_buffer.height) - { - return; - } - - CELL(&back_buffer, x, y) = *cell; -} - -void tb_change_cell(int x, int y, uint32_t ch, uint32_t fg, uint32_t bg) -{ - struct tb_cell c = {ch, fg, bg}; - tb_put_cell(x, y, &c); -} - -void tb_blit(int x, int y, int w, int h, const struct tb_cell* cells) -{ - if (x + w < 0 || x >= back_buffer.width) - { - return; - } - - if (y + h < 0 || y >= back_buffer.height) - { - return; - } - - int xo = 0, yo = 0, ww = w, hh = h; - - if (x < 0) - { - xo = -x; - ww -= xo; - x = 0; - } - - if (y < 0) - { - yo = -y; - hh -= yo; - y = 0; - } - - if (ww > back_buffer.width - x) - { - ww = back_buffer.width - x; - } - - if (hh > back_buffer.height - y) - { - hh = back_buffer.height - y; - } - - int sy; - struct tb_cell* dst = &CELL(&back_buffer, x, y); - const struct tb_cell* src = cells + yo * w + xo; - size_t size = sizeof(struct tb_cell) * ww; - - for (sy = 0; sy < hh; ++sy) - { - memcpy(dst, src, size); - dst += back_buffer.width; - src += w; - } -} - -struct tb_cell* tb_cell_buffer(void) -{ - return back_buffer.cells; -} - -int tb_poll_event(struct tb_event* event) -{ - return wait_fill_event(event, 0); -} - -int tb_peek_event(struct tb_event* event, int timeout) -{ - struct timeval tv; - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000; - return wait_fill_event(event, &tv); -} - -int tb_width(void) -{ - return termw; -} - -int tb_height(void) -{ - return termh; -} - -void tb_clear(void) -{ - if (buffer_size_change_request) - { - update_size(); - buffer_size_change_request = 0; - } - - cellbuf_clear(&back_buffer); -} - -int tb_select_input_mode(int mode) -{ - if (mode) - { - if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == 0) - { - mode |= TB_INPUT_ESC; - } - - // technically termbox can handle that, but let's be nice - // and show here what mode is actually used - if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == (TB_INPUT_ESC | TB_INPUT_ALT)) - { - mode &= ~TB_INPUT_ALT; - } - - inputmode = mode; - - if (mode & TB_INPUT_MOUSE) - { - memstream_puts(&write_buffer, funcs[T_ENTER_MOUSE]); - memstream_flush(&write_buffer); - } - else - { - memstream_puts(&write_buffer, funcs[T_EXIT_MOUSE]); - memstream_flush(&write_buffer); - } - } - - return inputmode; -} - -int tb_select_output_mode(int mode) -{ - if (mode) - { - outputmode = mode; - } - - return outputmode; -} - -void tb_set_clear_attributes(uint32_t fg, uint32_t bg) -{ - foreground = fg; - background = bg; -} - -static unsigned convertnum(uint32_t num, char* buf) -{ - unsigned i, l = 0; - int ch; - - do - { - buf[l++] = '0' + (num % 10); - num /= 10; - } - while (num); - - for (i = 0; i < l / 2; i++) - { - ch = buf[i]; - buf[i] = buf[l - 1 - i]; - buf[l - 1 - i] = ch; - } - - return l; -} - -#define WRITE_LITERAL(X) memstream_write(&write_buffer, (X), sizeof(X) -1) -#define WRITE_INT(X) memstream_write(&write_buffer, buf, convertnum((X), buf)) - -static void write_cursor(int x, int y) -{ - char buf[32]; - WRITE_LITERAL("\033["); - WRITE_INT(y + 1); - WRITE_LITERAL(";"); - WRITE_INT(x + 1); - WRITE_LITERAL("H"); -} - -static void write_sgr(uint32_t fg, uint32_t bg) -{ - char buf[32]; - - if (outputmode != TB_OUTPUT_TRUECOLOR && fg == TB_DEFAULT && bg == TB_DEFAULT) - { - return; - } - - switch (outputmode) - { - case TB_OUTPUT_TRUECOLOR: - WRITE_LITERAL("\033[38;2;"); - WRITE_INT(fg >> 16 & 0xFF); - WRITE_LITERAL(";"); - WRITE_INT(fg >> 8 & 0xFF); - WRITE_LITERAL(";"); - WRITE_INT(fg & 0xFF); - WRITE_LITERAL(";48;2;"); - WRITE_INT(bg >> 16 & 0xFF); - WRITE_LITERAL(";"); - WRITE_INT(bg >> 8 & 0xFF); - WRITE_LITERAL(";"); - WRITE_INT(bg & 0xFF); - WRITE_LITERAL("m"); - break; - - case TB_OUTPUT_256: - case TB_OUTPUT_216: - case TB_OUTPUT_GRAYSCALE: - WRITE_LITERAL("\033["); - - if (fg != TB_DEFAULT) - { - WRITE_LITERAL("38;5;"); - WRITE_INT(fg); - - if (bg != TB_DEFAULT) - { - WRITE_LITERAL(";"); - } - } - - if (bg != TB_DEFAULT) - { - WRITE_LITERAL("48;5;"); - WRITE_INT(bg); - } - - WRITE_LITERAL("m"); - break; - - case TB_OUTPUT_NORMAL: - default: - WRITE_LITERAL("\033["); - - if (fg != TB_DEFAULT) - { - WRITE_LITERAL("3"); - WRITE_INT(fg - 1); - - if (bg != TB_DEFAULT) - { - WRITE_LITERAL(";"); - } - } - - if (bg != TB_DEFAULT) - { - WRITE_LITERAL("4"); - WRITE_INT(bg - 1); - } - - WRITE_LITERAL("m"); - break; - } -} - -static void cellbuf_init(struct cellbuf* buf, int width, int height) -{ - buf->cells = (struct tb_cell*)malloc(sizeof(struct tb_cell) * width * height); - assert(buf->cells); - buf->width = width; - buf->height = height; -} - -static void cellbuf_resize(struct cellbuf* buf, int width, int height) -{ - if (buf->width == width && buf->height == height) - { - return; - } - - int oldw = buf->width; - int oldh = buf->height; - struct tb_cell* oldcells = buf->cells; - - cellbuf_init(buf, width, height); - cellbuf_clear(buf); - - int minw = (width < oldw) ? width : oldw; - int minh = (height < oldh) ? height : oldh; - int i; - - for (i = 0; i < minh; ++i) - { - struct tb_cell* csrc = oldcells + (i * oldw); - struct tb_cell* cdst = buf->cells + (i * width); - memcpy(cdst, csrc, sizeof(struct tb_cell) * minw); - } - - free(oldcells); -} - -static void cellbuf_clear(struct cellbuf* buf) -{ - int i; - int ncells = buf->width * buf->height; - - for (i = 0; i < ncells; ++i) - { - buf->cells[i].ch = ' '; - buf->cells[i].fg = foreground; - buf->cells[i].bg = background; - } -} - -static void cellbuf_free(struct cellbuf* buf) -{ - free(buf->cells); -} - -static void get_term_size(int* w, int* h) -{ - struct winsize sz; - memset(&sz, 0, sizeof(sz)); - - ioctl(out_fileno, TIOCGWINSZ, &sz); - - if (w) - { - *w = sz.ws_col; - } - - if (h) - { - *h = sz.ws_row; - } -} - -static void update_term_size(void) -{ - struct winsize sz; - memset(&sz, 0, sizeof(sz)); - - ioctl(out_fileno, TIOCGWINSZ, &sz); - - termw = sz.ws_col; - termh = sz.ws_row; -} - -static void send_attr(uint32_t fg, uint32_t bg) -{ -#define LAST_ATTR_INIT 0xFFFFFFFF - static uint32_t lastfg = LAST_ATTR_INIT, lastbg = LAST_ATTR_INIT; - - if (fg != lastfg || bg != lastbg) - { - memstream_puts(&write_buffer, funcs[T_SGR0]); - uint32_t fgcol; - uint32_t bgcol; - - switch (outputmode) - { - case TB_OUTPUT_TRUECOLOR: - fgcol = fg; - bgcol = bg; - break; - - case TB_OUTPUT_256: - fgcol = fg & 0xFF; - bgcol = bg & 0xFF; - break; - - case TB_OUTPUT_216: - fgcol = fg & 0xFF; - - if (fgcol > 215) - { - fgcol = 7; - } - - bgcol = bg & 0xFF; - - if (bgcol > 215) - { - bgcol = 0; - } - - fgcol += 0x10; - bgcol += 0x10; - break; - - case TB_OUTPUT_GRAYSCALE: - fgcol = fg & 0xFF; - - if (fgcol > 23) - { - fgcol = 23; - } - - bgcol = bg & 0xFF; - - if (bgcol > 23) - { - bgcol = 0; - } - - fgcol += 0xe8; - bgcol += 0xe8; - break; - - case TB_OUTPUT_NORMAL: - default: - fgcol = fg & 0x0F; - bgcol = bg & 0x0F; - } - - if (fg & TB_BOLD) - { - memstream_puts(&write_buffer, funcs[T_BOLD]); - } - - if (bg & TB_BOLD) - { - memstream_puts(&write_buffer, funcs[T_BLINK]); - } - - if (fg & TB_UNDERLINE) - { - memstream_puts(&write_buffer, funcs[T_UNDERLINE]); - } - - if ((fg & TB_REVERSE) || (bg & TB_REVERSE)) - { - memstream_puts(&write_buffer, funcs[T_REVERSE]); - } - - write_sgr(fgcol, bgcol); - - lastfg = fg; - lastbg = bg; - } -} - -static void send_char(int x, int y, uint32_t c) -{ - char buf[7]; - int bw = utf8_unicode_to_char(buf, c); - buf[bw] = '\0'; - - if (x - 1 != lastx || y != lasty) - { - write_cursor(x, y); - } - - lastx = x; - lasty = y; - - if (!c) - { - buf[0] = ' '; // replace 0 with whitespace - } - - memstream_puts(&write_buffer, buf); -} - -static void send_clear(void) -{ - send_attr(foreground, background); - memstream_puts(&write_buffer, funcs[T_CLEAR_SCREEN]); - - if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y)) - { - write_cursor(cursor_x, cursor_y); - } - - memstream_flush(&write_buffer); - - // we need to invalidate cursor position too and these two vars are - // used only for simple cursor positioning optimization, cursor - // actually may be in the correct place, but we simply discard - // optimization once and it gives us simple solution for the case when - // cursor moved - lastx = LAST_COORD_INIT; - lasty = LAST_COORD_INIT; -} - -static void sigwinch_handler(int xxx) -{ - (void) xxx; - const int zzz = 1; - write(winch_fds[1], &zzz, sizeof(int)); -} - -static void update_size(void) -{ - update_term_size(); - cellbuf_resize(&back_buffer, termw, termh); - cellbuf_resize(&front_buffer, termw, termh); - cellbuf_clear(&front_buffer); - send_clear(); -} - -static int wait_fill_event(struct tb_event* event, struct timeval* timeout) -{ -#define ENOUGH_DATA_FOR_INPUT_PARSING 128 - int result; - char buf[ENOUGH_DATA_FOR_INPUT_PARSING]; - fd_set events; - memset(event, 0, sizeof(struct tb_event)); - - // try to extract event from input buffer, return on success - event->type = TB_EVENT_KEY; - - if (extract_event(event, &inbuf, inputmode)) - { - return event->type; - } - - // it looks like input buffer is incomplete, let's try the short path - size_t r = fread(buf, 1, ENOUGH_DATA_FOR_INPUT_PARSING, in); - - if (r < ENOUGH_DATA_FOR_INPUT_PARSING && feof(in)) - { - clearerr(in); - } - - if (r > 0) - { - if (ringbuffer_free_space(&inbuf) < r) - { - return -1; - } - - ringbuffer_push(&inbuf, buf, r); - - if (extract_event(event, &inbuf, inputmode)) - { - return event->type; - } - } - - // no stuff in FILE's internal buffer, block in select - while (1) - { - FD_ZERO(&events); - FD_SET(in_fileno, &events); - FD_SET(winch_fds[0], &events); - int maxfd = (winch_fds[0] > in_fileno) ? winch_fds[0] : in_fileno; - result = select(maxfd + 1, &events, 0, 0, timeout); - - if (!result) - { - return 0; - } - - if (FD_ISSET(in_fileno, &events)) - { - event->type = TB_EVENT_KEY; - size_t r = fread(buf, 1, ENOUGH_DATA_FOR_INPUT_PARSING, in); - - if (r < ENOUGH_DATA_FOR_INPUT_PARSING && feof(in)) - { - clearerr(in); - } - - if (r == 0) - { - continue; - } - - // if there is no free space in input buffer, return error - if (ringbuffer_free_space(&inbuf) < r) - { - return -1; - } - - // fill buffer - ringbuffer_push(&inbuf, buf, r); - - if (extract_event(event, &inbuf, inputmode)) - { - return event->type; - } - } - - if (FD_ISSET(winch_fds[0], &events)) - { - event->type = TB_EVENT_RESIZE; - int zzz = 0; - read(winch_fds[0], &zzz, sizeof(int)); - buffer_size_change_request = 1; - get_term_size(&event->w, &event->h); - return TB_EVENT_RESIZE; - } - } -} diff --git a/framework/3rd/termbox/src/utf8.c b/framework/3rd/termbox/src/utf8.c deleted file mode 100755 index 43efd7f..0000000 --- a/framework/3rd/termbox/src/utf8.c +++ /dev/null @@ -1,106 +0,0 @@ -#include "termbox.h" - -static const unsigned char utf8_length[256] = -{ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1 -}; - -static const unsigned char utf8_mask[6] = -{ - 0x7F, - 0x1F, - 0x0F, - 0x07, - 0x03, - 0x01 -}; - -int utf8_char_length(char c) -{ - return utf8_length[(unsigned char)c]; -} - -int utf8_char_to_unicode(uint32_t* out, const char* c) -{ - if (*c == 0) - { - return TB_EOF; - } - - int i; - unsigned char len = utf8_char_length(*c); - unsigned char mask = utf8_mask[len - 1]; - uint32_t result = c[0] & mask; - - for (i = 1; i < len; ++i) - { - result <<= 6; - result |= c[i] & 0x3f; - } - - *out = result; - return (int)len; -} - -int utf8_unicode_to_char(char* out, uint32_t c) -{ - int len = 0; - int first; - int i; - - if (c < 0x80) - { - first = 0; - len = 1; - } - else if (c < 0x800) - { - first = 0xc0; - len = 2; - } - else if (c < 0x10000) - { - first = 0xe0; - len = 3; - } - else if (c < 0x200000) - { - first = 0xf0; - len = 4; - } - else if (c < 0x4000000) - { - first = 0xf8; - len = 5; - } - else - { - first = 0xfc; - len = 6; - } - - for (i = len - 1; i > 0; --i) - { - out[i] = (c & 0x3f) | 0x80; - c >>= 6; - } - - out[0] = c | first; - - return len; -} diff --git a/framework/3rd/termbox/tools/astylerc b/framework/3rd/termbox/tools/astylerc deleted file mode 100755 index a296bc3..0000000 --- a/framework/3rd/termbox/tools/astylerc +++ /dev/null @@ -1,27 +0,0 @@ ---style=break ---indent=force-tab=4 ---indent-classes ---indent-switches ---indent-namespaces ---indent-after-parens ---indent-continuation=1 ---indent-preproc-block ---indent-preproc-define ---indent-preproc-cond ---indent-col1-comments ---min-conditional-indent=0 ---max-continuation-indent=40 ---break-blocks ---pad-oper ---pad-comma ---pad-header ---unpad-paren ---align-pointer=type ---align-reference=type ---break-one-line-headers ---add-braces ---attach-return-type ---attach-return-type-decl ---remove-comment-prefix ---max-code-length=80 ---mode=c diff --git a/framework/3rd/termbox/tools/collect_terminfo.py b/framework/3rd/termbox/tools/collect_terminfo.py deleted file mode 100755 index 596c3c4..0000000 --- a/framework/3rd/termbox/tools/collect_terminfo.py +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env python - -import sys, os, subprocess - -def escaped(s): - return s.replace("\033", "\\033") - -def tput(term, name): - try: - return subprocess.check_output(['tput', '-T%s' % term, name]).decode() - except subprocess.CalledProcessError as e: - return e.output.decode() - - -def w(s): - if s == None: - return - sys.stdout.write(s) - -terminals = { - 'xterm' : 'xterm', - 'rxvt-256color' : 'rxvt_256color', - 'rxvt-unicode' : 'rxvt_unicode', - 'linux' : 'linux', - 'Eterm' : 'eterm', - 'screen' : 'screen' -} - -keys = [ - "F1", "kf1", - "F2", "kf2", - "F3", "kf3", - "F4", "kf4", - "F5", "kf5", - "F6", "kf6", - "F7", "kf7", - "F8", "kf8", - "F9", "kf9", - "F10", "kf10", - "F11", "kf11", - "F12", "kf12", - "INSERT", "kich1", - "DELETE", "kdch1", - "HOME", "khome", - "END", "kend", - "PGUP", "kpp", - "PGDN", "knp", - "KEY_UP", "kcuu1", - "KEY_DOWN", "kcud1", - "KEY_LEFT", "kcub1", - "KEY_RIGHT", "kcuf1" -] - -funcs = [ - "T_ENTER_CA", "smcup", - "T_EXIT_CA", "rmcup", - "T_SHOW_CURSOR", "cnorm", - "T_HIDE_CURSOR", "civis", - "T_CLEAR_SCREEN", "clear", - "T_SGR0", "sgr0", - "T_UNDERLINE", "smul", - "T_BOLD", "bold", - "T_BLINK", "blink", - "T_REVERSE", "rev", - "T_ENTER_KEYPAD", "smkx", - "T_EXIT_KEYPAD", "rmkx" -] - -def iter_pairs(iterable): - iterable = iter(iterable) - while True: - yield (next(iterable), next(iterable)) - -def do_term(term, nick): - w("// %s\n" % term) - w("static const char *%s_keys[] = {\n\t" % nick) - for k, v in iter_pairs(keys): - w('"') - w(escaped(tput(term, v))) - w('",') - w(" 0\n};\n") - w("static const char *%s_funcs[] = {\n\t" % nick) - for k,v in iter_pairs(funcs): - w('"') - if v == "sgr": - w("\\033[3%d;4%dm") - elif v == "cup": - w("\\033[%d;%dH") - else: - w(escaped(tput(term, v))) - w('", ') - w("\n};\n\n") - -def do_terms(d): - w("static struct term {\n") - w("\tconst char *name;\n") - w("\tconst char **keys;\n") - w("\tconst char **funcs;\n") - w("} terms[] = {\n") - for k, v in d.items(): - w('\t{"%s", %s_keys, %s_funcs},\n' % (k, v, v)) - w("\t{0, 0, 0},\n") - w("};\n") - -for k,v in terminals.items(): - do_term(k, v) - -do_terms(terminals) diff --git a/framework/lualib-src/Makefile b/framework/lualib-src/Makefile index f47b369..0948b9c 100644 --- a/framework/lualib-src/Makefile +++ b/framework/lualib-src/Makefile @@ -16,6 +16,7 @@ LFS_SO = $(LUA_CLIB_PATH)/lfs.so CJSON_SO = $(LUA_CLIB_PATH)/cjson.so PROFILE_SO = $(LUA_CLIB_PATH)/profile.so SKIPLIST_SO = $(LUA_CLIB_PATH)/skiplist.so +SKIPSET_SO = $(LUA_CLIB_PATH)/skipset.so SNAPSHOT_SO = $(LUA_CLIB_PATH)/snapshot.so SHIFTTIMER_SO = $(LUA_CLIB_PATH)/shiftimer.so CLUA_SO = $(LUA_CLIB_PATH)/clua.so @@ -24,12 +25,14 @@ ECS_SO = $(LUA_CLIB_PATH)/ecs.so LUASOCKET_SO = $(LUA_CLIB_PATH)/socket.so TERMFX_SO = $(LUA_CLIB_PATH)/termfx.so RC4_SO = $(LUA_CLIB_PATH)/rc4.so +MATH_SO = $(LUA_CLIB_PATH)/math3d.so ##################################################### all: $(LFS_SO) \ $(CJSON_SO) \ $(PROFILE_SO) \ $(SKIPLIST_SO) \ + $(SKIPSET_SO) \ $(SNAPSHOT_SO) \ $(SHIFTTIMER_SO) \ $(AOI_SO) \ @@ -37,6 +40,7 @@ all: $(LFS_SO) \ $(TERMFX_SO) \ $(RC4_SO) \ $(CLUA_SO) \ + $(MATH_SO) \ $(LUASOCKET_SO) ##################################################### @@ -51,7 +55,10 @@ $(PROFILE_SO): cd lua-profile && $(MAKE) PLAT=$(PLAT) $(SKIPLIST_SO): - cd lua-zset && $(MAKE) PLAT=$(PLAT) + cd lua-skiplist && $(MAKE) PLAT=$(PLAT) + +$(SKIPSET_SO): + cd lua-skipset && $(MAKE) PLAT=$(PLAT) $(SNAPSHOT_SO): cd lua-snapshot && $(MAKE) PLAT=$(PLAT) @@ -68,6 +75,9 @@ $(CLUA_SO): $(ECS_SO): cd lua-ecs && $(MAKE) PLAT=$(PLAT) +$(MATH_SO): + cd math3d && $(MAKE) PLAT=$(PLAT) + $(RC4_SO): cd lua-rc4 && $(MAKE) PLAT=$(PLAT) diff --git a/framework/lualib-src/lua-zset/Makefile b/framework/lualib-src/lua-skiplist/Makefile similarity index 100% rename from framework/lualib-src/lua-zset/Makefile rename to framework/lualib-src/lua-skiplist/Makefile diff --git a/framework/lualib-src/lua-zset/lua-skiplist.c b/framework/lualib-src/lua-skiplist/lua-skiplist.c similarity index 77% rename from framework/lualib-src/lua-zset/lua-skiplist.c rename to framework/lualib-src/lua-skiplist/lua-skiplist.c index 80e0f76..74ba301 100644 --- a/framework/lualib-src/lua-zset/lua-skiplist.c +++ b/framework/lualib-src/lua-skiplist/lua-skiplist.c @@ -11,16 +11,19 @@ #include "skiplist.h" static inline skiplist * -_to_skiplist(lua_State *L) { +_to_skiplist(lua_State *L) +{ skiplist **sl = lua_touserdata(L, 1); - if (sl == NULL) { + if (sl == NULL) + { luaL_error(L, "must be skiplist object"); } return *sl; } static int -_insert(lua_State *L) { +_insert(lua_State *L) +{ skiplist *sl = _to_skiplist(L); double score = luaL_checknumber(L, 2); lua_Integer obj = luaL_checkinteger(L, 3); @@ -29,7 +32,8 @@ _insert(lua_State *L) { } static int -_delete(lua_State *L) { +_delete(lua_State *L) +{ skiplist *sl = _to_skiplist(L); double score = luaL_checknumber(L, 2); lua_Integer obj = luaL_checkinteger(L, 3); @@ -38,7 +42,8 @@ _delete(lua_State *L) { } static void -_delete_rank_cb(void *ud, int64_t obj) { +_delete_rank_cb(void *ud, int64_t obj) +{ lua_State *L = (lua_State *)ud; lua_pushvalue(L, 4); lua_pushinteger(L, obj); @@ -46,12 +51,14 @@ _delete_rank_cb(void *ud, int64_t obj) { } static int -_delete_by_rank(lua_State *L) { +_delete_by_rank(lua_State *L) +{ skiplist *sl = _to_skiplist(L); unsigned int start = luaL_checkinteger(L, 2); unsigned int end = luaL_checkinteger(L, 3); luaL_checktype(L, 4, LUA_TFUNCTION); - if (start > end) { + if (start > end) + { unsigned int tmp = start; start = end; end = tmp; @@ -62,20 +69,23 @@ _delete_by_rank(lua_State *L) { } static int -_get_count(lua_State *L) { +_get_count(lua_State *L) +{ skiplist *sl = _to_skiplist(L); lua_pushinteger(L, sl->length); return 1; } static int -_get_rank(lua_State *L) { +_get_rank(lua_State *L) +{ skiplist *sl = _to_skiplist(L); double score = luaL_checknumber(L, 2); lua_Integer obj = luaL_checkinteger(L, 3); unsigned long rank = slGetRank(sl, score, obj); - if (rank == 0) { + if (rank == 0) + { return 0; } @@ -84,15 +94,19 @@ _get_rank(lua_State *L) { } static int -_get_rank_range(lua_State *L) { +_get_rank_range(lua_State *L) +{ skiplist *sl = _to_skiplist(L); unsigned long r1 = luaL_checkinteger(L, 2); unsigned long r2 = luaL_checkinteger(L, 3); int reverse, rangelen; - if (r1 <= r2) { + if (r1 <= r2) + { reverse = 0; rangelen = r2 - r1 + 1; - } else { + } + else + { reverse = 1; rangelen = r1 - r2 + 1; } @@ -100,7 +114,8 @@ _get_rank_range(lua_State *L) { skiplistNode *node = slGetNodeByRank(sl, r1); lua_createtable(L, rangelen, 0); int n = 0; - while (node && n < rangelen) { + while (node && n < rangelen) + { n++; lua_pushinteger(L, node->obj); lua_rawseti(L, -2, n); @@ -110,15 +125,19 @@ _get_rank_range(lua_State *L) { } static int -_get_rank_score_range(lua_State *L) { +_get_rank_score_range(lua_State *L) +{ skiplist *sl = _to_skiplist(L); unsigned long r1 = luaL_checkinteger(L, 2); unsigned long r2 = luaL_checkinteger(L, 3); int reverse, rangelen; - if (r1 <= r2) { + if (r1 <= r2) + { reverse = 0; rangelen = r2 - r1 + 1; - } else { + } + else + { reverse = 1; rangelen = r1 - r2 + 1; } @@ -126,7 +145,8 @@ _get_rank_score_range(lua_State *L) { skiplistNode *node = slGetNodeByRank(sl, r1); lua_createtable(L, rangelen, 0); int n = 0; - while (node && n < rangelen) { + while (node && n < rangelen) + { n++; lua_createtable(L, 2, 0); lua_pushinteger(L, node->obj); @@ -141,28 +161,36 @@ _get_rank_score_range(lua_State *L) { } static int -_get_score_range(lua_State *L) { +_get_score_range(lua_State *L) +{ skiplist *sl = _to_skiplist(L); double s1 = luaL_checknumber(L, 2); double s2 = luaL_checknumber(L, 3); int reverse; skiplistNode *node; - if (s1 <= s2) { + if (s1 <= s2) + { reverse = 0; node = slFirstInRange(sl, s1, s2); - } else { + } + else + { reverse = 1; node = slLastInRange(sl, s2, s1); } lua_newtable(L); int n = 0; - while (node) { - if (reverse) { + while (node) + { + if (reverse) + { if (node->score < s2) break; - } else { + } + else + { if (node->score > s2) break; } @@ -177,11 +205,13 @@ _get_score_range(lua_State *L) { } static int -_get_member_by_rank(lua_State *L){ +_get_member_by_rank(lua_State *L) +{ skiplist *sl = _to_skiplist(L); unsigned long r = luaL_checkinteger(L, 2); skiplistNode *node = slGetNodeByRank(sl, r); - if (node) { + if (node) + { lua_pushinteger(L, node->obj); return 1; } @@ -189,7 +219,8 @@ _get_member_by_rank(lua_State *L){ } static int -_new(lua_State *L) { +_new(lua_State *L) +{ skiplist *psl = slCreate(); skiplist **sl = (skiplist **)lua_newuserdata(L, sizeof(skiplist *)); @@ -200,7 +231,8 @@ _new(lua_State *L) { } static int -_release(lua_State *L) { +_release(lua_State *L) +{ skiplist *sl = _to_skiplist(L); //printf("collect sl:%p\n", sl); slFree(sl); @@ -208,24 +240,24 @@ _release(lua_State *L) { } LUAMOD_API int -luaopen_skiplist_c(lua_State *L) { +luaopen_skiplist_c(lua_State *L) +{ luaL_checkversion(L); luaL_Reg l[] = { - { "insert", _insert }, - { "delete", _delete }, - { "delete_by_rank", _delete_by_rank }, + {"insert", _insert}, + {"delete", _delete}, + {"delete_by_rank", _delete_by_rank}, - { "get_count", _get_count }, - { "get_rank", _get_rank }, - { "get_rank_range", _get_rank_range }, - { "get_score_range", _get_score_range }, - { "get_member_by_rank", _get_member_by_rank}, + {"get_count", _get_count}, + {"get_rank", _get_rank}, + {"get_rank_range", _get_rank_range}, + {"get_score_range", _get_score_range}, + {"get_member_by_rank", _get_member_by_rank}, - { "get_rank_score_range", _get_rank_score_range }, + {"get_rank_score_range", _get_rank_score_range}, - { NULL, NULL } - }; + {NULL, NULL}}; lua_createtable(L, 0, 2); diff --git a/framework/lualib-src/lua-zset/skiplist.c b/framework/lualib-src/lua-skiplist/skiplist.c similarity index 81% rename from framework/lualib-src/lua-zset/skiplist.c rename to framework/lualib-src/lua-skiplist/skiplist.c index ba468e7..bcf0c5f 100644 --- a/framework/lualib-src/lua-zset/skiplist.c +++ b/framework/lualib-src/lua-skiplist/skiplist.c @@ -14,18 +14,21 @@ #define SKIPLIST_MAXLEVEL 32 #define SKIPLIST_P 0.25 -skiplistNode *slCreateNode(int level, double score, int64_t obj) { +skiplistNode *slCreateNode(int level, double score, int64_t obj) +{ skiplistNode *n = malloc(sizeof(*n) + level * sizeof(struct skiplistLevel)); n->score = score; n->obj = obj; return n; } -void slFreeNode(skiplistNode *node) { +void slFreeNode(skiplistNode *node) +{ free(node); } -skiplist *slCreate(void) { +skiplist *slCreate(void) +{ int j; skiplist *sl; @@ -33,7 +36,8 @@ skiplist *slCreate(void) { sl->level = 1; sl->length = 0; sl->header = slCreateNode(SKIPLIST_MAXLEVEL, 0, 0); - for (j = 0; j < SKIPLIST_MAXLEVEL; j++) { + for (j = 0; j < SKIPLIST_MAXLEVEL; j++) + { sl->header->level[j].forward = NULL; sl->header->level[j].span = 0; } @@ -42,10 +46,12 @@ skiplist *slCreate(void) { return sl; } -void slFree(skiplist *sl) { +void slFree(skiplist *sl) +{ skiplistNode *node = sl->header->level[0].forward, *next; free(sl->header); - while (node) { + while (node) + { next = node->level[0].forward; slFreeNode(node); node = next; @@ -53,23 +59,27 @@ void slFree(skiplist *sl) { free(sl); } -int slRandomLevel(void) { +int slRandomLevel(void) +{ int level = 1; while ((random() & 0xffff) < (SKIPLIST_P * 0xffff)) level += 1; return (level < SKIPLIST_MAXLEVEL) ? level : SKIPLIST_MAXLEVEL; } -void slInsert(skiplist *sl, double score, int64_t obj) { +void slInsert(skiplist *sl, double score, int64_t obj) +{ skiplistNode *update[SKIPLIST_MAXLEVEL], *x; unsigned int rank[SKIPLIST_MAXLEVEL]; int i, level; x = sl->header; - for (i = sl->level - 1; i >= 0; i--) { + for (i = sl->level - 1; i >= 0; i--) + { /* store rank that is crossed to reach the insert position */ rank[i] = i == (sl->level - 1) ? 0 : rank[i + 1]; - while (x->level[i].forward && (x->level[i].forward->score < score || (x->level[i].forward->score == score && (x->level[i].forward->obj - obj) < 0))) { + while (x->level[i].forward && (x->level[i].forward->score < score || (x->level[i].forward->score == score && (x->level[i].forward->obj - obj) < 0))) + { rank[i] += x->level[i].span; x = x->level[i].forward; } @@ -80,8 +90,10 @@ void slInsert(skiplist *sl, double score, int64_t obj) { * happen since the caller of slInsert() should test in the hash table * if the element is already inside or not. */ level = slRandomLevel(); - if (level > sl->level) { - for (i = sl->level; i < level; i++) { + if (level > sl->level) + { + for (i = sl->level; i < level; i++) + { rank[i] = 0; update[i] = sl->header; update[i]->level[i].span = sl->length; @@ -89,7 +101,8 @@ void slInsert(skiplist *sl, double score, int64_t obj) { sl->level = level; } x = slCreateNode(level, score, obj); - for (i = 0; i < level; i++) { + for (i = 0; i < level; i++) + { x->level[i].forward = update[i]->level[i].forward; update[i]->level[i].forward = x; @@ -99,7 +112,8 @@ void slInsert(skiplist *sl, double score, int64_t obj) { } /* increment span for untouched levels */ - for (i = level; i < sl->level; i++) { + for (i = level; i < sl->level; i++) + { update[i]->level[i].span++; } @@ -112,19 +126,27 @@ void slInsert(skiplist *sl, double score, int64_t obj) { } /* Internal function used by slDelete, slDeleteByScore */ -void slDeleteNode(skiplist *sl, skiplistNode *x, skiplistNode **update) { +void slDeleteNode(skiplist *sl, skiplistNode *x, skiplistNode **update) +{ int i; - for (i = 0; i < sl->level; i++) { - if (update[i]->level[i].forward == x) { + for (i = 0; i < sl->level; i++) + { + if (update[i]->level[i].forward == x) + { update[i]->level[i].span += x->level[i].span - 1; update[i]->level[i].forward = x->level[i].forward; - } else { + } + else + { update[i]->level[i].span -= 1; } } - if (x->level[0].forward) { + if (x->level[0].forward) + { x->level[0].forward->backward = x->backward; - } else { + } + else + { sl->tail = x->backward; } while (sl->level > 1 && sl->header->level[sl->level - 1].forward == NULL) @@ -133,12 +155,14 @@ void slDeleteNode(skiplist *sl, skiplistNode *x, skiplistNode **update) { } /* Delete an element with matching score/object from the skiplist. */ -int slDelete(skiplist *sl, double score, int64_t obj) { +int slDelete(skiplist *sl, double score, int64_t obj) +{ skiplistNode *update[SKIPLIST_MAXLEVEL], *x; int i; x = sl->header; - for (i = sl->level - 1; i >= 0; i--) { + for (i = sl->level - 1; i >= 0; i--) + { while (x->level[i].forward && (x->level[i].forward->score < score || (x->level[i].forward->score == score && (x->level[i].forward->obj - obj) < 0))) x = x->level[i].forward; update[i] = x; @@ -146,11 +170,14 @@ int slDelete(skiplist *sl, double score, int64_t obj) { /* We may have multiple elements with the same score, what we need * is to find the element with both the right score and object. */ x = x->level[0].forward; - if (x && score == x->score && (x->obj == obj)) { + if (x && score == x->score && (x->obj == obj)) + { slDeleteNode(sl, x, update); slFreeNode(x); return 1; - } else { + } + else + { return 0; /* not found */ } return 0; /* not found */ @@ -158,14 +185,17 @@ int slDelete(skiplist *sl, double score, int64_t obj) { /* Delete all the elements with rank between start and end from the skiplist. * Start and end are inclusive. Note that start and end need to be 1-based */ -unsigned long slDeleteByRank(skiplist *sl, unsigned int start, unsigned int end, slDeleteCb cb, void *ud) { +unsigned long slDeleteByRank(skiplist *sl, unsigned int start, unsigned int end, slDeleteCb cb, void *ud) +{ skiplistNode *update[SKIPLIST_MAXLEVEL], *x; unsigned long traversed = 0, removed = 0; int i; x = sl->header; - for (i = sl->level - 1; i >= 0; i--) { - while (x->level[i].forward && (traversed + x->level[i].span) < start) { + for (i = sl->level - 1; i >= 0; i--) + { + while (x->level[i].forward && (traversed + x->level[i].span) < start) + { traversed += x->level[i].span; x = x->level[i].forward; } @@ -174,7 +204,8 @@ unsigned long slDeleteByRank(skiplist *sl, unsigned int start, unsigned int end, traversed++; x = x->level[0].forward; - while (x && traversed <= end) { + while (x && traversed <= end) + { skiplistNode *next = x->level[0].forward; slDeleteNode(sl, x, update); cb(ud, x->obj); @@ -190,20 +221,24 @@ unsigned long slDeleteByRank(skiplist *sl, unsigned int start, unsigned int end, * Returns 0 when the element cannot be found, rank otherwise. * Note that the rank is 1-based due to the span of sl->header to the * first element. */ -unsigned long slGetRank(skiplist *sl, double score, int64_t o) { +unsigned long slGetRank(skiplist *sl, double score, int64_t o) +{ skiplistNode *x; unsigned long rank = 0; int i; x = sl->header; - for (i = sl->level - 1; i >= 0; i--) { - while (x->level[i].forward && (x->level[i].forward->score < score || (x->level[i].forward->score == score && (x->level[i].forward->obj - o) <= 0))) { + for (i = sl->level - 1; i >= 0; i--) + { + while (x->level[i].forward && (x->level[i].forward->score < score || (x->level[i].forward->score == score && (x->level[i].forward->obj - o) <= 0))) + { rank += x->level[i].span; x = x->level[i].forward; } /* x might be equal to sl->header, so test if obj is non-NULL */ - if (x->obj && (x->obj == o)) { + if (x->obj && (x->obj == o)) + { return rank; } } @@ -211,8 +246,10 @@ unsigned long slGetRank(skiplist *sl, double score, int64_t o) { } /* Finds an element by its rank. The rank argument needs to be 1-based. */ -skiplistNode *slGetNodeByRank(skiplist *sl, unsigned long rank) { - if (rank == 0 || rank > sl->length) { +skiplistNode *slGetNodeByRank(skiplist *sl, unsigned long rank) +{ + if (rank == 0 || rank > sl->length) + { return NULL; } @@ -221,12 +258,15 @@ skiplistNode *slGetNodeByRank(skiplist *sl, unsigned long rank) { int i; x = sl->header; - for (i = sl->level - 1; i >= 0; i--) { - while (x->level[i].forward && (traversed + x->level[i].span) <= rank) { + for (i = sl->level - 1; i >= 0; i--) + { + while (x->level[i].forward && (traversed + x->level[i].span) <= rank) + { traversed += x->level[i].span; x = x->level[i].forward; } - if (traversed == rank) { + if (traversed == rank) + { return x; } } @@ -236,11 +276,13 @@ skiplistNode *slGetNodeByRank(skiplist *sl, unsigned long rank) { /* range [min, max], left & right both include */ /* Returns if there is a part of the zset is in range. */ -int slIsInRange(skiplist *sl, double min, double max) { +int slIsInRange(skiplist *sl, double min, double max) +{ skiplistNode *x; /* Test for ranges that will always be empty. */ - if (min > max) { + if (min > max) + { return 0; } x = sl->tail; @@ -255,7 +297,8 @@ int slIsInRange(skiplist *sl, double min, double max) { /* Find the first node that is contained in the specified range. * Returns NULL when no element is contained in the range. */ -skiplistNode *slFirstInRange(skiplist *sl, double min, double max) { +skiplistNode *slFirstInRange(skiplist *sl, double min, double max) +{ skiplistNode *x; int i; @@ -264,7 +307,8 @@ skiplistNode *slFirstInRange(skiplist *sl, double min, double max) { return NULL; x = sl->header; - for (i = sl->level - 1; i >= 0; i--) { + for (i = sl->level - 1; i >= 0; i--) + { /* Go forward while *OUT* of range. */ while (x->level[i].forward && x->level[i].forward->score < min) x = x->level[i].forward; @@ -277,7 +321,8 @@ skiplistNode *slFirstInRange(skiplist *sl, double min, double max) { /* Find the last node that is contained in the specified range. * Returns NULL when no element is contained in the range. */ -skiplistNode *slLastInRange(skiplist *sl, double min, double max) { +skiplistNode *slLastInRange(skiplist *sl, double min, double max) +{ skiplistNode *x; int i; @@ -286,7 +331,8 @@ skiplistNode *slLastInRange(skiplist *sl, double min, double max) { return NULL; x = sl->header; - for (i = sl->level - 1; i >= 0; i--) { + for (i = sl->level - 1; i >= 0; i--) + { /* Go forward while *IN* range. */ while (x->level[i].forward && x->level[i].forward->score <= max) x = x->level[i].forward; diff --git a/framework/lualib-src/lua-zset/skiplist.h b/framework/lualib-src/lua-skiplist/skiplist.h similarity index 89% rename from framework/lualib-src/lua-zset/skiplist.h rename to framework/lualib-src/lua-skiplist/skiplist.h index d4c4dbd..dc06c89 100644 --- a/framework/lualib-src/lua-zset/skiplist.h +++ b/framework/lualib-src/lua-skiplist/skiplist.h @@ -3,17 +3,20 @@ #include -typedef struct skiplistNode { +typedef struct skiplistNode +{ int64_t obj; double score; struct skiplistNode *backward; - struct skiplistLevel { + struct skiplistLevel + { struct skiplistNode *forward; unsigned int span; } level[]; } skiplistNode; -typedef struct skiplist { +typedef struct skiplist +{ struct skiplistNode *header, *tail; unsigned long length; int level; diff --git a/framework/lualib-src/lua-skipset/Makefile b/framework/lualib-src/lua-skipset/Makefile new file mode 100644 index 0000000..74530ce --- /dev/null +++ b/framework/lualib-src/lua-skipset/Makefile @@ -0,0 +1,30 @@ +SKYNET_ROOT ?= ../../skynet +include $(SKYNET_ROOT)/platform.mk + +PLAT ?= none + +TARGET = ../../luaclib/skipset.so + +ifeq ($(PLAT), macosx) + CFLAGS = -g -O2 -dynamiclib -Wl,-undefined,dynamic_lookup +else +ifeq ($(PLAT), linux) + CFLAGS = -g -O2 -shared -fPIC +endif +endif + +LUA_LIB ?= $(SKYNET_ROOT)/3rd/lua/ +LUA_INC ?= $(SKYNET_ROOT)/3rd/lua/ +SKYNET_SRC ?= $(SKYNET_ROOT)/skynet-src + +SRC = . + +.PHONY: all clean + +all: $(TARGET) + +$(TARGET): $(foreach dir, $(SRC), $(wildcard $(dir)/*.c)) + $(CC) $(CFLAGS) $(SHARED) -I$(LUA_INC) -I$(SKYNET_SRC) $^ -o $@ + +clean: + rm -f *.o $(TARGET) \ No newline at end of file diff --git a/framework/lualib-src/lua-zset/lua-skipset.c b/framework/lualib-src/lua-skipset/lua-skipset.c similarity index 75% rename from framework/lualib-src/lua-zset/lua-skipset.c rename to framework/lualib-src/lua-skipset/lua-skipset.c index 42cf1c4..1114622 100644 --- a/framework/lualib-src/lua-zset/lua-skipset.c +++ b/framework/lualib-src/lua-skipset/lua-skipset.c @@ -6,46 +6,56 @@ #include "lauxlib.h" #include "lua.h" +// https://github.com/hongling0/lua-zset + #define SKIPLIST_MAXLEVEL 32 #define SKIPLIST_P 0.25 -typedef struct skipsetNode { +typedef struct skipsetNode +{ int64_t obj; struct skipsetNode *backward; - struct skiplistLevel { + struct skiplistLevel + { struct skipsetNode *forward; unsigned int span; } level[]; } skipsetNode; -typedef struct skipset { +typedef struct skipset +{ struct skipsetNode *header, *tail; unsigned long length; int level; } skipset; -static int slRandomLevel(void) { +static int slRandomLevel(void) +{ int level = 1; while ((random() & 0xffff) < (SKIPLIST_P * 0xffff)) level += 1; return (level < SKIPLIST_MAXLEVEL) ? level : SKIPLIST_MAXLEVEL; } -static skipsetNode *slCreateNode(int level, int64_t obj) { +static skipsetNode *slCreateNode(int level, int64_t obj) +{ skipsetNode *n = malloc(sizeof(*n) + level * sizeof(struct skiplistLevel)); n->obj = obj; return n; } -static int slInsert(skipset *sl, int64_t obj) { +static int slInsert(skipset *sl, int64_t obj) +{ skipsetNode *update[SKIPLIST_MAXLEVEL], *x; unsigned int rank[SKIPLIST_MAXLEVEL]; int i, level; x = sl->header; - for (i = sl->level - 1; i >= 0; i--) { + for (i = sl->level - 1; i >= 0; i--) + { rank[i] = i == (sl->level - 1) ? 0 : rank[i + 1]; - while (x->level[i].forward && x->level[i].forward->obj < obj) { + while (x->level[i].forward && x->level[i].forward->obj < obj) + { rank[i] += x->level[i].span; x = x->level[i].forward; } @@ -56,8 +66,10 @@ static int slInsert(skipset *sl, int64_t obj) { return 0; level = slRandomLevel(); - if (level > sl->level) { - for (i = sl->level; i < level; i++) { + if (level > sl->level) + { + for (i = sl->level; i < level; i++) + { rank[i] = 0; update[i] = sl->header; update[i]->level[i].span = sl->length; @@ -65,7 +77,8 @@ static int slInsert(skipset *sl, int64_t obj) { sl->level = level; } x = slCreateNode(level, obj); - for (i = 0; i < level; i++) { + for (i = 0; i < level; i++) + { x->level[i].forward = update[i]->level[i].forward; update[i]->level[i].forward = x; @@ -73,7 +86,8 @@ static int slInsert(skipset *sl, int64_t obj) { update[i]->level[i].span = (rank[0] - rank[i]) + 1; } - for (i = level; i < sl->level; i++) { + for (i = level; i < sl->level; i++) + { update[i]->level[i].span++; } @@ -86,19 +100,27 @@ static int slInsert(skipset *sl, int64_t obj) { return 1; } -static void slDeleteNode(skipset *sl, skipsetNode *x, skipsetNode **update) { +static void slDeleteNode(skipset *sl, skipsetNode *x, skipsetNode **update) +{ int i; - for (i = 0; i < sl->level; i++) { - if (update[i]->level[i].forward == x) { + for (i = 0; i < sl->level; i++) + { + if (update[i]->level[i].forward == x) + { update[i]->level[i].span += x->level[i].span - 1; update[i]->level[i].forward = x->level[i].forward; - } else { + } + else + { update[i]->level[i].span -= 1; } } - if (x->level[0].forward) { + if (x->level[0].forward) + { x->level[0].forward->backward = x->backward; - } else { + } + else + { sl->tail = x->backward; } while (sl->level > 1 && sl->header->level[sl->level - 1].forward == NULL) @@ -106,51 +128,62 @@ static void slDeleteNode(skipset *sl, skipsetNode *x, skipsetNode **update) { sl->length--; } -static void slFreeNode(skipsetNode *node) { +static void slFreeNode(skipsetNode *node) +{ free(node); } -static int slDelete(skipset *sl, int64_t obj) { +static int slDelete(skipset *sl, int64_t obj) +{ skipsetNode *update[SKIPLIST_MAXLEVEL], *x; int i; x = sl->header; - for (i = sl->level - 1; i >= 0; i--) { + for (i = sl->level - 1; i >= 0; i--) + { while (x->level[i].forward && x->level[i].forward->obj < obj) x = x->level[i].forward; update[i] = x; } x = x->level[0].forward; - if (x && (x->obj == obj)) { + if (x && (x->obj == obj)) + { slDeleteNode(sl, x, update); slFreeNode(x); return 1; - } else { + } + else + { return 0; /* not found */ } return 0; /* not found */ } -static unsigned long slGetRank(skipset *sl, int64_t obj) { +static unsigned long slGetRank(skipset *sl, int64_t obj) +{ skipsetNode *x; unsigned long rank = 0; int i; x = sl->header; - for (i = sl->level - 1; i >= 0; i--) { - while (x->level[i].forward && x->level[i].forward->obj <= obj) { + for (i = sl->level - 1; i >= 0; i--) + { + while (x->level[i].forward && x->level[i].forward->obj <= obj) + { rank += x->level[i].span; x = x->level[i].forward; } - if (x->obj && (x->obj == obj)) { + if (x->obj && (x->obj == obj)) + { return rank; } } return 0; } -static skipsetNode *slGetNodeByRank(skipset *sl, unsigned long rank) { +static skipsetNode *slGetNodeByRank(skipset *sl, unsigned long rank) +{ if (rank == 0 || rank > sl->length) return NULL; @@ -159,12 +192,15 @@ static skipsetNode *slGetNodeByRank(skipset *sl, unsigned long rank) { int i; x = sl->header; - for (i = sl->level - 1; i >= 0; i--) { - while (x->level[i].forward && (traversed + x->level[i].span) <= rank) { + for (i = sl->level - 1; i >= 0; i--) + { + while (x->level[i].forward && (traversed + x->level[i].span) <= rank) + { traversed += x->level[i].span; x = x->level[i].forward; } - if (traversed == rank) { + if (traversed == rank) + { return x; } } @@ -172,14 +208,17 @@ static skipsetNode *slGetNodeByRank(skipset *sl, unsigned long rank) { return NULL; } -unsigned long slDeleteByRank(skipset *sl, unsigned int rank) { +unsigned long slDeleteByRank(skipset *sl, unsigned int rank) +{ skipsetNode *update[SKIPLIST_MAXLEVEL], *x; unsigned long traversed = 0; int i; x = sl->header; - for (i = sl->level - 1; i >= 0; i--) { - while (x->level[i].forward && (traversed + x->level[i].span) < rank) { + for (i = sl->level - 1; i >= 0; i--) + { + while (x->level[i].forward && (traversed + x->level[i].span) < rank) + { traversed += x->level[i].span; x = x->level[i].forward; } @@ -187,7 +226,8 @@ unsigned long slDeleteByRank(skipset *sl, unsigned int rank) { } x = x->level[0].forward; - if (x) { + if (x) + { slDeleteNode(sl, x, update); slFreeNode(x); return 1; @@ -195,7 +235,8 @@ unsigned long slDeleteByRank(skipset *sl, unsigned int rank) { return 0; } -static skipset *slCreate(void) { +static skipset *slCreate(void) +{ int j; skipset *sl; @@ -203,7 +244,8 @@ static skipset *slCreate(void) { sl->level = 1; sl->length = 0; sl->header = slCreateNode(SKIPLIST_MAXLEVEL, 0); - for (j = 0; j < SKIPLIST_MAXLEVEL; j++) { + for (j = 0; j < SKIPLIST_MAXLEVEL; j++) + { sl->header->level[j].forward = NULL; sl->header->level[j].span = 0; } @@ -212,10 +254,12 @@ static skipset *slCreate(void) { return sl; } -static void slFree(skipset *sl) { +static void slFree(skipset *sl) +{ skipsetNode *node = sl->header->level[0].forward, *next; free(sl->header); - while (node) { + while (node) + { next = node->level[0].forward; slFreeNode(node); node = next; @@ -224,16 +268,19 @@ static void slFree(skipset *sl) { } static inline skipset * -_to_skipset(lua_State *L, int idx) { +_to_skipset(lua_State *L, int idx) +{ skipset **sl = lua_touserdata(L, idx); - if (sl == NULL) { + if (sl == NULL) + { luaL_error(L, "must be skipset object"); } return *sl; } static int -_insert(lua_State *L) { +_insert(lua_State *L) +{ skipset *sl = _to_skipset(L, 1); lua_Integer obj = luaL_checkinteger(L, 2); lua_pushboolean(L, slInsert(sl, obj)); @@ -241,7 +288,8 @@ _insert(lua_State *L) { } static int -_delete(lua_State *L) { +_delete(lua_State *L) +{ skipset *sl = _to_skipset(L, 1); lua_Integer obj = luaL_checkinteger(L, 2); lua_pushboolean(L, slDelete(sl, obj)); @@ -249,19 +297,22 @@ _delete(lua_State *L) { } static int -_get_count(lua_State *L) { +_get_count(lua_State *L) +{ skipset *sl = _to_skipset(L, 1); lua_pushinteger(L, sl->length); return 1; } static int -_get_rank(lua_State *L) { +_get_rank(lua_State *L) +{ skipset *sl = _to_skipset(L, 1); lua_Integer obj = luaL_checkinteger(L, 2); unsigned long rank = slGetRank(sl, obj); - if (rank == 0) { + if (rank == 0) + { return 0; } @@ -270,12 +321,14 @@ _get_rank(lua_State *L) { } static int -_get_by_rank(lua_State *L) { +_get_by_rank(lua_State *L) +{ skipset *sl = _to_skipset(L, 1); unsigned long r1 = luaL_checkinteger(L, 2); skipsetNode *node = slGetNodeByRank(sl, r1); - if (node) { + if (node) + { lua_pushinteger(L, node->obj); return 1; } @@ -283,7 +336,8 @@ _get_by_rank(lua_State *L) { } static int -_delete_by_rank(lua_State *L) { +_delete_by_rank(lua_State *L) +{ skipset *sl = _to_skipset(L, 1); unsigned int rank = luaL_checkinteger(L, 2); lua_pushinteger(L, slDeleteByRank(sl, rank)); @@ -291,7 +345,8 @@ _delete_by_rank(lua_State *L) { } static int -_new(lua_State *L) { +_new(lua_State *L) +{ skipset *psl = slCreate(); skipset **sl = (skipset **)lua_newuserdata(L, sizeof(skipset *)); @@ -302,27 +357,28 @@ _new(lua_State *L) { } static int -_release(lua_State *L) { +_release(lua_State *L) +{ skipset *sl = _to_skipset(L, 1); slFree(sl); return 0; } LUAMOD_API int -luaopen_skipset_c(lua_State *L) { +luaopen_skipset_c(lua_State *L) +{ luaL_checkversion(L); luaL_Reg l[] = { - { "insert", _insert }, - { "delete", _delete }, - { "delete_byrank", _delete_by_rank }, + {"insert", _insert}, + {"delete", _delete}, + {"delete_byrank", _delete_by_rank}, - { "count", _get_count }, - { "rank", _get_rank }, - { "byrank", _get_by_rank }, + {"count", _get_count}, + {"rank", _get_rank}, + {"byrank", _get_by_rank}, - { NULL, NULL } - }; + {NULL, NULL}}; lua_createtable(L, 0, 2); diff --git a/framework/3rd/termbox/src/termbox.h b/framework/lualib-src/lua-termfx/termbox.h old mode 100755 new mode 100644 similarity index 100% rename from framework/3rd/termbox/src/termbox.h rename to framework/lualib-src/lua-termfx/termbox.h diff --git a/framework/lualib-src/testluaclib.lua b/framework/lualib-src/testluaclib.lua index 90771cd..b6f3ca9 100644 --- a/framework/lualib-src/testluaclib.lua +++ b/framework/lualib-src/testluaclib.lua @@ -34,3 +34,9 @@ print("termfx", termfx) local socket = require "socket.core" print("socket", socket) +local skipset = require "skipset.c" +print("skipset", skipset) + +local math3d = require "math3d" +print("math3d", math3d) + diff --git a/framework/skynet b/framework/skynet index ff24ce6..bc298e4 160000 --- a/framework/skynet +++ b/framework/skynet @@ -1 +1 @@ -Subproject commit ff24ce6b004259f4e372e0f82bd9104a8b9a1b7b +Subproject commit bc298e4a91d016df653e9a48f118672d209a3a05