1. sdl2 and ffi

Hi Andy

SimpleWin

What are you doing here?

18 object event = allocate_struct(event)

Cheers

Chris

new topic     » topic index » view message » categorize

2. Re: sdl2 and ffi

Whats the ffi.e equivalent of allocate, as in

atom rect = allocate(16)
poke4(rect,{50,50,100,100})

Cheers

Chris

new topic     » goto parent     » topic index » view message » categorize

3. Re: sdl2 and ffi

ChrisB said...

What are you doing here?

18 object event = allocate_struct(event)

I'm not sure that going to work. I had discussed the current "correct" approach here: https://openeuphoria.org/forum/137153.wc

ChrisB said...

Whats the ffi.e equivalent of allocate, as in

atom rect = allocate(16)
poke4(rect,{50,50,100,100})

The prototype currently works like this:

-- RECT structure (windef.h) 
-- https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-rect 
constant C_RECT = define_c_type({ 
    C_LONG, -- left 
    C_LONG, -- top 
    C_LONG, -- right 
    C_LONG  -- bottom 
}) 
 
atom rect = allocate_struct( C_RECT ) 
poke_type( rect, C_RECT, {50,50,100,100} ) 

But I'd like to rename define_c_type() to define_c_struct() and then add define_c_union(), and then re-implement define_c_type() for "raw" types (like C_STRING as a copy of C_POINTER).

-- RECT structure (windef.h) 
-- https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-rect 
constant C_RECT = define_c_struct({ 
    {C_LONG,"left"}, 
    {C_LONG,"top"}, 
    {C_LONG,"right"}, 
    {C_LONG,"bottom"} 
}, "RECT") 
 
atom rect = allocate_struct( C_RECT, {50,50,100,100} ) 
? peek_member( rect, "RECT.left" ) -- prints 50 
? peek_member( rect, "RECT" ) -- prints {50,50,100,100} 

Thoughts/opinions on this design are welcome.

-Greg

new topic     » goto parent     » topic index » view message » categorize

4. Re: sdl2 and ffi

ghaberek said...
ChrisB said...

What are you doing here?

18 object event = allocate_struct(event)

I'm not sure that going to work. I had discussed the current "correct" approach here: https://openeuphoria.org/forum/137153.wc

ChrisB said...

Whats the ffi.e equivalent of allocate, as in

atom rect = allocate(16)
poke4(rect,{50,50,100,100})

The prototype currently works like this:

-- RECT structure (windef.h) 
-- https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-rect 
constant C_RECT = define_c_type({ 
    C_LONG, -- left 
    C_LONG, -- top 
    C_LONG, -- right 
    C_LONG  -- bottom 
}) 
 
atom rect = allocate_struct( C_RECT ) 
poke_type( rect, C_RECT, {50,50,100,100} ) 

But I'd like to rename define_c_type() to define_c_struct() and then add define_c_union(), and then re-implement define_c_type() for "raw" types (like C_STRING as a copy of C_POINTER).

-- RECT structure (windef.h) 
-- https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-rect 
constant C_RECT = define_c_struct({ 
    {C_LONG,"left"}, 
    {C_LONG,"top"}, 
    {C_LONG,"right"}, 
    {C_LONG,"bottom"} 
}, "RECT") 
 
atom rect = allocate_struct( C_RECT, {50,50,100,100} ) 
? peek_member( rect, "RECT.left" ) -- prints 50 
? peek_member( rect, "RECT" ) -- prints {50,50,100,100} 

Thoughts/opinions on this design are welcome.

-Greg

Hmm, that'll take some digesting, but my initial thought is that it needs to be as simple as possible to fit the eu ethos, so that someone like me can make stuff with the simpler stuff, and someone like you can do the more brain strangling spaghetti stuff.

so,

rect = allocate(16) allocates a block of memory of 16 bytes 
poke4(rect,{50,50,100,100}) - poke 4 4-byte integers into memory whose location begins at rect 

replace by
create a structure containing the following memory elements

constant C_RECT = define_c_type({            --this c_type is made up of 4 C_LONGs 
    C_LONG, -- left                          --could be any other mix of c_types 
    C_LONG, -- top                           --even other pointers to strings 
    C_LONG, -- right 
    C_LONG  -- bottom 
})\\ 

then

atom rect = allocate_struct( C_RECT )         --allocate a memory block of the size required to hold the c_type C_RECT 
poke_type( rect, C_RECT, {50,50,100,100} ) 
poke type( rect - pointer to where the memory is located 
           C_RECT - the definition of the structure elements 
           {50,50,100,100} - what to poke into each of those memory elements. 

and you could access that structure in the same way with peek as before

peek4u(rect) (= 50) 
peek4u(rect + 12) (= 100) 
peek4u({rect,4}) (= {50,50,100,100}  ) 

or is there a newer way to do that too?

Then you add define_c_struct to short circuit the above process

constant C_RECT = define_c_struct({                               C_RECT is the structure defined 
    {C_LONG,"left"},                                              these are the elements of the structure 
    {C_LONG,"top"},                                               they are named for convenience of access 
    {C_LONG,"right"}, 
    {C_LONG,"bottom"} 
}, "RECT")                                                        this is the variable we are defining with 
}}}                                                                  the structure of C_RECT 
 
Now we are going to create a block of memory that will allow the above structure to live in it 
{{{ 
atom rect = allocate_struct( C_RECT, {50,50,100,100} ) 
which we could still access with
peek4u(rect + 12) (= 100) 
peek4u({rect,4}) (= {50,50,100,100}  ) 

or

? peek_member( rect, "RECT.left" ) -- prints 50 
? peek_member( rect, "RECT" ) -- prints {50,50,100,100} 

I can see the advantages, even though it looks more messy to me, and may not be intermediate level concept wise. However, I also like the simpler version, so

define_c_type
allocate structure

is simpler than

define_c_struct
allocate_struct

Both methods produce the same end results, the second method has more frills, which IMHO increase the complexity. It's a shame you have to wrap the definitions in quotes.

If a union is an amalgamation of single items and structures, I take it it would look something like this (in simple form)

C_RECT already defined as above 
 
constant C_THING = define_c_union { 
          C_LONG, 
          C_LONG, 
          C_POINTER, 
          C_RECT,                         --pointer to the C_RECT structure 
} 
thing = allocate_union(C_THING) 
 

Just my confused thoughts.

Chris

new topic     » goto parent     » topic index » view message » categorize

5. Re: sdl2 and ffi

ChrisB said...

Hmm, that'll take some digesting, but my initial thought is that it needs to be as simple as possible to fit the eu ethos, so that someone like me can make stuff with the simpler stuff, and someone like you can do the more brain strangling spaghetti stuff.

so,

rect = allocate(16) allocates a block of memory of 16 bytes 
poke4(rect,{50,50,100,100}) - poke 4 4-byte integers into memory whose location begins at rect 

replace by
create a structure containing the following memory elements

constant C_RECT = define_c_type({ 
    C_LONG, -- left 
    C_LONG, -- top 
    C_LONG, -- right 
    C_LONG  -- bottom 
})\\ 

then

atom rect = allocate_struct( C_RECT ) --allocate a memory block of the size required to hold the c_type C_RECT 
poke_type( rect, C_RECT, {50,50,100,100} ) 
poke type( rect - pointer to where the memory is located 
           C_RECT - the definition of the structure elements 
           {50,50,100,100} - what to poke into each of those memory elements. 

Yes that's what I'm going for. I originally called it define_c_type() because that's basically what I want: to define additional C types, be they "int" or "float" or structures and unions. That way we're extending the existing design of define_c_func/proc() and the new structure/union "types" will work there as well, almost transparently.

ChrisB said...

and you could access that structure in the same way with peek as before

peek4u(rect) (= 50) 
peek4u(rect + 12) (= 100) 
peek4u({rect,4}) (= {50,50,100,100}  ) 

You're not wrong; you can certainly do that. But there's a lot of potential for error here, especially with the addition of 64-bit Euphoria and many more peek/poke routines like peek8u() and peek_pointer(). IMHO the ethos of Euphoria is to help reduce this kind of confusion. Why should you have to worry about the size of those structure members?

ChrisB said...

or is there a newer way to do that too?

That's where peek_type() and poke_type() come in: these routines will handle the nonsense of "is it four or eight bytes? signed? floating-point?" and just dispatch to the correct peek/poke for you, and it will do so correctly based on the platform (not all C_LONG are four bytes).

ChrisB said...

Then you add define_c_struct to short circuit the above process

constant C_RECT = define_c_struct({ 
    {C_LONG,"left"}, 
    {C_LONG,"top"}, 
    {C_LONG,"right"}, 
    {C_LONG,"bottom"} 
}, "RECT") 

Now we are going to create a block of memory that will allow the above structure to live in it

atom rect = allocate_struct( C_RECT, {50,50,100,100} ) 
which we could still access with
peek4u(rect + 12) (= 100) 
peek4u({rect,4}) (= {50,50,100,100}  ) 

That's the gist of it. Although minding all of those "+ 12" offsets gets very messy when we have to support 32-bit and 64-bit Intel/AMD architecture, let alone that ARM processors are more common and those could introduce differences as well. Using libffi to help do some of this heavy lifting means we're building on literal decades of experience.

ChrisB said...

or

? peek_member( rect, "RECT.left" ) -- prints 50 
? peek_member( rect, "RECT" ) -- prints {50,50,100,100} 

I can see the advantages, even though it looks more messy to me, and may not be intermediate level concept wise. However, I also like the simpler version, so

define_c_type
allocate structure

is simpler than

define_c_struct
allocate_struct

Both methods produce the same end results, the second method has more frills, which IMHO increase the complexity. It's a shame you have to wrap the definitions in quotes.

This is where feedback helps the most. I'm trying to reduce complexity, not add to it. Is peek_type( ptr, C_TYPE ) or peek_member( ptr, "TYPE.name" ) really more complex than peek4u( ptr + 12 )? and what does 12 mean? it might have to be 16 or 20 on 64-bit, right? who knows! So I'm basically going back to the drawing board on what Matt had original mostly-built for memstruct. He was doing all of these sizes-and-offsets by hand. Plus it seems a bit out of order to implement memstruct now with out a better plan for how classes will work later, since they might utilize a lot of the same type-sniffing code.

ChrisB said...

If a union is an amalgamation of single items and structures, I take it it would look something like this (in simple form)

C_RECT already defined as above 
 
constant C_THING = define_c_union { 
    C_LONG, 
    C_LONG, 
    C_POINTER, 
    C_RECT,                         --pointer to the C_RECT structure 
} 
thing = allocate_union(C_THING) 

This is probably where having named members helps the most. Otherwise you're really shooting in the dark with magic numbers. Basically a union lets you perform bit-twiddling by converting a chunk of memory into different types. I don't know about you but to me, remembering what something like peek_type( ptr, C_THING, 1 ) does is going to be hard if not impossible.

ChrisB said...

Just my confused thoughts.

I think you're doing great, Chris.

-Greg

new topic     » goto parent     » topic index » view message » categorize

6. Re: sdl2 and ffi

Already helped with converting more demos to SDL2FFI

sdl_render.e
line 387
public function SDL_RenderFillRect(atom ren,sequence rect)
should be
public function SDL_RenderFillRect(atom ren, atom rect)

Cheers

Chris

new topic     » goto parent     » topic index » view message » categorize

7. Re: sdl2 and ffi

Includes missing in sdl.e

public include SDL_keyboard.e
public include SDL_scancode.e

Cheers

Chris

new topic     » goto parent     » topic index » view message » categorize

8. Re: sdl2 and ffi

I like the idea of having define_c_struct() and define_c_union(), I think that would help simplify things.

Would it be possible to do something like this, eventually:

public constant MY_STRUCT = define_c_struct({ 
    C_INT, --x 
    C_INT, --y 
    C_INT, --w 
    C_INT --h, 
    MY_UNION = define_c_union({ 
    C_FLOAT, --xx 
    C_FLOAT, --yy 
    }) --end of union 
}) --end of struct 

The SDL 2 wrapper using FFI is still a work in progress and can be fixed and updated. It works for the most part, but it could use some tidying up here and there.

new topic     » goto parent     » topic index » view message » categorize

9. Re: sdl2 and ffi

You guys are making cool stuff! The data structures that must exist could open the door to making validating data as well.

-- you could write a function like this: 
is_struct(RECT, x) -- returns true iff x has four members and each member falls within the range of the respective c types 
 
type rect(object x) 
  return is_struct(RECT, x) 
end type 
 
new topic     » goto parent     » topic index » view message » categorize

10. Re: sdl2 and ffi

SDPringle said...

You guys are making cool stuff! The data structures that must exist could open the door to making validating data as well.

-- you could write a function like this: 
is_struct(RECT, x) -- returns true iff x has four members and each member falls within the range of the respective c types 
 
type rect(object x) 
  return is_struct(RECT, x) 
end type 
 

That would help when storing structure values in a sequence but we can't do much about inferring a structure's type while in memory. A structure of int[4] could look the same as short[8] or char[16] and we wouldn't be able to tell by just looking at it. (This is how unions work and it's what allows for things like type punning). I'm still considering how to go about this for implementing structs and classes later. Suggestions are welcome.

-Greg

new topic     » goto parent     » topic index » view message » categorize

11. Re: sdl2 and ffi

FAO Andy

Hi

You've defined SDL_surface in SDL_surface.e as a define_c_type, and then

export constant xSDL_GetWindowSurface = define_c_func(sdl,"+SDL_GetWindowSurface",{C_POINTER},SDL_Surface) 
 
public function SDL_GetWindowSurface(atom win) 
	return c_func(xSDL_GetWindowSurface,{win}) 
end function 

the function SDL_GetWindowSurface returns a pointer to the SDL_surface, not the actual structure itself, so eu is throwing type errors.

atom surf = SDL_GetWindowSurface(win) 

D:\EuProgramming\Euphoria4_1\SDL2Wrapper\Examples\WinImage.exw:28 
type_check failure, surf is {7195552,{36'$',{1952061344,{298874067,2684419766,...  

So, am I missing a step somewhere?

Cheers

Chris

new topic     » goto parent     » topic index » view message » categorize

12. Re: sdl2 and ffi

FAO Greg

Andy's simplewin demo works when you do this

 --object event = allocate_struct(event) 
 --this is the missing allocate_mem 
 object event = machine_func(16, 1024)   --16 is M_ALLOC 

I know that event is a union, and you can't access the union part of the event (not without lots of pointers referincing pointers spaghetti at least), but the first part is a structure, and you can access those.

Could I request an allocate_mem procedure in ffi, to allow for those rare events when users might want to allocate there own blocks of memory that don't fit into other types of memory allocation?

And....make your M_ALLOC, M_FREE etc public

Cheers Chris

new topic     » goto parent     » topic index » view message » categorize

13. Re: sdl2 and ffi

ChrisB said...

FAO Andy

Hi

You've defined SDL_surface in SDL_surface.e as a define_c_type, and then

export constant xSDL_GetWindowSurface = define_c_func(sdl,"+SDL_GetWindowSurface",{C_POINTER},SDL_Surface) 
 
public function SDL_GetWindowSurface(atom win) 
	return c_func(xSDL_GetWindowSurface,{win}) 
end function 

the function SDL_GetWindowSurface returns a pointer to the SDL_surface, not the actual structure itself, so eu is throwing type errors.

atom surf = SDL_GetWindowSurface(win) 

D:\EuProgramming\Euphoria4_1\SDL2Wrapper\Examples\WinImage.exw:28 
type_check failure, surf is {7195552,{36'$',{1952061344,{298874067,2684419766,...  

So, am I missing a step somewhere?

Cheers

Chris

I've changed it from SDL_Surface to C_POINTER. I've uploaded the change to github. I also like the allocate_mem feature that could be added to the FFI wrapper/library.

new topic     » goto parent     » topic index » view message » categorize

14. Re: sdl2 and ffi

Thanks

slightly change allocate_string in ffi

public function allocate_string( sequence s, integer cleanup = 0 ) 
 
	atom mem = machine_func( M_ALLOC, length(s) + 1 ) 
 
	if mem then 
		poke( mem, s ) 
		poke( mem+length(s), 0 ) 
	end if 
 
    if cleanup then end if 
 
	return mem 
end function 

This brings it more in line with eu's machin.e allocate_string(). You'll note the cleanup is basically just a stub.

Note, I'm just getting them to work atm, don't know if this is the best way.

Cheers

Chris

new topic     » goto parent     » topic index » view message » categorize

15. Re: sdl2 and ffi

SDL_render.e
line 116
public function SDL_CreateTextureFromSurface(atom ren, sequence surf)
->
public function SDL_CreateTextureFromSurface(atom ren, atom surf)

Cheers Chris

new topic     » goto parent     » topic index » view message » categorize

16. Re: sdl2 and ffi

ChrisB said...

SDL_render.e
line 116
public function SDL_CreateTextureFromSurface(atom ren, sequence surf)
->
public function SDL_CreateTextureFromSurface(atom ren, atom surf)

Cheers Chris

I have fixed this and uploaded it to Github.

new topic     » goto parent     » topic index » view message » categorize

17. Re: sdl2 and ffi

Hi Andy

Did you just replace that one instance? There are quite a few others. All surf s and rect s need to be atoms, not sequences, because all of the sdl C functions, afaict, need to have pointers sent to them, and you are trying to send a type. I may be missing something of course.

I've just recloned the repository, and it's wiped all the changes and test files I made on my local folder. Ho hum, back to the drawing board.

Cheers

Chris

edit - I see that define_c_type returns an atom, which I imagine is a pointer to the block of memory allocated for the structure. The structure may need to be filled to be sent, or may be filled by the C function, but on the eu side the function still need to recieve pointers (atoms) to send to the C function of the block of memory allocated.

Cheers

Chris

new topic     » goto parent     » topic index » view message » categorize

18. Re: sdl2 and ffi

ChrisB said...

I've just recloned the repository, and it's wiped all the changes and test files I made on my local folder. Ho hum, back to the drawing board.

Oh no! Here's nice guide from the R community on how to avoid that: https://happygitwithr.com/pull-tricky.html Basically you use git stash to save your changes then git pull to pull updates then git stash pop to put back your changes.

ChrisB said...

I see that define_c_type returns an atom, which I imagine is a pointer to the block of memory allocated for the structure. The structure may need to be filled to be sent, or may be filled by the C function, but on the eu side the function still need to recieve pointers (atoms) to send to the C function of the block of memory allocated.

This is correct. The value returned from define_c_type() are pointers to ffi_type structures. When using std/ffi.e the values of C_INT, C_POINTER, etc. are also pointers. The only difference being the built-in values from std/ffi.e are pulled directly from libffi using define_c_var().

The crucial point being that you still need to allocate memory and then peek/poke values accordingly. Not only should you avoid passing the values you made with define_c_type() to c_func(), but doing so could overwrite the memory at that location which could cause any number of weird problems later.

-Greg

new topic     » goto parent     » topic index » view message » categorize

19. Re: sdl2 and ffi

ChrisB said...

Hi Andy

Did you just replace that one instance? There are quite a few others. All surf s and rect s need to be atoms, not sequences, because all of the sdl C functions, afaict, need to have pointers sent to them, and you are trying to send a type. I may be missing something of course.

I've just recloned the repository, and it's wiped all the changes and test files I made on my local folder. Ho hum, back to the drawing board.

Cheers

Chris

edit - I see that define_c_type returns an atom, which I imagine is a pointer to the block of memory allocated for the structure. The structure may need to be filled to be sent, or may be filled by the C function, but on the eu side the function still need to recieve pointers (atoms) to send to the C function of the block of memory allocated.

Cheers

Chris

I'll try and go through the code and update it, this weekend. I think my inital thought process must have been from looking at how Greg wrapped Raylib using FFI.

--Raylib Wrapper (FFI) 
public constant RL_VECTOR2 = define_c_type({ 
	C_FLOAT, -- x   // Vector x component 
	C_FLOAT  -- y   // Vector y component 
}) 
 
export constant raylib = open_dll( "raylib.dll" ), 
	xInitWindow         = define_c_proc( raylib, "+InitWindow", {C_INT,C_INT,C_STRING} ), 
	xWindowShouldClose  = define_c_func( raylib, "+WindowShouldClose", {}, C_BOOL ), 
	xCloseWindow        = define_c_proc( raylib, "+CloseWindow", {} ), 
	xGetWindowPosition  = define_c_func( raylib, "+GetWindowPosition", {}, RL_VECTOR2 ), 
	xGetScreenToWorld2D = define_c_func( raylib, "+GetScreenToWorld2D", {RL_VECTOR2,RL_CAMERA2D}, RL_VECTOR2 ) 
--Only copying what is relevant to my thought process 
 
public function GetScreenToWorld2D( sequence position, sequence camera ) 
	return c_func( xGetScreenToWorld2D, {position,camera} ) 
end function 

My thought process being that if a struct or so is defined as define_c_type() using FFI, then it must be called as a sequence in Euphoria. However that it is clear that is not always the case. SDL must not pass all structs by value as Raylib does.

public constant SDL_Rect = define_c_type({ 
--Part of My SDL Wrapper 
--From SDL_Rect.e 
	C_INT, --x 
	C_INT, --y 
	C_INT, --w 
	C_INT --h 
}) 
 
export constant xSDL_HasIntersection = define_c_func(sdl,"+SDL_HasIntersection",{SDL_Rect,SDL_Rect},C_BOOL) 
 
public function SDL_HasIntersection(sequence a,sequence b) 
	return c_func(xSDL_HasIntersection,{a,b}) 
end function 
 
--My thought being you could use it as such: 
sequence myRect = SDL_Rect() 
myRect[1] = 5 --x 
myRect[2] = 5 --y 
 
And so forth.  
}) 

My thought process being that sequence is the closest thing Euphoria has to struct in its current form.

EDIT: I have changed most of the sequences to atoms. Functions that returned a define_c_type() should now return a pointer as I changed it from I.E. SDL_Rect to C_POINTER.

new topic     » goto parent     » topic index » view message » categorize

20. Re: sdl2 and ffi

Thanks Andy and Gregg.

This is a good place to put thought processes.

Chris

new topic     » goto parent     » topic index » view message » categorize

21. Re: sdl2 and ffi

Hi

There's a rule in there somewhere isn't there.

When the C library accepts pass by value you can pass the parameter as a sequence, eg

public constant RL_VECTOR2 = define_c_type({  
	C_FLOAT, -- x   // Vector x component  
	C_FLOAT  -- y   // Vector y component  
})  
 
constant xGetScreenToWorld2D = define_c_func( raylib, "+GetScreenToWorld2D", {RL_VECTOR2,RL_CAMERA2D}, RL_VECTOR2 )  
--Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera); // Get the world space position for a 2d camera screen space position 
 
public function GetScreenToWorld2D( sequence position, sequence camera )  
	return c_func( xGetScreenToWorld2D, {position,camera} )  
end function  

However, when the C library accepts pass by reference, you must use the pointer to the structure as a parameter, eg

public constant SDL_Rect = define_c_type({  
	C_INT, --x  
	C_INT, --y  
	C_INT, --w  
	C_INT --h  
})  
 
--to illustrate the 2 ways of filling the structure 
a = allocate_struct(SDL_Rect) 
b = allocate_struct(SDL_Rect, {30,70,10,10}) 
 
poke4(a, {20,30,50,50} ) 
 
export constant xSDL_HasIntersection = define_c_func(sdl,"+SDL_HasIntersection",{SDL_Rect,SDL_Rect},C_BOOL)  
--SDL_bool SDL_HasIntersection(const SDL_Rect * A, const SDL_Rect * B);  
 
public function SDL_HasIntersection(atom a,atom b)  
        --a and b pointers to the SDL_RECT structures, already filled with values poked into the allocated memory blocks 
	return c_func(xSDL_HasIntersection,{a,b})  
end function  
 

Does that look ok to add to some documentation?

Chris

new topic     » goto parent     » topic index » view message » categorize

Search



Quick Links

User menu

Not signed in.

Misc Menu