Very basic Notcurses support. There are bugs! (0.37.45)
This commit is contained in:
parent
4a6903e58a
commit
fd582f4d3a
30 changed files with 465 additions and 5 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
project(fun VERSION 0.37.44 LANGUAGES C)
|
project(fun VERSION 0.37.45 LANGUAGES C)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 99)
|
set(CMAKE_C_STANDARD 99)
|
||||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
|
|
@ -474,6 +474,38 @@ if(M_LIB)
|
||||||
target_link_libraries(fun_core PUBLIC ${M_LIB})
|
target_link_libraries(fun_core PUBLIC ${M_LIB})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Optional Notcurses TUI support (per-opcode files under src/vm/notcurses)
|
||||||
|
option(FUN_WITH_NOTCURSES "Enable Notcurses TUI support" OFF)
|
||||||
|
set(NOTCURSES_INCLUDE_DIRS "")
|
||||||
|
set(NOTCURSES_LINK_LIBS "")
|
||||||
|
if(FUN_WITH_NOTCURSES)
|
||||||
|
message(STATUS "FUN_WITH_NOTCURSES=ON: enabling Notcurses TUI ops")
|
||||||
|
add_definitions(-DFUN_WITH_NOTCURSES)
|
||||||
|
find_package(PkgConfig QUIET)
|
||||||
|
if(PkgConfig_FOUND)
|
||||||
|
# Prefer full 'notcurses', fall back to 'notcurses-core'
|
||||||
|
pkg_check_modules(NOTCURSES QUIET notcurses)
|
||||||
|
if(NOT NOTCURSES_FOUND)
|
||||||
|
pkg_check_modules(NOTCURSES QUIET notcurses-core)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if(NOTCURSES_FOUND)
|
||||||
|
list(APPEND NOTCURSES_INCLUDE_DIRS ${NOTCURSES_INCLUDE_DIRS} ${NOTCURSES_INCLUDE_DIRS})
|
||||||
|
list(APPEND NOTCURSES_LINK_LIBS ${NOTCURSES_LINK_LIBS} ${NOTCURSES_LIBRARIES})
|
||||||
|
include_directories(${NOTCURSES_INCLUDE_DIRS})
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "FUN_WITH_NOTCURSES=ON but notcurses was not found via pkg-config (tried notcurses and notcurses-core). Install notcurses or configure with -DFUN_WITH_NOTCURSES=OFF")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# notcurses include and link (if enabled)
|
||||||
|
if(NOTCURSES_INCLUDE_DIRS)
|
||||||
|
target_include_directories(fun_core PRIVATE ${NOTCURSES_INCLUDE_DIRS})
|
||||||
|
endif()
|
||||||
|
if(NOTCURSES_LINK_LIBS)
|
||||||
|
target_link_libraries(fun_core PUBLIC ${NOTCURSES_LINK_LIBS})
|
||||||
|
endif()
|
||||||
|
|
||||||
# Interpreter /usr/bin/fun
|
# Interpreter /usr/bin/fun
|
||||||
option(FUN_WITH_REPL "Enable interactive REPL in the fun CLI" OFF)
|
option(FUN_WITH_REPL "Enable interactive REPL in the fun CLI" OFF)
|
||||||
add_executable(fun
|
add_executable(fun
|
||||||
|
|
|
||||||
0
examples/ini_diag.fun
Normal file → Executable file
0
examples/ini_diag.fun
Normal file → Executable file
0
examples/math_ceil.fun
Normal file → Executable file
0
examples/math_ceil.fun
Normal file → Executable file
0
examples/math_cos.fun
Normal file → Executable file
0
examples/math_cos.fun
Normal file → Executable file
0
examples/math_exp_log.fun
Normal file → Executable file
0
examples/math_exp_log.fun
Normal file → Executable file
0
examples/math_floor.fun
Normal file → Executable file
0
examples/math_floor.fun
Normal file → Executable file
0
examples/math_fmin_fmax.fun
Normal file → Executable file
0
examples/math_fmin_fmax.fun
Normal file → Executable file
0
examples/math_gcd_lcm.fun
Normal file → Executable file
0
examples/math_gcd_lcm.fun
Normal file → Executable file
0
examples/math_isqrt.fun
Normal file → Executable file
0
examples/math_isqrt.fun
Normal file → Executable file
0
examples/math_round.fun
Normal file → Executable file
0
examples/math_round.fun
Normal file → Executable file
0
examples/math_sign.fun
Normal file → Executable file
0
examples/math_sign.fun
Normal file → Executable file
0
examples/math_sin.fun
Normal file → Executable file
0
examples/math_sin.fun
Normal file → Executable file
0
examples/math_sqrt.fun
Normal file → Executable file
0
examples/math_sqrt.fun
Normal file → Executable file
0
examples/math_tan.fun
Normal file → Executable file
0
examples/math_tan.fun
Normal file → Executable file
0
examples/math_trunc.fun
Normal file → Executable file
0
examples/math_trunc.fun
Normal file → Executable file
42
examples/notcurses_clock.fun
Executable file
42
examples/notcurses_clock.fun
Executable file
|
|
@ -0,0 +1,42 @@
|
||||||
|
#!/usr/bin/env fun
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://fun-lang.xyz/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the Apache-2.0 license.
|
||||||
|
* https://opensource.org/license/apache-2-0
|
||||||
|
*
|
||||||
|
* Added: 2026-01-03
|
||||||
|
*/
|
||||||
|
|
||||||
|
// BROKEN!!!
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Notcurses clock demo: updates time every 200ms; any key to finish.
|
||||||
|
*/
|
||||||
|
|
||||||
|
include <ui/notcurses.fun>
|
||||||
|
|
||||||
|
n = Notcurses()
|
||||||
|
if n.init() == 0
|
||||||
|
print("Notcurses not available. Rebuild with -DFUN_WITH_NOTCURSES=ON.")
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
n.clear()
|
||||||
|
n.draw_text(1, 0, "Clock demo (press any key)")
|
||||||
|
|
||||||
|
var done = 0
|
||||||
|
while done == 0
|
||||||
|
// current time string
|
||||||
|
var ms = time_now_ms()
|
||||||
|
var t = date_format("%Y-%m-%d %H:%M:%S", ms)
|
||||||
|
n.draw_text(3, 0, "Time: " + t)
|
||||||
|
|
||||||
|
// poll without blocking too long
|
||||||
|
var k = n.getch(200)
|
||||||
|
if k != -1
|
||||||
|
done = 1
|
||||||
|
|
||||||
|
n.shutdown()
|
||||||
46
examples/notcurses_hello.fun
Executable file
46
examples/notcurses_hello.fun
Executable file
|
|
@ -0,0 +1,46 @@
|
||||||
|
#!/usr/bin/env fun
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://fun-lang.xyz/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the Apache-2.0 license.
|
||||||
|
* https://opensource.org/license/apache-2-0
|
||||||
|
*
|
||||||
|
* Added: 2026-01-03
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Minimal Notcurses hello example.
|
||||||
|
*/
|
||||||
|
|
||||||
|
include <ui/notcurses.fun>
|
||||||
|
|
||||||
|
n = Notcurses()
|
||||||
|
|
||||||
|
if n.init() == 0
|
||||||
|
print("Notcurses not available. Rebuild with -DFUN_WITH_NOTCURSES=ON.")
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
n.clear()
|
||||||
|
n.draw_text(2, 0, "Fun + Notcurses")
|
||||||
|
n.draw_text(4, 0, "Press any key to exit...")
|
||||||
|
|
||||||
|
// blocking until key
|
||||||
|
_ = n.getch(0)
|
||||||
|
n.shutdown()
|
||||||
|
|
||||||
|
/* Possible output:
|
||||||
|
A TUI.
|
||||||
|
|
||||||
|
After exit:
|
||||||
|
3 renders, 991,14µs (223,57µs min, 330,38µs avg, 531,91µs max)
|
||||||
|
3 rasters, 320,76µs (106,45µs min, 106,92µs avg, 107,72µs max)
|
||||||
|
3 writes, 198,26µs (62,54µs min, 66,09µs avg, 69,94µs max)
|
||||||
|
59B (0B min, 19B avg, 30B max) 1 input Ghpa: 0
|
||||||
|
0 failed renders, 0 failed rasters, 0 refreshes, 0 input errors
|
||||||
|
RGB emits:elides: def 1:38 fg 0:0 bg 0:0
|
||||||
|
Cell emits:elides: 39:74805 (99,95%) 97,44% 0,00% 0,00%
|
||||||
|
Bmap emits:elides: 0:0 (0,00%) 0B (0,00%) SuM: 0 (0,00%)
|
||||||
|
*/
|
||||||
42
examples/notcurses_typing.fun
Executable file
42
examples/notcurses_typing.fun
Executable file
|
|
@ -0,0 +1,42 @@
|
||||||
|
#!/usr/bin/env fun
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://fun-lang.xyz/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the Apache-2.0 license.
|
||||||
|
* https://opensource.org/license/apache-2-0
|
||||||
|
*
|
||||||
|
* Added: 2026-01-03
|
||||||
|
*/
|
||||||
|
|
||||||
|
// BROKEN!!!
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Notcurses typing demo: shows last key code; ESC quits.
|
||||||
|
*/
|
||||||
|
|
||||||
|
include <ui/notcurses.fun>
|
||||||
|
|
||||||
|
n = Notcurses()
|
||||||
|
if n.init() == 0
|
||||||
|
print("Notcurses not available. Rebuild with -DFUN_WITH_NOTCURSES=ON.")
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
n.clear()
|
||||||
|
n.draw_text(1, 0, "Typing demo (ESC to quit)")
|
||||||
|
|
||||||
|
var running = 1
|
||||||
|
var last = -1
|
||||||
|
while running == 1
|
||||||
|
// poll every 100ms
|
||||||
|
var k = n.getch(100)
|
||||||
|
if k != -1
|
||||||
|
last = k
|
||||||
|
if k == 27 // ESC
|
||||||
|
running = 0
|
||||||
|
// draw last key code
|
||||||
|
n.draw_text(3, 0, "Last key code: " + to_string(last))
|
||||||
|
|
||||||
|
n.shutdown()
|
||||||
0
examples/ripemd160_example.fun
Normal file → Executable file
0
examples/ripemd160_example.fun
Normal file → Executable file
42
lib/ui/notcurses.fun
Normal file
42
lib/ui/notcurses.fun
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
#!/usr/bin/env fun
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://fun-lang.xyz/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the Apache-2.0 license.
|
||||||
|
* https://opensource.org/license/apache-2-0
|
||||||
|
*
|
||||||
|
* Added: 2026-01-03
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Notcurses stdlib abstraction wrapping VM NC_* builtins.
|
||||||
|
// Provides a tiny OO-style helper around nc_init/nc_clear/nc_draw_text/nc_getch/nc_shutdown.
|
||||||
|
// If Fun was built without Notcurses support, init() returns 0 and methods are no-ops.
|
||||||
|
|
||||||
|
class Notcurses()
|
||||||
|
// Initialize Notcurses core. Returns 1 on success, 0 on failure.
|
||||||
|
fun init(this)
|
||||||
|
return nc_init()
|
||||||
|
|
||||||
|
// Clear the screen and render. Returns 0 on success, -1 on error.
|
||||||
|
fun clear(this)
|
||||||
|
return nc_clear()
|
||||||
|
|
||||||
|
// Draw text at y, x and render. Returns 0 on success, -1 on error.
|
||||||
|
fun draw_text(this, y, x, text)
|
||||||
|
// allow flexible types
|
||||||
|
return nc_draw_text(to_number(y), to_number(x), to_string(text))
|
||||||
|
|
||||||
|
// Get a Unicode codepoint (int). timeout_ms<=0 blocks; >0 returns -1 if no key ready.
|
||||||
|
fun getch(this, timeout_ms)
|
||||||
|
if timeout_ms == nil
|
||||||
|
timeout_ms = 0
|
||||||
|
return nc_getch(to_number(timeout_ms))
|
||||||
|
|
||||||
|
// Shut down Notcurses, restoring the terminal.
|
||||||
|
fun shutdown(this)
|
||||||
|
return nc_shutdown()
|
||||||
|
|
||||||
|
// (banner helper removed temporarily due to parser limitations)
|
||||||
|
|
@ -261,7 +261,14 @@ typedef enum {
|
||||||
|
|
||||||
// Min/Max variants (float-aware, C99 semantics)
|
// Min/Max variants (float-aware, C99 semantics)
|
||||||
OP_FMIN, // pops b, a (int/float); pushes fmin(a,b) (NaN handling per C99)
|
OP_FMIN, // pops b, a (int/float); pushes fmin(a,b) (NaN handling per C99)
|
||||||
OP_FMAX // pops b, a (int/float); pushes fmax(a,b) (NaN handling per C99)
|
OP_FMAX, // pops b, a (int/float); pushes fmax(a,b) (NaN handling per C99)
|
||||||
|
|
||||||
|
/* Notcurses TUI (optional) */
|
||||||
|
OP_NC_INIT, // initializes Notcurses; returns 1 on success, 0 on failure
|
||||||
|
OP_NC_SHUTDOWN, // shuts down Notcurses; returns 0
|
||||||
|
OP_NC_CLEAR, // clears screen/plane; returns 0
|
||||||
|
OP_NC_DRAW_TEXT, // pops text, x, y; draws; returns 0
|
||||||
|
OP_NC_GETCH // pops timeout_ms; returns codepoint or -1 on timeout/error
|
||||||
} OpCode;
|
} OpCode;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
||||||
44
src/parser.c
44
src/parser.c
|
|
@ -1195,6 +1195,50 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
||||||
free(name);
|
free(name);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
/* Notcurses builtins (optional) */
|
||||||
|
if (strcmp(name, "nc_init") == 0) {
|
||||||
|
(*pos)++; /* '(' */
|
||||||
|
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "nc_init expects ()"); free(name); return 0; }
|
||||||
|
bytecode_add_instruction(bc, OP_NC_INIT, 0);
|
||||||
|
free(name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "nc_shutdown") == 0) {
|
||||||
|
(*pos)++; /* '(' */
|
||||||
|
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "nc_shutdown expects ()"); free(name); return 0; }
|
||||||
|
bytecode_add_instruction(bc, OP_NC_SHUTDOWN, 0);
|
||||||
|
free(name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "nc_clear") == 0) {
|
||||||
|
(*pos)++; /* '(' */
|
||||||
|
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "nc_clear expects ()"); free(name); return 0; }
|
||||||
|
bytecode_add_instruction(bc, OP_NC_CLEAR, 0);
|
||||||
|
free(name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "nc_draw_text") == 0) {
|
||||||
|
(*pos)++; /* '(' */
|
||||||
|
/* (y, x, text) -> push y, x, text, then opcode will pop text,x,y */
|
||||||
|
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_draw_text expects (y, x, text)"); free(name); return 0; }
|
||||||
|
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "nc_draw_text expects (y, x, text)"); free(name); return 0; }
|
||||||
|
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_draw_text expects (y, x, text)"); free(name); return 0; }
|
||||||
|
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "nc_draw_text expects (y, x, text)"); free(name); return 0; }
|
||||||
|
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_draw_text expects (y, x, text)"); free(name); return 0; }
|
||||||
|
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "Expected ')' after nc_draw_text args"); free(name); return 0; }
|
||||||
|
bytecode_add_instruction(bc, OP_NC_DRAW_TEXT, 0);
|
||||||
|
free(name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "nc_getch") == 0) {
|
||||||
|
(*pos)++; /* '(' */
|
||||||
|
/* (timeout_ms) */
|
||||||
|
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_getch expects (timeout_ms)"); free(name); return 0; }
|
||||||
|
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "Expected ')' after nc_getch arg"); free(name); return 0; }
|
||||||
|
bytecode_add_instruction(bc, OP_NC_GETCH, 0);
|
||||||
|
free(name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
if (strcmp(name, "pcsc_release") == 0) {
|
if (strcmp(name, "pcsc_release") == 0) {
|
||||||
(*pos)++; /* '(' */
|
(*pos)++; /* '(' */
|
||||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "pcsc_release expects 1 argument (ctx)"); free(name); return 0; }
|
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "pcsc_release expects 1 argument (ctx)"); free(name); return 0; }
|
||||||
|
|
|
||||||
21
src/vm.c
21
src/vm.c
|
|
@ -7,12 +7,15 @@
|
||||||
* https://opensource.org/license/apache-2-0
|
* https://opensource.org/license/apache-2-0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Ensure POSIX prototypes (nanosleep, clock_gettime, localtime_r, etc.) are available
|
/* Ensure POSIX/XSI prototypes (nanosleep, wcwidth, etc.) are available
|
||||||
* before any system headers are included by amalgamated .c files. */
|
* before any system headers are included by amalgamated .c files. */
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#ifndef _POSIX_C_SOURCE
|
#ifndef _POSIX_C_SOURCE
|
||||||
#define _POSIX_C_SOURCE 200809L
|
#define _POSIX_C_SOURCE 200809L
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef _XOPEN_SOURCE
|
||||||
|
#define _XOPEN_SOURCE 700
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
@ -45,6 +48,13 @@
|
||||||
#include "value.h"
|
#include "value.h"
|
||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
|
|
||||||
|
/* Shared Notcurses state (optional) */
|
||||||
|
#ifdef FUN_WITH_NOTCURSES
|
||||||
|
# include <wchar.h> /* ensure wcwidth/wcswidth prototypes present before notcurses.h on some systems */
|
||||||
|
# include <notcurses/notcurses.h>
|
||||||
|
#endif
|
||||||
|
#include "vm/notcurses/common.h"
|
||||||
|
|
||||||
// Optional by extensions commonly used code. #ifdef's are in each single file.
|
// Optional by extensions commonly used code. #ifdef's are in each single file.
|
||||||
#include "external/curl.c"
|
#include "external/curl.c"
|
||||||
#include "external/ini.c"
|
#include "external/ini.c"
|
||||||
|
|
@ -781,6 +791,15 @@ void vm_run(VM *vm, Bytecode *entry) {
|
||||||
#include "vm/tk/bind.c"
|
#include "vm/tk/bind.c"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Notcurses TUI ops (optional) */
|
||||||
|
#ifdef FUN_WITH_NOTCURSES
|
||||||
|
#include "vm/notcurses/init.c"
|
||||||
|
#include "vm/notcurses/shutdown.c"
|
||||||
|
#include "vm/notcurses/clear.c"
|
||||||
|
#include "vm/notcurses/draw_text.c"
|
||||||
|
#include "vm/notcurses/getch.c"
|
||||||
|
#endif
|
||||||
|
|
||||||
/* SQLite ops */
|
/* SQLite ops */
|
||||||
#ifdef FUN_WITH_SQLITE
|
#ifdef FUN_WITH_SQLITE
|
||||||
#include "vm/sqlite/open.c"
|
#include "vm/sqlite/open.c"
|
||||||
|
|
|
||||||
6
src/vm.h
6
src/vm.h
|
|
@ -53,7 +53,9 @@ static const char *opcode_names[] = {
|
||||||
"TK_BIND",
|
"TK_BIND",
|
||||||
"SERIAL_OPEN","SERIAL_CONFIG","SERIAL_SEND","SERIAL_RECV","SERIAL_CLOSE",
|
"SERIAL_OPEN","SERIAL_CONFIG","SERIAL_SEND","SERIAL_RECV","SERIAL_CLOSE",
|
||||||
"TK_EVAL","TK_RESULT","TK_LOOP","TK_WM_TITLE","TK_LABEL","TK_BUTTON","TK_PACK",
|
"TK_EVAL","TK_RESULT","TK_LOOP","TK_WM_TITLE","TK_LABEL","TK_BUTTON","TK_PACK",
|
||||||
"TRY_PUSH","TRY_POP","THROW"
|
"TRY_PUSH","TRY_POP","THROW",
|
||||||
|
/* Notcurses TUI (optional) */
|
||||||
|
"NC_INIT","NC_SHUTDOWN","NC_CLEAR","NC_DRAW_TEXT","NC_GETCH"
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -132,7 +134,7 @@ void vm_debug_request_finish(VM *vm);
|
||||||
void vm_debug_request_continue(VM *vm);
|
void vm_debug_request_continue(VM *vm);
|
||||||
|
|
||||||
static inline int opcode_is_valid(int op) {
|
static inline int opcode_is_valid(int op) {
|
||||||
return op >= OP_NOP && op <= OP_THROW; // all current opcodes
|
return op >= OP_NOP && op <= OP_NC_GETCH; // all current opcodes (including optional NC_*)
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
27
src/vm/notcurses/clear.c
Normal file
27
src/vm/notcurses/clear.c
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://fun-lang.xyz/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the Apache-2.0 license.
|
||||||
|
* https://opensource.org/license/apache-2-0
|
||||||
|
*
|
||||||
|
* Added: 2026-01-03
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* NC_CLEAR */
|
||||||
|
case OP_NC_CLEAR: {
|
||||||
|
#ifdef FUN_WITH_NOTCURSES
|
||||||
|
if (_fun_nc && _fun_nc_std) {
|
||||||
|
ncplane_erase(_fun_nc_std);
|
||||||
|
notcurses_render(_fun_nc);
|
||||||
|
push_value(vm, make_int(0));
|
||||||
|
} else {
|
||||||
|
push_value(vm, make_int(-1));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void)_fun_nc; (void)_fun_nc_std;
|
||||||
|
push_value(vm, make_int(-1));
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
23
src/vm/notcurses/common.h
Normal file
23
src/vm/notcurses/common.h
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
/**
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://fun-lang.xyz/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the Apache-2.0 license.
|
||||||
|
* https://opensource.org/license/apache-2-0
|
||||||
|
*
|
||||||
|
* Added: 2026-01-03
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Common helpers/state for Notcurses TUI ops */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "value.h"
|
||||||
|
|
||||||
|
/* Forward declarations to avoid depending on the notcurses headers unless enabled */
|
||||||
|
struct notcurses;
|
||||||
|
struct ncplane;
|
||||||
|
|
||||||
|
/* Shared static state within vm.c translation unit (header is included into vm.c at file scope) */
|
||||||
|
static struct notcurses* _fun_nc = NULL;
|
||||||
|
static struct ncplane* _fun_nc_std = NULL;
|
||||||
39
src/vm/notcurses/draw_text.c
Normal file
39
src/vm/notcurses/draw_text.c
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
/**
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://fun-lang.xyz/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the Apache-2.0 license.
|
||||||
|
* https://opensource.org/license/apache-2-0
|
||||||
|
*
|
||||||
|
* Added: 2026-01-03
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* NC_DRAW_TEXT */
|
||||||
|
case OP_NC_DRAW_TEXT: {
|
||||||
|
Value textv = pop_value(vm);
|
||||||
|
Value xv = pop_value(vm);
|
||||||
|
Value yv = pop_value(vm);
|
||||||
|
int x = (xv.type == VAL_INT ? (int)xv.i : (xv.type == VAL_FLOAT ? (int)xv.d : 0));
|
||||||
|
int y = (yv.type == VAL_INT ? (int)yv.i : (yv.type == VAL_FLOAT ? (int)yv.d : 0));
|
||||||
|
char *text = value_to_string_alloc(&textv);
|
||||||
|
free_value(textv);
|
||||||
|
free_value(xv);
|
||||||
|
free_value(yv);
|
||||||
|
#ifdef FUN_WITH_NOTCURSES
|
||||||
|
if (_fun_nc && _fun_nc_std && text) {
|
||||||
|
ncplane_putstr_yx(_fun_nc_std, y, x, text);
|
||||||
|
notcurses_render(_fun_nc);
|
||||||
|
free(text);
|
||||||
|
push_value(vm, make_int(0));
|
||||||
|
} else {
|
||||||
|
if (text) free(text);
|
||||||
|
push_value(vm, make_int(-1));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void)_fun_nc; (void)_fun_nc_std;
|
||||||
|
if (text) free(text);
|
||||||
|
push_value(vm, make_int(-1));
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
43
src/vm/notcurses/getch.c
Normal file
43
src/vm/notcurses/getch.c
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
/**
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://fun-lang.xyz/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the Apache-2.0 license.
|
||||||
|
* https://opensource.org/license/apache-2-0
|
||||||
|
*
|
||||||
|
* Added: 2026-01-03
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* NC_GETCH */
|
||||||
|
case OP_NC_GETCH: {
|
||||||
|
Value to_ms_v = pop_value(vm);
|
||||||
|
int timeout_ms = (to_ms_v.type == VAL_INT ? (int)to_ms_v.i : (to_ms_v.type == VAL_FLOAT ? (int)to_ms_v.d : 0));
|
||||||
|
free_value(to_ms_v);
|
||||||
|
#ifdef FUN_WITH_NOTCURSES
|
||||||
|
if (_fun_nc) {
|
||||||
|
/* For simplicity, use blocking get for now when timeout<=0 */
|
||||||
|
if (timeout_ms <= 0) {
|
||||||
|
ncinput ni;
|
||||||
|
uint32_t id = notcurses_get_blocking(_fun_nc, &ni);
|
||||||
|
push_value(vm, make_int((int)id));
|
||||||
|
} else {
|
||||||
|
/* Polling approximation: render then nonblocking get once */
|
||||||
|
ncinput ni;
|
||||||
|
uint32_t id = notcurses_get_nblock(_fun_nc, &ni);
|
||||||
|
if (id == 0) {
|
||||||
|
/* no input available now: return -1 to indicate timeout */
|
||||||
|
push_value(vm, make_int(-1));
|
||||||
|
} else {
|
||||||
|
push_value(vm, make_int((int)id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
push_value(vm, make_int(-1));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void)_fun_nc; (void)_fun_nc_std; (void)timeout_ms;
|
||||||
|
push_value(vm, make_int(-1));
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
27
src/vm/notcurses/init.c
Normal file
27
src/vm/notcurses/init.c
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://fun-lang.xyz/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the Apache-2.0 license.
|
||||||
|
* https://opensource.org/license/apache-2-0
|
||||||
|
*
|
||||||
|
* Added: 2026-01-03
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* NC_INIT */
|
||||||
|
case OP_NC_INIT: {
|
||||||
|
#ifdef FUN_WITH_NOTCURSES
|
||||||
|
if (_fun_nc) { push_value(vm, make_int(1)); break; }
|
||||||
|
struct notcurses_options opts = {0};
|
||||||
|
_fun_nc = notcurses_core_init(&opts, NULL);
|
||||||
|
if (!_fun_nc) { push_value(vm, make_int(0)); break; }
|
||||||
|
_fun_nc_std = notcurses_stdplane(_fun_nc);
|
||||||
|
push_value(vm, make_int(1));
|
||||||
|
#else
|
||||||
|
(void)_fun_nc; (void)_fun_nc_std;
|
||||||
|
fprintf(stderr, "Notcurses support disabled at build time. Reconfigure with -DFUN_WITH_NOTCURSES=ON.\n");
|
||||||
|
push_value(vm, make_int(0));
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
25
src/vm/notcurses/shutdown.c
Normal file
25
src/vm/notcurses/shutdown.c
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
/**
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://fun-lang.xyz/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the Apache-2.0 license.
|
||||||
|
* https://opensource.org/license/apache-2-0
|
||||||
|
*
|
||||||
|
* Added: 2026-01-03
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* NC_SHUTDOWN */
|
||||||
|
case OP_NC_SHUTDOWN: {
|
||||||
|
#ifdef FUN_WITH_NOTCURSES
|
||||||
|
if (_fun_nc) {
|
||||||
|
notcurses_stop(_fun_nc);
|
||||||
|
_fun_nc = NULL;
|
||||||
|
_fun_nc_std = NULL;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void)_fun_nc; (void)_fun_nc_std;
|
||||||
|
#endif
|
||||||
|
push_value(vm, make_int(0));
|
||||||
|
break;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue