In Arif Isaq booklet there is an example where the gen_server behaviour is replace with wx_object. I'm trying to run this example, but somehow or other the message I send to the processes
Eshell V13.1.4 (abort with ^G)
1> c(wo_player).
{ok,wo_player}
2> arbiter:start_link().
{ok,<0.93.0>}
3> gen_server:call(arbiter, {reset, 42}).
ok
4> gen_server:call(player1, move).
ok
do not start the timer.
I'm running the example on Debian buster
erlang@4diac:~$ pkg-config --modversion gtk+-3.0
3.24.5
erlang@4diac:~$ cat /usr/lib/erlang/releases/RELEASES
%% coding: utf-8
[{release,"Erlang/OTP","25","13.1.4",
[{kernel,"8.5.3","/usr/lib/erlang/lib/kernel-8.5.3"},
{stdlib,"4.2","/usr/lib/erlang/lib/stdlib-4.2"},
{sasl,"4.2","/usr/lib/erlang/lib/sasl-4.2"}],
permanent}].
erlang@4diac:~$
The wxWidgets are packaged into the gtk 3.24.5 version indicated above
Here are the Erlang modules for the example
-module(wo_arbiter).
-include_lib("wx/include/wx.hrl").
-behaviour(wx_object).
-export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
handle_event/2, terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
-record(state, {}).
start_link() ->
wx_object:start_link({local, ?SERVER}, ?MODULE, [], []).
init([]) ->
wx:new(),
%% Env = wx:get_env(),
%% build and layout the GUI components
Frame = wxFrame:new(wx:null(), 1, "Countdown"),
Player1 = wo_player:start_link(player1, Frame, ?SERVER),
Player2 = wo_player:start_link(player2, Frame, ?SERVER),
%% Player1 = gen_server:call(player1, get_panel),
%% Player2 = gen_server:call(player2, get_panel),
MainSizer = wxBoxSizer:new(?wxHORIZONTAL),
wxSizer:add(MainSizer, Player1, [{proportion, 1}, {flag, ?wxALL}, {border,5}]),
wxSizer:add(MainSizer, Player2, [{proportion, 1}, {flag, ?wxALL}, {border,5}]),
wxWindow:setSizer(Frame, MainSizer),
wxSizer:setSizeHints(MainSizer, Frame),
wxWindow:setMinSize(Frame, wxWindow:getSize(Frame)),
wxFrame:connect(Frame, close_window),
wxFrame:show(Frame),
{Frame, #state{}}.
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info({reset, N}, State) ->
player1 ! {reset, N},
player2 ! {reset, N},
{noreply, State};
handle_info({moved, player1}, State) ->
player2 ! move,
{noreply, State};
handle_info({moved, player2}, State) ->
player1 ! move,
{noreply, State};
handle_info({ilose, player1}, State) ->
player2 ! youwin,
{noreply, State};
handle_info({ilose, player2}, State) ->
player1 ! youwin,
{noreply, State};
handle_info(Msg, State) ->
io:format("frame got unexpected message ~p~n", [Msg]),
{noreply, State}.
handle_event(#wx{event = #wxClose{}}, State) ->
{stop, normal, State}.
terminate(_Reason, _State) ->
%% sys:terminate(player1, Reason),
%% sys:terminate(player2, Reason),
wx:destroy(),
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
-module(wo_player).
-behaviour(wx_object).
-export([start_link/3]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
handle_event/2, terminate/2, code_change/3]).
-include_lib("wx/include/wx.hrl").
-record(state, {panel, counter, button, tref, whoami, arbiter}).
start_link(Name, Frame, Arbiter) ->
wx_object:start_link({local, Name}, ?MODULE, [Name, Frame, Arbiter], []).
init([Name, Frame, Arbiter]) ->
%% wx:set_env(Env),
Panel = wxPanel:new(Frame),
%% build and layout the GUI components
Label = wxStaticText:new(Panel, ?wxID_ANY, "Seconds remaining", [{style, ?wxALIGN_RIGHT}]),
wxStaticText:wrap(Label,100),
Counter = wxTextCtrl:new(Panel, ?wxID_ANY, [{value, "42"}, {style, ?wxTE_RIGHT}]),
Font = wxFont:new(42, ?wxFONTFAMILY_DEFAULT, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_BOLD),
wxTextCtrl:setFont(Counter, Font),
wxTextCtrl:setEditable(Counter, false),
Button = wxButton:new(Panel, ?wxID_ANY, [{label, "Moved"}]),
wxButton:disable(Button),
CounterSizer = wxBoxSizer:new(?wxHORIZONTAL),
wxSizer:add(CounterSizer, Label, [{flag, ?wxALL bor ?wxALIGN_CENTRE},{border, 5}]),
wxSizer:add(CounterSizer, Counter, [{proportion,1}, {flag, ?wxEXPAND bor ?wxALL}, {border, 5}]),
MainSizer = wxBoxSizer:new(?wxVERTICAL),
wxSizer:add(MainSizer, CounterSizer, [{flag, ?wxEXPAND}]),
wxSizer:add(MainSizer, Button, [{flag, ?wxEXPAND bor ?wxALL}, {border,5}]),
wxWindow:setSizer(Panel, MainSizer),
wxSizer:setSizeHints(MainSizer, Panel),
wxWindow:setMinSize(Panel, wxWindow:getSize(Panel)),
wxButton:connect(Button, command_button_clicked),
{Panel, #state{panel = Panel,
counter = Counter,
button = Button,
whoami = Name,
arbiter = Arbiter}}.
handle_call(get_panel, _From, #state{panel = Panel} = State) ->
{reply, Panel, State};
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info({reset,N}, #state{counter = Counter, button = Button} = State) ->
wxTextCtrl:setValue(Counter, integer_to_list(N)),
wxButton:disable(Button),
{noreply, State};
handle_info(youwin, #state{counter = Counter} = State) ->
wxTextCtrl:setValue(Counter, "win"),
{noreply, State};
handle_info(move, #state{button = Button} = State) ->
io:format("Got move event", []),
wxButton:enable(Button),
TRef = erlang:send_after(1000, self(), update_gui),
{noreply, State#state{tref = TRef}};
handle_info(update_gui, #state{button = Button, counter = Counter, whoami = Name, arbiter = Arbiter} = State) ->
Value = wxTextCtrl:getValue(Counter),
case list_to_integer(Value) of
1 ->
wxTextCtrl:setValue(Counter, "0"),
wxButton:disable(Button),
Arbiter ! {ilose, Name},
{noreply, State};
N ->
wxTextCtrl:setValue(Counter, integer_to_list(N-1)),
TRef = erlang:send_after(1000, self(), update_gui),
{noreply, State#state{tref = TRef}}
end.
handle_event(#wx{obj = Button, event = #wxCommand{type = command_button_clicked}},
#state{tref = TRef, whoami = Name, arbiter = Arbiter} = State) ->
erlang:cancel_timer(TRef),
wxButton:disable(Button),
Arbiter ! {moved, Name},
{noreply, State#state{tref = undefined}}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
Would the problem be related to the version of wxWidgets included in the gtk 3.24.5 version I installed as a package?
My understanding is that the binding between wxErlang and wxWidgets is established by a port as soon as wx:new() is called. Transactions of messages through ports cannot completely deterministic to ensure soft real-time.
Erlang/OPT behaviours ensure soft real-time by maintaining a consistent time interval, depending on the message, when retrieving messages from mailboxes, and because they are handled in same order in which they are received. To aid in this, messages are retrieved using the handle_call/3 and handle_cast/2 callback functions whenever a gen_server:call/2 or a gen_server:cast/2 client calls are made. If a message can not be managed it results in a runtime error. To avoid runtime errors Erlang/OTP offers a catch-all handle_info(_Msg, LoopData) callback. According to Erlang/OTP dealing with calls and casts, all requests should originate form the behaviour callback module and any unknown messages should be caught in the early stages of testing.
GUI interfaces need not be soft real-time as they are operated on by the user. The handle_info/2 callback can be used similar to handle_call/3 as shown below.
To invoke these callbacks Erlang messages must be used and not gen_server:call/2 calls as shown below.
If you use
instead you will get the reply
because you be invoking the handle_call/3 callback