1. Phix+EuGTK

Has anybody ever tried, or is it possible, to use EuGTK for Phix?

Oops. I did just find this.

new topic     » topic index » view message » categorize

2. Re: Phix+EuGTK

EuGtk for Phix uses the Euphoria std/lib a lot. It also seems to use unique Euphoria syntax. Looks like translating euGtk to Phix will take a lot of work.

Console programs will work on Euphoria and Phix.

IUP gui will work on Euphoria and Phix. The wrappers are different but IUP remains the same. (My frustration with IUP is that 32bit Linux is no longer supported.)

JAPI I think will work on Euphoria and Phix. (You need to install Java for this.) The JAPI interface is a bit old looking.

Tcl/tk can be used on Euphoria and Phix. (The tk gui is satisfactory, and it works on 32 bit Linux)

_tom

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

3. Re: Phix+EuGTK

The JAPI2 interface uses Java Swing, which, although now superceded by JavaFX, looks, in my view, cleaner than IUP, wxwidgets, win32lib, tcl/tk, etc.

I haven't tried it on Phix though!

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

4. Re: Phix+EuGTK

No. I have tried to convert EuGtk to work with Phix, and got a basic window and button to work, but as noted above, it would be a major job. Different libs, etc.

If I recall correctly, there were some things not yet implemented in phix which were "show stoppers". Those would require writing significant amounts of code to implement and test.

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

5. Re: Phix+EuGTK

I'm now looking at this thread wondering why I never asked what...

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

6. Re: Phix+EuGTK

petelomax said...

I'm now looking at this thread wondering why I never asked what...

It's been a while, I can't remember what the problem was. However, I would like to see EuGTK work with phix, so I'll give it another try.

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

7. Re: Phix+EuGTK

So far, not so good...

I get a seg fault, core dumped when I try to call a function contained in a library as a method.

Note that calling "gtk_window_new" to create a public "handle" to the new window works fine. Calling "gtk_widget_show" (or any other shared library routine) from within a class procedure fails.

Maybe there's something I am doing completely wrong.

constant LIB = open_dll("libgtk-3.so.0"), 
I = C_INT,   -- INTEGER 
P = C_PTR,   -- POINTER  
 
integer init = define_c_func(LIB,"gtk_init_check",{P,P},P) 
if init < 1 or c_func(init,{0,0})=0 then 
  crash("Failed to initialize GTK library!") 
end if 
 
class Window  
 public atom handle = c_func(define_c_func(LIB,"gtk_window_new",{I},P),{0}) 
 procedure show() 
   puts(1,"This handle: ") ? this.handle 
   c_proc(define_c_proc(LIB,"gtk_widget_show",{P}),{this.handle}) -- seg fault 
 
-- define_c_proc is the part that fails when inside a procedure 
 
 end procedure 
end class 
 
Window win = new() 
puts(1,"Win handle: ") ? win.handle 
win.show() 
main() 
 
procedure main() 
c_proc(define_c_proc(LIB,"gtk_main",{})) 
end procedure 
new topic     » goto parent     » topic index » view message » categorize

8. Re: Phix+EuGTK

irv said...

So far, not so good...

I get a seg fault, core dumped when I try to call a function contained in a library as a method.

Works for me, on windows and linux (only tested 64 bit on the latter, 32 and 64 on windows).

I believe there's a thing with the linux kernel... see https://openeuphoria.org/forum/m/134873.wc
On the machine I tested it on, uname -r yields 3.5.0-54-generic, if that helps any.

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

9. Re: Phix+EuGTK

4.15.0-54-generic

I don't know if that is the same problem, I can successfully load routines from .so files. define_c_proc works when inside a class function, but not when inside a class procedure. Code below works:

class Window   
 public atom handle = c_func(define_c_func(LIB,"gtk_window_new",{I},P),{0})  
 function show()  
   puts(1,"This handle: ") ? this.handle  
   c_proc(define_c_proc(LIB,"gtk_widget_show",{P}),{this.handle})   
 return 1 
 end function  
end class 
new topic     » goto parent     » topic index » view message » categorize

10. Re: Phix+EuGTK

irv said...

4.15.0-54-generic

define_c_proc works when inside a class function, but not when inside a class procedure.

Right. I've now got the same crash (proc not func) on Ubuntu 20.04.1 LTS with a uname -r of 5.4.0-58-generic
building edb...

Some small progress, I think it's this (sort of) thing, from builtins\VM\pcfunc.e, line 308:

        [PE64] 
            mov rcx,rsp -- put 2 copies of rsp onto the stack... 
            push rsp 
            push rcx 
            or rsp,8    -- [rsp] is now 1st or 2nd copy: 
                        -- if on entry rsp was xxx8: both copies remain on the stack 
                        -- if on entry rsp was xxx0: or rsp,8 effectively pops one of them (+8) 
                        -- obviously rsp is now xxx8, whatever alignment we started with 
 
            mov rax,[lib] 
--          mov r15,h4 
            sub rsp,8*5                         -- minimum 4 param shadow space, and align 
            mov rdx,[name] 
            call :%pLoadMint -- (rax:=(int64)rax; rdx preserved) 
            shl rdx,2                           -- lpProcName 
            mov rcx,rax                         -- hModule 
            call "kernel32.dll","GetProcAddress" 
            lea rdi,[addr] 
--          add rsp,8*5 
--          pop rsp 
            mov rsp,[rsp+8*5]   -- equivalent to the add/pop 
            call :%pStoreMint 
        [ELF64] 
            mov rax,[lib] 
            mov rsi,[name] 
            call :%pLoadMint -- (rax:=(int64)rax) 
            shl rsi,2                           -- symbol 
            mov rdi,rax                         -- handle 
            call "libdl.so.2", "dlsym" 
            lea rdi,[addr] 
            call :%pStoreMint 
        [] 

The windows code is aligning the stack, but the Linux code isn't...
There's a movaps instruction which is expecting the stack to be aligned,
and I guess some kernels were built with whatever-compiler-flag it is
that causes it to emit movups instructions instead. And I originally ported
Phix to Linux on such a machine.

Anyway, it's fixed! The above needs to become

        [64] 
            mov rcx,rsp -- put 2 copies of rsp onto the stack... 
            push rsp 
            push rcx 
            or rsp,8    -- [rsp] is now 1st or 2nd copy: 
                        -- if on entry rsp was xxx8: both copies remain on the stack 
                        -- if on entry rsp was xxx0: or rsp,8 effectively pops one of them (+8) 
                        -- obviously rsp is now xxx8, whatever alignment we started with 
 
            mov rax,[lib] 
--          mov r15,h4 
            sub rsp,8*5                         -- minimum 4 param shadow space, and align 
        [PE64] 
            mov rdx,[name] 
            call :%pLoadMint -- (rax:=(int64)rax; rdx preserved) 
            shl rdx,2                           -- lpProcName 
            mov rcx,rax                         -- hModule 
            call "kernel32.dll","GetProcAddress" 
        [ELF64] 
            mov rsi,[name] 
            call :%pLoadMint -- (rax:=(int64)rax) 
            shl rsi,2                           -- symbol 
            mov rdi,rax                         -- handle 
            call "libdl.so.2", "dlsym" 
        [64] 
            lea rdi,[addr] 
--          add rsp,8*5 
--          pop rsp 
            mov rsp,[rsp+8*5]   -- equivalent to the add/pop 
            call :%pStoreMint 
        [] 

and further down, also in builtins\VM\pcfunc.e, line 1115, replace

        if platform()=WINDOWS then 
            if la<5 then 
                args &= repeat(0,5-la) 
                argdefs &= repeat(#01000004,5-la) 
            elsif remainder(la,2)!=1 then 
                args &= 0 
                argdefs &= #01000004    -- (C_INT) 
            end if 
        else -- LINUX 
            if la<6 then 
                args &= repeat(0,6-la) 
                argdefs &= repeat(#01000004,6-la) 
            end if 
        end if 
        #ilASM{ 
            [PE64] 

with

        if platform()=WINDOWS then 
            if la<5 then 
                args &= repeat(0,5-la) 
                argdefs &= repeat(#01000004,5-la) 
                la = 5 
            end if 
        else -- LINUX 
--          if la<6 then 
            if la<7 then 
                args &= repeat(0,7-la) 
                argdefs &= repeat(#01000004,7-la) 
                la = 7 
            end if 
        end if 
        if remainder(la,2)!=1 then 
            args &= 0 
            argdefs &= #01000004    -- (C_INT) 
        end if 
        #ilASM{ 
--14/12/20 
--          [PE64] 
            [64] 

Obviously, a quick './p -c p' and you should be good.

I'll have a quick trawl for other places that need the same treatment.

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

11. Re: Phix+EuGTK

Two more in pcfunc.e. line 206

    #ilASM{ 
        [PE32] 
            mov eax,[filename] 
            push ebx    --(=0) (for fild qword) 
            shl eax,2 
            push eax                            -- lpLibFileName 
            call "kernel32.dll","LoadLibraryA" 
            push eax 
            lea edi,[res] 
            fild qword[esp] 
            add esp,8 
            call :%pStoreFlt                    -- ([edi]:=ST0) 
        [PE64] 
            mov rcx,rsp -- put 2 copies of rsp onto the stack... 
            push rsp 
            push rcx 
            or rsp,8    -- [rsp] is now 1st or 2nd copy: 
                        -- if on entry rsp was xxx8: both copies remain on the stack 
                        -- if on entry rsp was xxx0: or rsp,8 effectively pops one of them (+8) 
                        -- obviously rsp is now xxx8, whatever alignment we started with 
            mov rcx,[filename] 
            sub rsp,8*5         -- minimum 4 param shadow space, and align 
            shl rcx,2                           -- lpLibFileName 
            call "kernel32.dll","LoadLibraryA" 
            mov [rsp],rax 
            lea rdi,[res] 
            fild qword[rsp] 
--          add rsp,8*5 
--          pop rsp 
            mov rsp,[rsp+8*5]   -- equivalent to the add/pop 
            call :%pStoreFlt    -- ([rdi]:=ST0) 
        [ELF32] 
            mov eax,[filename] 
--          push 1              -- flags (RTLD_LAZY) 
            push 0x00101        -- flags (RTLD_GLOBAL|RTLD_LAZY) 
            shl eax,2           -- ref->raw 
            push eax            -- library name 
            call "libdl.so.2", "dlopen" 
            add esp,8 
            lea edi,[res] 
            call :%pStoreMint   -- [edi]:=eax, as float if rqd 
        [ELF64] 
            mov rdi,[filename] 
            mov rsi,0x00101     -- flags (RTLD_GLOBAL|RTLD_LAZY) 
            shl rdi,2           -- ref->raw (library name) 
            call "libdl.so.2", "dlopen" 
            lea rdi,[res] 
            call :%pStoreMint   -- [rdi]:=rax, as float if rqd 
        [] 
          } 

should be replaced with this

    #ilASM{ 
        [PE32] 
            mov eax,[filename] 
            push ebx    --(=0) (for fild qword) 
            shl eax,2 
            push eax                            -- lpLibFileName 
            call "kernel32.dll","LoadLibraryA" 
            push eax 
            lea edi,[res] 
            fild qword[esp] 
            add esp,8 
            call :%pStoreFlt                    -- ([edi]:=ST0) 
        [ELF32] 
            mov eax,[filename] 
            push 0x00101        -- flags (RTLD_GLOBAL|RTLD_LAZY) 
            shl eax,2           -- ref->raw 
            push eax            -- library name 
            call "libdl.so.2", "dlopen" 
            add esp,8 
            lea edi,[res] 
            call :%pStoreMint   -- [e/rdi]:=e/rax, as float if rqd 
        [64] 
            mov rcx,rsp -- put 2 copies of rsp onto the stack... 
            push rsp 
            push rcx 
            or rsp,8    -- [rsp] is now 1st or 2nd copy: 
                        -- if on entry rsp was xxx8: both copies remain on the stack 
                        -- if on entry rsp was xxx0: or rsp,8 effectively pops one of them (+8) 
                        -- obviously rsp is now xxx8, whatever alignment we started with 
        [PE64] 
            mov rcx,[filename] 
            sub rsp,8*5         -- minimum 4 param shadow space, and align 
            shl rcx,2                           -- lpLibFileName 
            call "kernel32.dll","LoadLibraryA" 
        [ELF64] 
            mov rdi,[filename] 
            sub rsp,8*5         -- minimum 4 param shadow space, and align 
            mov rsi,0x00101     -- flags (RTLD_GLOBAL|RTLD_LAZY) 
            shl rdi,2           -- ref->raw (library name) 
            call "libdl.so.2", "dlopen" 
        [64] 
            lea rdi,[res] 
--          add rsp,8*5 
--          pop rsp 
            mov rsp,[rsp+8*5]   -- equivalent to the add/pop 
            call :%pStoreMint   -- [e/rdi]:=e/rax, as float if rqd 
        [] 
          } 

and in call(), line 900ish, there are two [PE64] which should be deleted (and then no need to repeat the [64] guards after the first one)

There is a mmap call in builtins\VM\pHeap.e line 1058, and a pthread_exit call in builtins\VM\pThreadN.e line 322 that may need stack alignment, but I have left them alone for now.

Otherwise (surprisingly) I think that's it for the stack alignment issues

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

12. Re: Phix+EuGTK

OOPS - those mods seemed fine on linux, but they've spannered me windows tests...

OK, that's better now - I've updated post #11, make sure you've got the replacement with [PE32]/pStoreFlt in it.

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

13. Re: Phix+EuGTK

So far, so good. Now a class procedure which calls a shared lib function works. Here's what I'm testing with:

constant LIB = open_dll("libgtk-3.so.0") 
constant  
 P = C_PTR, 
 I = C_INT 
  
integer init = define_c_func(LIB,"gtk_init_check",{P,P},P)  
if init < 1 or c_func(init,{0,0})=0 then  
  crash("Failed to initialize GTK library!")  
end if  
 
class Window  
  public atom handle = c_func(define_c_func(LIB,"gtk_window_new",{I},P),{0}) 
   
  procedure showp() 
    ? "show proc:" ? handle 
    c_proc(define_c_proc(LIB,"gtk_widget_show",{P}),{handle}) 
  end procedure 
   
  function showf() 
    ? "show func:" ? handle 
    c_proc(define_c_proc(LIB,"gtk_widget_show",{P}),{handle}) 
  return 1 
  end function 
   
end class 
 
procedure main()  
c_proc(define_c_proc(LIB,"gtk_main",{}))  
end procedure  
 
Window w1 = new()  
 
? w1 ? w1.handle 
 
 w1.showp() -- was seg fault, now works 
? w1.showf() -- works 
 
main() 

Calling either the showp procedure or the showf function displays the window.

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

14. Re: Phix+EuGTK

Next question: how to create new instances?

constant LIB = open_dll("libgtk-3.so.0") 
constant  
 P = C_PTR, 
 I = C_INT 
  
integer init_rid = define_c_func(LIB,"gtk_init_check",{P,P},P)  
if init_rid < 1 or c_func(init_rid,{0,0})=0 then  
  crash("Failed to initialize GTK library!")  
end if  
 
class Window  
  public atom handle = c_func(define_c_func(LIB,"gtk_window_new",{I},P),{0}) 
  procedure show() 
    c_proc(define_c_proc(LIB,"gtk_widget_show",{P}),{handle}) 
  end procedure 
end class 
 
procedure main()  
c_proc(define_c_proc(LIB,"gtk_main",{}))  
end procedure  
 
Window w1 = new()  
Window w2 = new() 
 
? w1 ? w1.handle 
? w2 ? w2.handle -- same as w1 
 
w1.show()  
w2.show() -- already shown 
 
main() 

{"struct","Window",2,1} 
30917232 
{"struct","Window",2,2} 
30917232 

Both windows have the same "handle". How can I change my code so each call to new() will call the "gtk_window_new" function again to get a new unique handle?

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

15. Re: Phix+EuGTK

irv said...

Both windows have the same "handle". How can I change my code so each call to new() will call the "gtk_window_new" function again to get a new unique handle?

Yeah, inline field defaults like that are evaluated precisely once.

The sure-fire solution looks like this

class window 
    integer handle 
    procedure show() ? handle end procedure 
end class 
window w1 = new({rand(99)}), 
       w2 = new({rand(99)}) 
w1.show() 
w2.show() 

I ran out of steam on constructors, so they're incomplete, undocumented, and possibly subject to change (eg, dunno why I made em functions).

class window 
    integer handle 
    function window() 
        this.handle = rand(99) 
        return this 
    end function 
    procedure show() ? handle end procedure 
end class 
window w1 = new(), 
       w2 = new() 
w1.show() 
w2.show() 

Optional parameters aren't as flexible in Phix as they are in OE, they only support entirely fixed constants and single-level calls of length(), command_line(), routine_id(), platform(), machine_bits(), and machine_word(), rather than full expressions. I could perhaps take another run at that if it's genuinely going to help, but I'll probably just run into the same reasons that made me run away screaming the first time.
The other [related] area of doubt is how a constructor might mix and match optional/defaulted bits with its own code - given the above you can't put the gtk_window_new() on the parameter itself, but maybe you could use say null or -1 as a sentinel [I'm sticking with the likewise-also-illegal rand() for this demo]

class window 
    integer handle 
    function window(integer r=-1) 
--      ?{"r",r,this.handle} 
        if r=-1 then r = rand(99) end if 
        this.handle = r 
--or    if r!=-1 then this.handle = r end if 
        return this 
    end function 
    procedure show() ? handle end procedure 
end class 
window w1 = new(), 
       w2 = new({rand(99)}) 
w1.show() 
w2.show() 

PS I can tell you from today's experience that errors in constructors generate the same level of helpfulness you'd expect from C++

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

16. Re: Phix+EuGTK

This works to open multiple instances of a class (handles returned by GTK are unique)

constant LIB = open_dll("libgtk-3.so.0") 
constant  
 P = C_PTR, 
 I = C_INT 
 
integer init_rid = define_c_func(LIB,"gtk_init_check",{P,P},P)  
if init_rid < 1 or c_func(init_rid,{0,0})=0 then  
  crash("Failed to initialize GTK library!")  
end if  
 
class Widget 
  public atom handle 
  public integer sigid  
 
  procedure show() 
    c_proc(define_c_proc(LIB,"gtk_widget_show",{P}),{this.handle}) 
  end procedure   
 
  procedure connect(object sig, object fn, object data=0, object closure=0, integer flags=0) 
    integer rid 
    sig = allocate_string(sig) 
    if string(fn) then 
      rid = routine_id(fn)  
      if rid > 0 then 
        fn = call_back(rid) 
        if fn < 1 then  
          crash("Invalid callback for -> %s",{fn}) 
        end if 
      else 
        crash("Can't locate function -> %s",{fn}) 
      end if 
    end if 
    if string(data) then data = allocate_string(data) end if 
    sigid = c_func(define_c_func(LIB,"g_signal_connect_data",{P,P,P,P,P,I},I), 
            {this.handle,sig,fn,data,closure,flags}) 
  end procedure 
 
end class 
 
class Window extends Widget 
  function Window() 
    this.handle = c_func(define_c_func(LIB,"gtk_window_new",{I},P),{0}) 
    return this 
  end function 
 
  procedure title(sequence t) 
    c_proc(define_c_proc(LIB,"gtk_window_set_title",{P,P}),{handle,t}) 
  end procedure 
 
  procedure size(integer w, integer h) 
    c_proc(define_c_proc(LIB,"gtk_window_set_default_size",{P,I,I}),{handle,w,h}) 
  end procedure 
   
end class 
 
function Quit() 
  c_proc(define_c_proc(LIB,"gtk_main_quit",{})) 
return 0 
end function 
 
procedure main()  
c_proc(define_c_proc(LIB,"gtk_main",{}))  
end procedure  
 
-------------------------------------------------------------------- 
 
Window w1 = new()  ? w1.handle 
Window w2 = new()  ? w2.handle 
w1.title("I'm Number One!") 
w2.title("but I'm on top") 
w1.size(500,300) 
w1.connect("destroy","Quit") 
w1.show() w2.show() 
main() 

Window handles:

10781296 
10781968 

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

17. Re: Phix+EuGTK

irv said...

This works

This works on windoze:

constant LIB = open_dll(iff(platform()=LINUX?"libgtk-3.so.0":"libgtk-3-0.dll")), 
         LIBGOBJECT = open_dll(iff(platform()=LINUX?"libgobject-2.0.so.0?":"libgobject-2.0-0.dll")) 
 
... 
 
  procedure connect(string sig, object fn, data=0, closure=0, integer flags=0)  
--  integer rid  
--  sig = allocate_string(sig)  
    if string(fn) then  
      integer rid = routine_id(fn)   
      if rid <= 0 then  
        crash("Can't locate function -> %s",{fn})  
      end if 
      integer cb = call_back(rid)  
      if cb < 1 then     
          crash("Invalid callback for -> %s",{fn})  
      end if  
      fn = cb 
    end if  
--  if string(data) then data = allocate_string(data) end if  
    sigid = c_func(define_c_func(LIBGOBJECT,"g_signal_connect_data",{P,P,P,P,P,I},I),  
            {this.handle,sig,fn,data,closure,flags})  
  end procedure  

Linux is generally lax about .so names, as long as an open_dll has succeeded somewhere, it'll hunt things down. (I've seen lnx/OE code use open_dll("") before now.)
However Winderrs kicks up a right old fuss at the slightest slip-up. (Though some dlls re-export some of their imports, sometimes.)
You don't need allocate_string() if it's already a string (though you do if it's a d/qword sequence of 4/8 bytes per 8-bit char).
I figured it was probably best to only overwrite fn once it was no longer needed for the the error message. Everything else stayed the same.

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

18. Re: Phix+EuGTK

Great, thanks.

I'm finding Phix classes really make a difference. I've implemented some of the most used widgets and gotten GTK to display a nice window with a label, image, and a couple of buttons. Looks good, and the coding is much easier.

Of course, there are about 300 more to go. shocked

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

19. Re: Phix+EuGTK

This is working really well.

Question: is there a way to retrieve a class instance if all you have is the handle?

Example: For buttons the first parameter passed to a function linked to the button click event is the handle (a unique pointer supplied by GTK). The second parameter is optional data.

I could do as I had to do in EuGTK, and build a registry of control instances, but I have a suspicion that such a thing is already in phix.

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

20. Re: Phix+EuGTK

irv said...

Question: is there a way to retrieve a class instance if all you have is the handle?

No. There is a table of fields in builtins\structs.e, but it's private, you'd need to faff about with struct_dx() and field_dx() to decipher it, and the delete_routine()'d reference you're after has been given over to your application, and trying to replicate a "fake copy" would no doubt end in tears.

Here's what I'd do:

class window nullable 
    integer handle  
    function get_handle() return handle end function  
end class  
 
integer hdict = new_dict("handle lookup") 
 
function new_window(integer handle) 
    window w = new({handle}) 
    setd(w.get_handle(),w,hdict) 
    return w 
end function 
 
function get_by_handle(integer handle) 
    return getd(handle,hdict) 
end function 
 
function destroy_window(window w) 
    deld(w.get_handle(),hdict) 
    delete(w) 
    return null 
end function 
 
window w = new_window(17) 
 
?w 
?get_by_handle(17) 
 
w = destroy_window(w) 

PS Here's a bugfix to builtins/structs.e, line 536, in case you need it:

--16/12/20: [struct(0)/class(0) and similar should return false, not crash] 
--      elsif s=NULL then 
        elsif s=NULL and rid!=0 then 
new topic     » goto parent     » topic index » view message » categorize

21. Re: Phix+EuGTK

Actually, this might will be more to your liking

class Widget 
  public atom handle  
end class  
 
integer hdict = new_dict("handle lookup") 
  
class Window extends Widget  
  function Window()  
    this.handle = 17 
    setd(this.handle,this,hdict) 
    return this  
  end function  
  procedure ~Window()  
    deld(this.handle,hdict) 
  end procedure  
end class 
  
function get_by_handle(integer handle) 
    return getd(handle,hdict) 
end function 
 
Window w = new() 
 
?w 
?get_by_handle(17) 
 
?dict_size(hdict) -- 1 
delete(w) 
?dict_size(hdict) -- 0 
new topic     » goto parent     » topic index » view message » categorize

22. Re: Phix+EuGTK

Super!

Phix is full of tricks.

And faster than a politician pocketing a bribe smile

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

23. Re: Phix+EuGTK

Next problem: passing a value to/from a function in a shared lib (.so) which expects a C_DOUBLE doesn't work on Linux 64 bit.

  • Edit - problem was in my code.
new topic     » goto parent     » topic index » view message » categorize

24. Re: Phix+EuGTK

irv said...

Next problem: passing a value to a function in a shared lib (.so) which expects a C_DOUBLE doesn't work on Linux 64 bit.

Does not really surprise me. With just a quick scan, I spotted this on line 1554 of builtins\VM\pcfunc.e:

                            fstp dword[rsp] 

I'm pretty sure that should be qword. Let me know it if helps.

Ugh. Ignore that. The one on line 1450 is correct, so that's not it.

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

25. Re: Phix+EuGTK

For testing purposes, I created a shared library in C:

/* libmytest */ 
 
#include <stdio.h> 
 
double hello(double x) 
{ 
 printf("Hello! Your number was %g\n",x); 
 return x*2.0; 
} 
Euphoria:

include std/dll.e 
atom mylib = open_dll("libmytest.so") 
atom fn = define_c_func(mylib,"hello",{C_DOUBLE},C_DOUBLE) 
printf(1,"Got back: %g\n", c_func(fn,{123.456})) 

Can anybody explain why I get two hello's?

$ eui eulibtest.ex  
Hello! Your number was 123.456 
Hello! Your number was 123.456 
Got back: 246.912 
Phix:

atom mylib = open_dll("libmytest.so") 
atom fn = define_c_func(mylib,"hello",{C_DOUBLE},C_DOUBLE) 
printf(1,"Got back: %g\n", c_func(fn,{123.456})) 

$ p plibtest.ex 
Hello! Your number was 123.456 
Got back: 246.912 
I appears that passing doubles to and from C libraries does work in Phix, and works twice as hard in Euphoria. The problem lies elsewhere in my program. Will keep digging.

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

26. Re: Phix+EuGTK

irv said...

double hello(double x) 
Can anybody explain why I get two hello's?

LOL.

Anyway, erm, it will be worth checking what happens when you pass an integer from Phix into a C_DOUBLE.

And then I'd check {1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8,9.9,10.10} are all present and correct.
Forked into: External functions returning C_DOUBLE are called twice (bug?)

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

27. Re: Phix+EuGTK

petelomax said...
irv said...

double hello(double x) 
Can anybody explain why I get two hello's?

LOL.


Forked into: External functions returning C_DOUBLE are called twice (bug?)

This is the FIRST TIME EVER that C has done exactly what I told it to do! blink

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

28. Re: Phix+EuGTK

Constructing GTK classes works fine, using the syntax below. What I need to find out is what the deconstructor does i.e. ~Window()

  • When or how is it called?
  • Do I have to connect every widget's "destroy" signal to call the deconstructor for that class of widget?
  • What does it do besides remove the Window instance from the dictionary?
class Window extends Widget   
 
  function Window()   
    this.handle = 17  
    setd(this.handle,this,hdict)  
    return this   
  end function 
   
  procedure ~Window()   
    deld(this.handle,hdict)  
  end procedure   
 
end class  
new topic     » goto parent     » topic index » view message » categorize

29. Re: Phix+EuGTK

irv said...

When or how is it called?

The private destroy_instance() in builtins\structs.e is called when the reference count of an instance drops to zero, as per delete_routine(), or when delete(instance) is explicitly invoked.
Should a destructor be defined, it is invoked out of courtesy so the app can do any needed housekeeping.

irv said...

Do I have to connect every widget's "destroy" signal to call the deconstructor for that class of widget?

I wouldn't think so, delete_routine() should take care of it, eventually - in some cases an explicit delete(instance) might fix some race condition.
Generally speaking all reference counts are tidied up when a routine returns, but an active loop might squirrel away a reference in a hidden temp (or explicit local var) until the next iteration.
Obviously, should you invoke delete(instance) but keep a reference, that is explicitly, and try to keep on using it, that's on you: sometimes it'll crash, sometimes it'll clobber a (new) sibling.
Here I'm just using it so that get_by_handle() does not return (long-)dead window handles, and in such simple code race conditions would seem quite unlikely.

irv said...

What does it do besides remove the Window instance from the dictionary?

destroy_instance() places the slot in the (also private) instance table on a free list for re-use (ditto now-unused dicts from any dynamic class instances).
If it didn't do that some applications might exhibit a continuous memory leak.

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

30. Re: Phix+EuGTK

Ok, this is strange. I had to devise a test to see what is going on. I created a simple window with a label and a button. Click the button, and a dialog pops up. Here's what happens:

"---------- BEFORE ------------"  
hdict before: {24327152,26215072,26239360,26284368,26325424} 
1 24327152 -> {"struct","Label",11,1} 
2 26215072 -> {"struct","Window",7,1} 
3 26239360 -> {"struct","Button",15,1} 
4 26284368 -> {"struct","ButtonBox",12,1} 
5 26325424 -> {"struct","Box",9,1} 
"----------- DIALOG CREATED ------------" 
hdict before: {24327152,26215072,26239360,26284368,26325424} 
hdict now:    {24327152,24328400,24328800,26215072,26239360,26284368,26325424,26325776,28222240} 
1 24327152 -> {"struct","Label",11,1} 
2 24328400 -> {"struct","Label",11,2} 
3 24328800 -> {"struct","Expander",18,1} 
4 26215072 -> {"struct","Window",7,1} 
5 26239360 -> {"struct","Button",15,1} 
6 26284368 -> {"struct","ButtonBox",12,1} 
7 26325424 -> {"struct","Box",9,1} 
8 26325776 -> {"struct","Box",9,2} 
9 28222240 -> {"struct","MessageDialog",17,1} 
"********** DIALOG CLOSED **************" 
Deleting Label 24328400 {"struct","Label",11,2} 
Deleting Box 26325776 {"struct","Box",9,2} 
Deleting Expander 24328800 {"struct","Expander",18,1} 
Deleting Message Dialog 28222240 {"struct","MessageDialog",17,1} 
"========== AFTER =============" 
hdict at start: {24327152,26215072,26239360,26284368,26325424} 
hdict now:      {24327152,26215072,26239360,26284368,26325424} 
1 24327152 -> {"struct","Label",11,1} 
2 26215072 -> {"struct","Window",7,1} 
3 26239360 -> {"struct","Button",15,1} 
4 26284368 -> {"struct","ButtonBox",12,1} 
5 26325424 -> {"struct","Box",9,1} 

So far, so good, the Before and After match, just as it should be. Now for another run, and a different outcome:

"---------- BEFORE ------------" 
hdict before: {23229424,25060000,25395632,25436496,25477504} 
1 23229424 -> {"struct","Label",11,1} 
2 25060000 -> {"struct","Window",7,1} 
3 25395632 -> {"struct","Box",9,1} 
4 25436496 -> {"struct","ButtonBox",12,1} 
5 25477504 -> {"struct","Button",15,1} 
"----------- DIALOG CREATED ------------" 
hdict before: {23229424,25060000,25395632,25436496,25477504} 
hdict now:    {23229424,23230672,23231072,25060000,25395632,25395984,25436496,25477504,27124512} 
1 23229424 -> {"struct","Label",11,1} 
2 23230672 -> {"struct","Label",11,2} 
3 23231072 -> {"struct","Expander",18,1} 
4 25060000 -> {"struct","Window",7,1} 
5 25395632 -> {"struct","Box",9,1} 
6 25395984 -> {"struct","Box",9,2} 
7 25436496 -> {"struct","ButtonBox",12,1} 
8 25477504 -> {"struct","Button",15,1} 
9 27124512 -> {"struct","MessageDialog",17,1} 
"********** DIALOG CLOSED **************" 
Deleting Label 23230672 {"struct","Label",11,2} 
Deleting Box 25395984 {"struct","Box",9,2} 
Deleting Expander 23231072 {"struct","Expander",18,1} 
Deleting Message Dialog 27124512 {"struct","MessageDialog",17,1} 
"========== AFTER =============" 
hdict at start: {23229424,25060000,25395632,25436496,25477504} 
hdict now:      {23229424,25060000,25395632,25436496,25477504} 
1 23229424 -> {"struct","Label",11,1} 
2 25060000 -> {"struct","Label",11,2} -- NOTE: this is the "handle" previously assigned to the main window. 
3 25395632 -> {"struct","Box",9,1} 
4 25436496 -> {"struct","ButtonBox",12,1} 
5 25477504 -> {"struct","Button",15,1} 
?? where's my window gone ?? 

With no change to my program, every run comes out different, with something not deleted that should have been, and something deleted that should not have been. Sometimes it works properly. Sometimes isn't good enough. What to do?

The delete routine for each of the widgets is similar to this one:

procedure ~Label() --(replacing Label here and below with the appropriate class name) 
 printf(1,"Deleting Label %d ",this.handle) ? this   
 deld(this.handle,hdict) 
 this.destroy() 
end procedure 
new topic     » goto parent     » topic index » view message » categorize

31. Re: Phix+EuGTK

I can't reproduce that here. The first question I would ask is do dict.e and struct.e match.
Below, I've created handles 171717, 181818, 191919, 202020, and deleted the last two, and done a fair bit of manual editing to the ex.err to make things line up nicely.

 C:\Program Files (x86)\Phix\builtins\dict.e: 
    trees[3][01..05] = {171717,{`struct`,`Window`,3,1},0,1,0} 
    trees[3][06..10] = {181818,{`struct`,`Label` ,4,1},1,2,0} 
    trees[3][11..15] = {    16,{`struct`,`Window`,3,2},0,1,0} 
    trees[3][16..20] = {     0,{`struct`,`Label` ,4,2},0,1,0} 
    treenames = {`1`,``,`handle lookup`} 
    freelists = {0,0,11} 
 
 C:\Program Files (x86)\Phix\builtins\structs.e: 
    instances[3] = {2,{{171717,2545,2550},0}} 
    instances[4] = {2,{{181818,2579,2584},0}} 

So, hdict has a freelist of {11,16,0} and the right handles left in place.
instances[3,4] have freelists of {2,0} and the right handles remain.

PS the exact order in which things are created might be helpful, similar to the "Deleting Xxx" messages - should help me reproduce this.
PPS "every run comes out different" is prolly to be expected, as everything comes out in handle order - and will still be so after whatever bug this is gets fixed.

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

32. Re: Phix+EuGTK

Oops, yesterday I said "when the reference count drops to zero, or when delete(instance) is explicitly invoked" - but I forgot that hdict will stop the refcount ever naturally dropping to zero, so you probably will need explicit delete(instance) calls all hooked up.

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

33. Re: Phix+EuGTK

The problem is very random, changing the order that controls are declared sometimes causes this to fail more frequently. The only thing that is repeatable is that the deleted control always takes on the handle of the control that follows it in the dictionary. This can be anything, since the controls don't show up in the dictionary in the same order that they were declared.

In addition, if I find a combination that fails fairly regularly, adding a ? to the code causes the problem to go away. Maybe that's a clue.

I wouldn't be concerned about this, except that if it is indeed changing the "handle" to an object, that could cause bigger problems later on.

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

34. Re: Phix+EuGTK

irv said...

I wouldn't be concerned about this

I am. The good news is I've just reproduced it - and the problem is in either dict.e or structs.e

Update: The problem is somewhere in dict.e

Update: pretty sure this is it, in dict.e routine deleteNode, line 228:

        trees[tid][root+KEY] = key 
--bugfix 29/1/21: 
        trees[tid][root+DATA] = trees[tid][temp+DATA] 

It was copying the key but not the data. I will have to run quite a few more tests, but so far that looks good.

Update: test\t67dicts.exw added: detects it properly if I comment out that fix, no other problems found.

Many thanks for finding a very nasty bug in a critical component!

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

35. Re: Phix+EuGTK

Great! That seems to have fixed the problem. Testing will continue.

Deleting Label 13712400 {"struct","Label",11,2} 
Deleting Box 15661136 {"struct","Box",9,2} 
Deleting Expander 13712800 {"struct","Expander",18,1} 
Deleting Message Dialog 17613600 {"struct","MessageDialog",17,1} 
"========== AFTER =============" 
hdict at start: {13710320,15615152,15659376,15991168,15991616,15992064,17384032} 
hdict now:      {13710320,15615152,15659376,15991168,15991616,15992064,17384032} 
1 13710320 -> {"struct","Label",11,1} 
2 15615152 -> {"struct","ButtonBox",12,1} 
3 15659376 -> {"struct","Box",9,1} 
4 15991168 -> {"struct","Button",15,1} 
5 15991616 -> {"struct","Button",15,2} 
6 15992064 -> {"struct","Button",15,3} 
7 17384032 -> {"struct","Window",7,1} 
All correct, and seems repeatable regardless of the order in which controls are added.


-- just out of curiosity, I then add: 
? bx ? ans ? dlg 

Results:

{"struct","Box",9,2} 
{"struct","Label",11,2} 
{"struct","MessageDialog",17,1} 
If I try to access the handle of a deleted control:

? bx.handle 

I get a crash (as expected), since it's been deleted:

/usr/local/bin/builtins/structs.e:1110 in function fetch_field() 
type check failure, s is {"struct","Box",9,2} 
    s = {"struct","Box",9,2} 
    field_name = "handle" 
    context = 0 
    sdx = <novalue> 
    stype = <novalue> 
    fdx = <novalue> 
    cdii = <novalue> 
    getter = <novalue> 
    flags = <novalue> 
    getfn = <novalue> 
    maxp = <novalue> 
    minp = <novalue> 

It seems possible that some code might someday try to access a field of a deleted instance, and crash. Is there a better way to prevent a crash than using, for example:

try 
  ? dlg.handle 
catch e 
  ? "It's dead, Jim" 
end try 
new topic     » goto parent     » topic index » view message » categorize

36. Re: Phix+EuGTK

Yes, it's probably better as well as easier and faster to just test it first:

--if Box(bx) then       -- best, if/when you can 
--if struct(bx) then    -- or Widget or whatever (see note below) 
if is_struct(bx,0) then -- (equivalent to struct) 
--if is_struct(bx,Box) then -- same as Box() 
--if is_struct(bx,routine_id("Box")) then -- ditto 
    ?bx.handle 
end if 

However, I think you know this, there will (probably) come a day when it succeeds, because {"struct","Box",5,2} or close enough has been re-created for something else, and you or someone else will sorely wish it hadn't.
Actually, though, there is something I could and should do to improve upon that: implement a new builtin (and use it in is_struct(), and therefore in both struct() and Box() too)
[DONE, ready for next release]

bool bRes = still_has_delete_routine(object x) 

I'd still recommend doing everything you can to nullify all delete()'d instance refs/vars, tho.

Update: Added still_has_delete_routine(), used it, and fixed the glitch in using struct() ready for the next release.
I also cleaned up the original comments and added a couple extra options for testing.

Should you hit that struct() bug, the fix is in pmain.e, DoSubScripts() line 6495:

--31/1/21: 
--      integer stype = symtab[N][S_ltype] 
        integer stype = symtab[N][S_vtype] 
new topic     » goto parent     » topic index » view message » categorize

Search



Quick Links

User menu

Not signed in.

Misc Menu