[m-users.] Calling pass-by-value with C FFI with raylib

Sean Charles (emacstheviking) objitsu at gmail.com
Sat Jul 15 18:08:11 AEST 2023


Thanks all, Zoltan and Peter on point as usual.

I had a good long think about what I am doing, writing an IDE as there are not enough editors in the world already, one that is customised to the transpiler I have written, also written in Mercury, in fact the two may well become a single application but for now the remain separated. I can't decide wether to implement LSP in the transpiler as a command line option, making my editor more generic, or to use zeromq and just make them talk sooner. First world problem.

Anyway, I realised that, writing an editor, I am far more concerned with having all of the wonderful advantages that the Mercury compiler gives me rather than worry about performance over bit fiddling some colour values, thus I opted to do any bit shifting etc at the point of creation of my ui objects and then just as Peter recommended, pass a uint32 across the FFI boundary. So far this is working absolutely fine, so thank you all again for your guiding input.

The code I have ended up with so far is (so far) structured like this.

For the creation of new entities that can on-screen:

new_cursor(X, Y, Color) = cursor(X, Y, to_rgba(Color)).

new_text(Str, X, Y, Size, Color) =
    text(Str, X, Y, Size, to_rgba(Color)).


:- func to_rgba(color::in) = (uint32::out) is det.
to_rgba(C) = RGBA :-
    color_parts(C, R, G, B, A),
    RGBA = uint32.from_bytes_le(R, G, B, A).

These are then rendered on a case by case basis, I will add more state to them as the need arise, the raylib library has tweening functions for example, so I intend to make things be able to fade in, out, move about etc as you interact with the code. That's the vision!

% Drawing the graphics objects.

    % Cursor
ui_draw(cursor(X, Y, C), !IO) :-
    draw_rectangle(X, Y, 10, 20, C, !IO).

ui_draw(text(Str, X, Y, Size, C), !IO) :-
    draw_text(Str, X, Y, Size, C, !IO).


And my very simple UI main loop just checks to see if it show/not show the cursor, I will move that functionality into the UI objects I think, giving a reference to the current world state so each object can make it's own decisions on how to render, or not. As I say it is early days.

:- type world
    --->    world(
                wFrameTime  ::float,
                wTime       ::float,
                f1Hz        ::bool
            ).

:- pred run_loop(world::in, world::out, io::di, io::uo) is det.

run_loop(!W, !IO) :-
    window_should_close(X, !IO),
    (
        X = yes,
        trace_log("Stopped by user", [], !IO)
    ;
        X = no,

        % Update world state.
        get_frame_time(FT, !IO),
        get_time(T, !IO),
        !:W = !.W ^wFrameTime := FT,
        !:W = !.W ^wTime := T,
        % 1Hz timer flag
        Hz1 = float.round_to_int(wTime(!.W)) mod 2,
        (if Hz1 = 1 then Hz1On = yes else Hz1On = no),
        !:W = !.W ^f1Hz := Hz1On,

        begin_drawing(!IO),
            clear_background(color(raywhite), !IO),
            draw_fps(0, 0, !IO),

            ( if f1Hz(!.W) = yes then
                ui_draw(new_cursor(140, 100, color(purple)), !IO)
            else
                true
            ),

            ui_draw(new_text(
                string.format("frameTime: %f", [ f(wFrameTime(!.W))]),
                120, 130, 20, color(red)), !IO),

            ui_draw(new_text(
                string.format("time: %f", [ f(wTime(!.W))]),
                120, 150, 20, color(blue)), !IO),

        end_drawing(!IO),

            % File dropping
        dropped_files(Files, !IO),
        ( if list.is_not_empty(Files) then
            io.write_list(Files, "\n", io.print_line, !IO)
        else
            true
        ),
        run_loop(!W, !IO)
    ).

My influences for the project are the Squeak Morphic system (its inheritence and approach, I was a big Squeak addict once) and of course 'that demo' by Douglas Englebart in the DARPA video.

I have no idea where this is taking me but it sure is a pleasant journey and a whole lot of fun! Mercury just makes me know that the code I write is going to be robust and well thought out, I live in fear of dangling references with C/assembler which is why my C FFI code checks every single things passed to it and only calls the C function if it is satisfied with its inputs!

Thanks,
Sean.



> On 14 Jul 2023, at 08:51, Peter Wang <novalazy at gmail.com> wrote:
> 
> On Fri, 14 Jul 2023 07:29:10 +0100 "Sean Charles (emacstheviking)" <objitsu at gmail.com> wrote:
>> 
>> The only real way I could find to model that in Mercury has been to literally
>> re-invent the weel and create this union type and then some helpers:
>> 
>>    :- type p_color
>>        --->    lightgray
>>        ;       gray
>>        ;       yellow
>>        :
>>        ;       raywhite. % FInal colour from header file palette
>> 
>>    :- type color
>>        --->    rgb(uint8, uint8, uint8)
>>        ;       rgba(uint8, uint8, uint8, uint8)
>>        ;       c(p_color).
> 
> I expect you don't actually need color to be a Mercury d.u. type,
> so you can define color as a uint32, and have predicates/functions
> that convert between colors and color components.
> 
> Peter

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.mercurylang.org/archives/users/attachments/20230715/705d9042/attachment.html>


More information about the users mailing list