(c-set-style "cc")
options for the networking
- sdl_net
(nahh.. couldn’t use simple/dummy AF_INET socket)
- https://nng.nanomsg.org/man/v1.3.0/nng_recv.3.html
- https://github.com/nanomsg/nng/blob/master/demo/reqrep/reqrep.c
huh! it reads everything into a buffer, don’t have to deal with that myself
char * buf = NULL;
size_t sz;
uint64_t val;
if ((rv = nng_recv(sock, &buf, &sz, NNG_FLAG_ALLOC)) != 0) {
fatal("nng_recv", rv);
}
NNG_FLAG_ALLOC
If this flag is present, then a “zero-copy” mode is used. In this case the caller must set the value of data to the location of another pointer (of type void *), and the sizep pointer must be set to a location to receive the size of the message body. The function will then allocate a message buffer (as if by nng_alloc()), fill it with the message body, and store it at the address referenced by data, and update the size referenced by sizep. The caller is responsible for disposing of the received buffer either by the nng_free() function or passing the message (also with the NNG_FLAG_ALLOC flag) in a call to nng_send().
357aaa1 commit broke the libc_s7.c
generation
(last working commit 7fb147f)
but how does all this work?
;; cload.scm (define *cload-c-compiler* (if (provided? 'gcc) "gcc" (if (provided? 'clang) "clang" "cc")))
example dumb repl
// s7.c
static void dumb_repl(s7_scheme *sc)
{
while (true)
{
char buffer[512];
fprintf(stdout, "\n> ");
if (!fgets(buffer, 512, stdin)) break; /* error or ctrl-D */
if (((buffer[0] != '\n') || (strlen(buffer) > 1)))
{
char response[1024];
snprintf(response, 1024, "(write %s)", buffer);
s7_eval_c_string(sc, response);
}
}
fprintf(stdout, "\n");
if (ferror(stdin))
fprintf(stderr, "read error on stdin\n");
}
- formatting
#include <sstream> #include <iostream> std::ostringstream stream; stream << "Foo" << "bar" << std::endl; std::string str = stream.str();
- returning strings
// returning char* char* returnChar(){ char* str = "blahblah"; return str; } // BAD! I return a dangling pointer char* returnC_str(){ std::string myString = "nice"; return myString.c_str(); } // BAD! again, dangling pointer char* returnHeapChar(){ int lenght = 10; char * new_string = new char[length + 1]; // +1 for the terminating the string with 0 std::string someString = "nice"; std::strcpy(new:string, someString.c_str()); } // OK! but I have to run delete myself std::string returnStr(){ // all good, go wild }
to use for the tcp server
Usage:
server.listen(1234, [](const char*) {
// doing something with data?
} )
https://stackoverflow.com/questions/20353210/usage-and-syntax-of-stdfunction
ninja -C build
./build/s7-imgui
(run-scheme "netcat localhost 1234")
netcat localhost 1234
(display ;;as
"hi" ;; 2nd line
)
1
s7_pointer s7_read(s7_scheme *sc, s7_pointer port)
{
if (is_input_port(port))
{
s7_pointer old_let;
declare_jump_info();
old_let = sc->curlet;
sc->curlet = sc->nil;
push_input_port(sc, port);
store_jump_info(sc);
set_jump_info(sc, READ_SET_JUMP);
if (jump_loc != NO_JUMP)
{
if (jump_loc != ERROR_JUMP)
eval(sc, sc->cur_op);
}
else
{
push_stack_no_let_no_code(sc, OP_BARRIER, port);
push_stack_direct(sc, OP_EVAL_DONE);
eval(sc, OP_READ_INTERNAL);
if (sc->tok == TOKEN_EOF)
sc->value = eof_object;
if ((sc->cur_op == OP_EVAL_DONE) &&
(stack_op(sc->stack, s7_stack_top(sc) - 1) == OP_BARRIER))
pop_stack(sc);
}
pop_input_port(sc);
sc->curlet = old_let;
restore_jump_info(sc);
return(sc->value);
}
return(simple_wrong_type_argument_with_type(sc, sc->read_symbol, port, an_input_port_string));
}
- upon socket data, append to a string stream
- try to “read” the current stream string
- if it succeeds, perfect, just eval the string & emptry the stream
- if not, just wait until the next chunk of data. store the reader error
upon new data, if there is pending error, but plenty of time (define ?) has passed, ignore the previous error and start reading from scratch. probably also send something to the connected client?
- mimic the processing nomenclature
(define (setup) .. )
(define (draw) .. )
- ffi for the imgui functionality
- bind them to s7
meson wrap install gtest
(button-lock-mode 1)
(defun button/compile ()
"Shout when clicked"
(interactive)
(message "compiling")
(comint-send-string "*s7-imgui*" "ninja -C build\r")
)
(defun button/run ()
"Shout when clicked"
(interactive)
(message "running")
(comint-send-string "*s7-imgui*" "./build/s7-imgui\r")
)
(defun button/stop ()
"Shout when clicked"
(interactive)
(message "stop")
(interrupt-process "*s7-imgui*" comint-ptyp)
)
(defun button/test ()
"Shout when clicked"
(interactive)
(message "stop")
(comint-send-string "*s7-imgui*" "./build/test/gtest-all\r")
)
(button-lock-set-button (regexp-quote ">compile")
'button/compile
:face 'link )
(button-lock-set-button (regexp-quote ">run")
'button/run
:face 'link )
(button-lock-set-button (regexp-quote ">stop")
'button/stop
:face 'link )
(button-lock-set-button (regexp-quote ">test")
'button/test
:face 'link )
- >compile
- >run
- >stop
- >test
(define (inc! x)
(format #t "increasing x: ~A ~A" x (symbol->value x))
((outlet (curlet)) x)
;; (set! ((outlet (curlet)) x) 2)
;; (set! x (+ 1 x))
;; (format #t "x is now ~A" x)
)
(define x 0)
(inc! x)
(inc! 'x)
(symbol? 'x)
(symbol->value 'x)
(inc! x)
x ;; x is still 0
check
> (set! (lt 'a) 12)
in the documentation
- exec-in-script
is true for shell, it doesn’t ask anything
(defun eir-repl-start (repl-buffer-regexp fun-repl-start &optional exec-in-script)
in
eir-eval-in-ielm
add a last argumentt
to theeir-eval-in-repl-lisp
call
in linux there is plenty of magic
- generates a c file
- compiles it
- dynamically loads it
(let ((e (box 1)))
(test (box? e) #t)
(test (unbox e) 1)
(test (e 'value) 1)
(set-box! e 2)
(test (unbox e) 2)
(test (string=? (object->string e) "(inlet 'type box-type 'value 2)") #t))
- https://github.com/actonDev/SDL/commit/2da3b23c429c923cc38763d47626aa473f8013cf
# wrong sdl2 = library('sdl2', link_args: core_ldflags, # ... ) # correct sdl2_dep = declare_dependency(link_with : sdl2, include_directories : core_inc, link_args : core_ldflags) # link arguments are in the dependency, not in the library! ugh
- https://github.com/actonDev/SDL/commit/3e11ce100ef74d11ef57f7d1ee837d67c7e2ab2c
added the following defines on windows
- SDL_VIDEO_OPENGL
- SDL_VIDEO_OPENGL_WGL
fixed in https://github.com/actonDev/SDL/commit/3e11ce100ef74d11ef57f7d1ee837d67c7e2ab2c in CMakeLists
if(SDL_VIDEO) if(VIDEO_OPENGL) set(SDL_VIDEO_OPENGL 1) set(SDL_VIDEO_OPENGL_WGL 1) set(SDL_VIDEO_RENDER_OGL 1) set(HAVE_VIDEO_OPENGL TRUE) endif() endif()
see also subprojects/sdl2/premake/VisualC/VS2008/SDL_config_premake.h
#define SDL_VIDEO_DRIVER_WINDOWS 1 #define SDL_VIDEO_RENDER_OGL 1 #endif #ifndef SDL_VIDEO_RENDER_OGL #define SDL_VIDEO_RENDER_OGL 1 #endif #ifndef SDL_VIDEO_OPENGL #define SDL_VIDEO_OPENGL 1 #endif #ifndef SDL_VIDEO_OPENGL_WGL #define SDL_VIDEO_OPENGL_WGL 1 #endif
#mesondefine SDL_VIDEO_RENDER_D3D11 // it's on in premake #mesondefine SDL_VIDEO_RENDER_OGL // on premake
LINK : fatal error LNK1181: cannot open input file ‘..\subprojects\imgui-1.76\imgui.lib’
Solved by adding this in the project
project('s7-imgui', 'cpp', 'c',
default_options: [
'default_library=static', # this fixed it
]
)
the default_library=static
did the trick
Windows wants a WinMain function
// https://stackoverflow.com/a/58819006/8720686
#ifdef _WIN32
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
return main(__argc, __argv);
}
#endif
But, instead of that, if we link against the sdl2_main
library, this is taken care of
https://stackoverflow.com/questions/5862097/sdl-opengl-screenshot-is-black#5867170
[3/16] Linking target examples/example_imgui.exe FAILED: examples/example_imgui.exe c++ @examples/example_imgui.exe.rsp c++.exe: error: winmm.lib: No such file or directory c++.exe: error: version.lib: No such file or directory c++.exe: error: imm32.lib: No such file or directory c++.exe: error: opengl32.lib: No such file or directory c++.exe: error: iphlpapi.lib: No such file or directory
aaah, aha! it was using gcc. not msvc
- wipe
- setup build –backend vs
added a check in meson.build
meson setup build-mingw
ninja -C build-mingw
wrote some ffi utils, creating c-objects and sharing between c<->scheme
TODO
- slider
- layout (same line
check out the layout. how is it?
ImGui::SameLine();
create macro: m-horizontal
- first element just gets drawn
- subsequent elements have the
same-line
call prepended
something like
(defmacro* imgui/m-window (:title title
:open open
) . body
`(begin
(imgui/begin title open?)
,body
(imgui/end)
)
)
ok, actual solution don’t know if I can use define-macro* and pass the rest . body
(define-macro (imgui/m-begin args . body)
`(begin
(imgui/begin ,@args)
,@body
(imgui/end)))
(comment
(macroexpand (imgui/m-begin ("test" 'the-c-object)
(imgui/text "hi")
(imgui/text "scheme s7")
))
;; =>
(begin (imgui/begin "test" 'the-c-object) (imgui/text "hi") (imgui/text "scheme s7") (imgui/end))
)
yup, I can use it
(define-macro* (imgui/m-begin2 (title "") (*open #t) :rest body)
(if (eq? #t *open)
`(begin
(imgui/begin ,title)
,@body
(imgui/end))
`(begin
(imgui/begin ,title ,*open)
,@body
(imgui/end))))
(comment
(macroexpand (imgui/m-begin2 :title "always open"
(imgui/text "hi")
(imgui/text "scheme s7")
))
;; =>
(begin (imgui/begin "always open" (imgui/text "hi")) (imgui/text "scheme s7") (imgui/end))
(macroexpand (imgui/m-begin2 :title "always open"
:*open 'the-c-object
(imgui/text "hi")
(imgui/text "scheme s7")
))
;; =>
(begin (imgui/begin "always open" 'the-c-object) (imgui/text "hi") (imgui/text "scheme s7") (imgui/end))
)
I prefer the simpler, first version
I can crash the application when sending quickly from the repl the (draw)
definition. expected :)
- use a mutex around the main loop
delete void* isn’t very smart.. fix this :) but.. why wasn’t the compiler complaining??
oor.. could be that for internal types it’s ok.hmm..
S7 doesn’t have a precise garbage collector
updated the test, indeed the free
gets called
- circle
- line
- text
- triangle
- ..etc
see
make a call style like SDL2_gfxPrimitives.h
- circleColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Uint32 color);
- filledCircleColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 r, Uint32 color);
or like https://learn.adafruit.com/adafruit-gfx-graphics-library/graphics-primitives
ImVec2 p = ImGui::GetCursorScreenPos();
ImDrawList *draw_list = ImGui::GetWindowDrawList();
float cx = 0;
float cy = 0;
float r = 50;
float thickness = 2;
float line_height = ImGui::GetTextLineHeight();
ImGuiStyle &style = ImGui::GetStyle();
// style.ItemInnerSpacing.y
ImGui::Dummy(
ImVec2(2 * r + style.ItemInnerSpacing.x,
2 * r + style.ItemInnerSpacing.y));
int segments = 16;
ImU32 col32line = ImGui::GetColorU32(ImGuiCol_SliderGrabActive);
draw_list->AddCircle(ImVec2(p.x + cx + r, p.y + cy + r), r, col32line, 32,
thickness);
meson configure build -Dcpp_std=c++17 (it has the cool filesystem/path libs)
https://github.com/mlabbe/nativefiledialog
ItemGroup>
<ClInclude Include="..\..\src\common.h" />
<ClInclude Include="..\..\src\include\nfd.h" />
<ClInclude Include="..\..\src\nfd_common.h" />
<ClInclude Include="..\..\src\simple_exec.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\src\nfd_common.c" />
<ClCompile Include="..\..\src\nfd_win.cpp" />
</ItemGroup>
$(OBJDIR)/nfd_common.o: ../../src/nfd_common.c
@echo $(notdir $<)
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
$(OBJDIR)/nfd_gtk.o: ../../src/nfd_gtk.c
@echo $(notdir $<)
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
https://stackoverflow.com/a/28734794
Done
Thanks for the bug report. This is actually a bug in the documentation – the array of names should be sorted alphabetically, but I forgot to mention that in s7.html.
sent to the mailing list
TEST(s7_environments, autoloads_bug) {
const char *autoloads[6] = {
// each pair of entries is entity name + file name
"aod.lib1", "aod/lib1.scm", //
"aod.lib2", "aod/lib2.scm", //
"aod.extra.foo", "aod/extra/foo.scm",
};
s7_scheme* sc1 = s7_init();
s7_autoload_set_names(sc1, autoloads, 3);
char* sexp = "(begin "
"(require aod.lib1)"
"(require aod.lib2)"
"1)";
// ok that works
ASSERT_EQ(1, s7_integer(s7_eval_c_string(sc1, sexp)));
s7_scheme* sc2 = s7_init();
s7_autoload_set_names(sc2, autoloads, 3);
char* sexp2 = "(begin "
"(require aod.extra.foo)"
"2)";
// THAT FAILS!!
ASSERT_EQ(2, s7_integer(s7_eval_c_string(sc2, sexp2)));
/**
* ----------
;require: no autoload info for aod.extra.foo
; (require aod.extra.foo)
; ((lambda (hook lst) (if (do ((p lst (cdr ...
; (2)
* -----------
*/
}
(define-macro* (aod/-require-bindings lib-exports as)
(format *stderr* "=> require bindings ~A as ~A\n" lib-exports as)
(let* ((as (or as lib-exports))
(prefix (symbol->string as)))
;; note: if I used a let inside the ` block, the (curlet) would refer to the let closure
;; thus, making any let assignments outside of the macro
`(apply varlet (curlet)
(map (lambda (binding)
(let ((new-binding (string->symbol
(string-append ,prefix "/" (symbol->string (car binding))))))
(format *stderr* "binding ~A\n" new-binding)
(cons new-binding
(cdr binding))))
,lib-exports))))
(define-macro* (aod/-require-as autoload-symbol (as #f))
(format *stderr* "=> require autoload symbol ~A as ~A\n" autoload-symbol as)
(let* ((as (or as autoload-symbol))
(prefix (symbol->string as)))
`(apply varlet (curlet)
(with-let (unlet)
(let ()
(require ,autoload-symbol)
(map (lambda (binding)
(let ((new-binding (string->symbol
(string-append ,prefix "/" (symbol->string (car binding))))))
(format *stderr* "binding ~A\n" new-binding)
(cons new-binding
(cdr binding))))
(curlet)))))))
eventually replaced from
(define-macro* (aod/require what (as #f))
(let ((prefix (symbol->string `,(or as what))))
;; (format *stderr* "aod/require ~A :as ~A\n" what prefix)
(if (defined? what)
;; bindings from c
(begin
;; (format *stderr* "requiring foreign bindings ~A as ~A\n" what prefix)
`(apply varlet (curlet)
(map (lambda (binding)
(let ((new-binding (string->symbol
(string-append ,prefix "/" (symbol->string (car binding))))))
(format *stderr* "binding ~A\n" new-binding)
(cons new-binding
(cdr binding))))
,what)))
;; normal autload, symbol "what" not present
(begin
;; (format *stderr* "requiring autoload symbol ~A as ~A, features ~A\n autoload ~A\n" what prefix *features* (*autoload* what))
(if (defined? (string->symbol (string-append prefix "/*features*")))
(format *stderr* "WARNING: ~A already required as ~A\n" what prefix)
`(apply varlet (curlet)
(with-let (unlet)
(let ()
;; note: we use load cause if we required already nothing will happen!
;; (*autoload* ',what) gives us the file name
(load (*autoload* ',what) (curlet))
(map (lambda (binding)
(let ((new-binding (string->symbol
(string-append ,prefix "/" (symbol->string (car binding))))))
(format *stderr* "binding ~A\n" new-binding)
(cons new-binding
(cdr binding))))
(curlet))))))))))
… new version
(define-macro* (aod/require what (as #f))
(let* ((prefix (symbol->string `,(or as what)))
(features-symbol (string->symbol (string-append prefix "/*features*"))))
(format *stderr* "prefix ~A features ~A\n" prefix (string->symbol (string-append prefix "/*features*")))
`(if (defined? ',features-symbol)
(format *stderr* "WARNING: ~A already required as ~A\n" ',what ,prefix)
;; else, doing the bidings:
(if (defined? ',what)
;; bindings from c
(apply varlet (curlet)
(map (lambda (binding)
(let ((binding-symbol (string->symbol
(string-append ,prefix "/" (symbol->string (car binding))))))
(cons binding-symbol
(cdr binding))))
,what))
;; normal autload, symbol "what" not present
(apply varlet (curlet)
(with-let (unlet)
(let ()
;; note: we use load cause if we required already nothing will happen!
;; (*autoload* ',what) gives us the file name
(load (*autoload* ',what) (curlet))
(map (lambda (binding)
(let ((binding-symbol (string->symbol
(string-append ,prefix "/" (symbol->string (car binding))))))
(cons binding-symbol
(cdr binding))))
(curlet)))))))))
created the src/scheme/aod/ns.scm for dealing with namespaces some 160 lines including plenty of comments. hope it will serve well.
See more in docs/s7.org
to quickly prototype with imgui. the window creation and imgui drawing is done from scheme, i’m not called by c.
example
(ns imgui-sratch)
(ns-require aod.c.imgui-sdl :as igsdl)
(ns-require aod.c.imgui :as ig)
(ns-require aod.imgui.macros :as igm)
(define *ctx* (igsdl/setup 400 400))
(define (draw)
(igsdl/prepare *ctx*)
;; your drawing logic here
;; eg
(igm/maximized
("imgui scratch")
(ig/text "hi devil")
(ig/text ""))
(igsdl/flush *ctx*)
)
;; drawing upon first running
(draw)
(comment
;; run destroy to kill the sdl window
(igsdl/destroy *ctx*)
)
error C2039: ‘filesystem’: is not a member of ‘std’ [W:\dev\actondev\s7-imgui\build\src\aod\7bbcc5b@@aod@sta.vcxproj]
‘s7_pointer s7_add_to_load_path(s7_scheme *,const char *)’: cannot convert argument 2 from ‘const std::filesystem::path::value_type *’ to ‘const char *’
aha. on linux path.c_str()
returns char*
, but on windows it’s w_char
.. ughh
Solution:
path.string().c_str()
meson configure build | grep std # it's indeed c++17
solution (for aod lib)
cpp_args = []
system = host_machine.system()
if system == 'windows'
cpp_args += '/std:c++17'
endif
aod_lib = library(
'aod',
# sources: aod_sources,
include_directories: include_directories('..'),
sources: aod_sources,
cpp_args: cpp_args,
# ...
)
or..
aod_lib = library(
'aod',
cpp_std: 'c++17',
# ..
)
uhmmm.
subprojects\s7_imgui\src\aod\meson.build:44: WARNING: Passed invalid keyword argument “cpp_std”.
meson -v
pip3 install meson --upgrade
# hm updated from 0.54.3 to 0.55
hm.. now it works..?
IMGUI_API void AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0);
In the first working demo of s7vst, the full each frame drawing was using ~11% cpu
but with
;; in aod/sxs.scm
(set! lines (memoize lines))
Cpu is around ~5%
hooray :D
Could I maybe cache certaing components? How I imagine it:
- define-imgui or something
- uses its own drawlist on each call
- invalidates the drawlist if there is a mouse event or something..
Saw a demo of using a shader with iPlug2 in oli larkin’s talk (creator of iPlug)
Wonder how easy (or not) would be to do something similar with s7
(define (make-file-contents *char)
;; dilambda is a quick way to define getters and setters
(dilambda
;; getter
(lambda ()
(*char))
(lambda (v)
(if (< buffer-size (length v))
(print "text bigger than buffer-size!")
(begin
(set! (*char) v))))))
(define *file-contents* (make-file-contents *buffer))
Removed it, but it’s nice to keep
(let ((build-res (with-temp-buffer
(call-process "ninja" nil t t "-C" "build"))))
(if (= 0 build-res)
(progn
;; (message "OK! running scheme")
(let ((default-directory (projectile-project-root)))
(run-scheme (concat (projectile-project-root) "build/repl " file))
))
(progn
;; (message "build failed")
(message (buffer-string))
)))
the “bug” that I stumbled in the video
// Edit a string of text
// - buf_size account for the zero-terminator, so a buf_size of 6 can hold "Hello" but not "Hello!".
// This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match
// Note that in std::string world, capacity() would omit 1 byte used by the zero-terminator.
// - When active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while the InputText is active has no effect.
// - If you want to use ImGui::InputText() with std::string, see misc/cpp/imgui_stdlib.h
// (FIXME: Rather confusing and messy function, among the worse part of our codebase, expecting to rewrite a V2 at some point.. Partly because we are
// doing UTF8 > U16 > UTF8 conversions on the go to easily interface with stb_textedit. Ideally should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188)
bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* callback_user_data)
{
When active, hold on a privately held copy of the text (and apply back to ‘buf’). So changing ‘buf’ while the InputText is active has no effect.
probably with emacs & org mode.. :)
<2020-08-03 Mon> let’s try to do that