1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2008-2017. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%%-------------------------------------------------------------------
20%%% File    : wx_object.erl
21%%% Author  : Dan Gudmundsson <dan.gudmundsson@ericsson.com>
22%%% Description : Frame work for erlang sub-classes.
23%%%
24%%% Created : 25 Nov 2008 by Dan Gudmundsson <dan.gudmundsson@ericsson.com>
25%%%-------------------------------------------------------------------
26%%
27%% @doc wx_object - Generic wx object behaviour
28%%
29%% This is a behaviour module that can be used for "sub classing"
30%% wx objects. It works like a regular gen_server module and creates
31%% a server per object.
32%%
33%% NOTE: Currently no form of inheritance is implemented.
34%%
35%%
36%% The user module should export:
37%%
38%%   init(Args) should return <br/>
39%%     {wxObject, State} | {wxObject, State, Timeout} |
40%%         ignore | {stop, Reason}
41%%
42%%   Asynchronous window event handling: <br/>
43%%   handle_event(#wx{}, State)  should return <br/>
44%%    {noreply, State} | {noreply, State, Timeout} | {stop, Reason, State}
45%%
46%% The user module can export the following callback functions:
47%%
48%%   handle_call(Msg, {From, Tag}, State) should return <br/>
49%%    {reply, Reply, State} | {reply, Reply, State, Timeout} |
50%%        {noreply, State} | {noreply, State, Timeout} |
51%%        {stop, Reason, Reply, State}
52%%
53%%   handle_cast(Msg, State) should return <br/>
54%%    {noreply, State} | {noreply, State, Timeout} |
55%%        {stop, Reason, State}
56%%
57%% If the above are not exported but called, the wx_object process will crash.
58%% The user module can also export:
59%%
60%%   Info is message e.g. {'EXIT', P, R}, {nodedown, N}, ...  <br/>
61%%   handle_info(Info, State)  should return , ...  <br/>
62%%    {noreply, State} | {noreply, State, Timeout} | {stop, Reason, State}
63%%
64%% If a message is sent to the wx_object process when handle_info is not
65%% exported, the message will be dropped and ignored.
66%%
67%%   When stop is returned in one of the functions above with Reason =
68%% normal | shutdown | Term, terminate(State) is called. It lets the
69%% user module clean up, it is always called when server terminates or
70%% when wx_object() in the driver is deleted. If the Parent process
71%% terminates the Module:terminate/2 function is called. <br/>
72%% terminate(Reason, State)
73%%
74%%
75%% Example:
76%%
77%% ```
78%% -module(myDialog).
79%% -export([new/2, show/1, destroy/1]).  %% API
80%% -export([init/1, handle_call/3, handle_event/2,
81%%          handle_info/2, code_change/3, terminate/2]).
82%%          new/2, showModal/1, destroy/1]).  %% Callbacks
83%%
84%% %% Client API
85%% new(Parent, Msg) ->
86%%    wx_object:start(?MODULE, [Parent,Id], []).
87%%
88%% show(Dialog) ->
89%%    wx_object:call(Dialog, show_modal).
90%%
91%% destroy(Dialog) ->
92%%    wx_object:call(Dialog, destroy).
93%%
94%% %% Server Implementation ala gen_server
95%% init([Parent, Str]) ->
96%%    Dialog = wxDialog:new(Parent, 42, "Testing", []),
97%%    ...
98%%    wxDialog:connect(Dialog, command_button_clicked),
99%%    {Dialog, MyState}.
100%%
101%% handle_call(show, _From, State) ->
102%%    wxDialog:show(State#state.win),
103%%    {reply, ok, State};
104%% ...
105%% handle_event(#wx{}, State) ->
106%%    io:format("Users clicked button~n",[]),
107%%    {noreply, State};
108%% ...
109%% '''
110
111-module(wx_object).
112-include("wxe.hrl").
113-include("../include/wx.hrl").
114
115%% API
116-export([start/3, start/4,
117	 start_link/3, start_link/4,
118	 stop/1, stop/3,
119	 call/2, call/3,
120         send_request/2, wait_response/1, wait_response/2, check_response/2,
121	 cast/2,
122	 reply/2,
123	 get_pid/1,
124	 set_pid/2
125	]).
126
127-type request_id() :: term().
128-type server_ref() :: Obj::wx:wx_object()|atom()|pid().
129
130%% -export([behaviour_info/1]).
131-callback init(Args :: term()) ->
132    {#wx_ref{}, State :: term()} | {#wx_ref{}, State :: term(), timeout() | 'hibernate'} |
133    {'stop', Reason :: term()} | 'ignore'.
134-callback handle_event(Request :: #wx{}, State :: term()) ->
135    {'noreply', NewState :: term()} |
136    {'noreply', NewState :: term(), timeout() | 'hibernate'} |
137    {'stop', Reason :: term(), NewState :: term()}.
138-callback handle_call(Request :: term(), From :: {pid(), Tag :: term()},
139                      State :: term()) ->
140    {'reply', Reply :: term(), NewState :: term()} |
141    {'reply', Reply :: term(), NewState :: term(), timeout() | 'hibernate'} |
142    {'noreply', NewState :: term()} |
143    {'noreply', NewState :: term(), timeout() | 'hibernate'} |
144    {'stop', Reason :: term(), Reply :: term(), NewState :: term()} |
145    {'stop', Reason :: term(), NewState :: term()}.
146-callback handle_cast(Request :: term(), State :: term()) ->
147    {'noreply', NewState :: term()} |
148    {'noreply', NewState :: term(), timeout() | 'hibernate'} |
149    {'stop', Reason :: term(), NewState :: term()}.
150-callback handle_info(Info :: timeout() | term(), State :: term()) ->
151    {'noreply', NewState :: term()} |
152    {'noreply', NewState :: term(), timeout() | 'hibernate'} |
153    {'stop', Reason :: term(), NewState :: term()}.
154-callback handle_sync_event(Request :: #wx{}, Ref :: #wx_ref{}, State :: term()) ->
155    ok.
156-callback terminate(Reason :: ('normal' | 'shutdown' | {'shutdown', term()} |
157                               term()),
158                    State :: term()) ->
159    term().
160-callback code_change(OldVsn :: (term() | {'down', term()}), State :: term(),
161                      Extra :: term()) ->
162    {'ok', NewState :: term()} | {'error', Reason :: term()}.
163
164-optional_callbacks(
165    [handle_call/3, handle_cast/2, handle_info/2,
166     handle_sync_event/3, terminate/2, code_change/3]).
167
168%% System exports
169-export([system_continue/3,
170	 system_terminate/4,
171	 system_code_change/4,
172	 format_status/2]).
173
174%% Internal exports
175-export([init_it/6]).
176
177-import(error_logger, [format/2]).
178
179%%%=========================================================================
180%%%  API
181%%%=========================================================================
182%% @hidden
183%% behaviour_info(callbacks) ->
184%%     [{init,1},
185%%      {handle_call,3},
186%%      {handle_info,2},
187%%      {handle_event,2},
188%%      {terminate,2},
189%%      {code_change,3}];
190%% behaviour_info(_Other) ->
191%%     undefined.
192
193
194%%  -----------------------------------------------------------------
195%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
196%% new process.
197-spec start(Mod, Args, Options) -> wxWindow:wxWindow() | {error, term()} when
198      Mod::atom(),
199      Args::term(),
200      Flag::trace | log | {logfile, string()} | statistics | debug,
201      Options::[{timeout, timeout()} | {debug, [Flag]}].
202start(Mod, Args, Options) ->
203    gen_response(gen:start(?MODULE, nolink, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
204
205%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
206%% new process.
207-spec start(Name, Mod, Args, Options) -> wxWindow:wxWindow()  | {error, term()} when
208      Name::{local, atom()},
209      Mod::atom(),
210      Args::term(),
211      Flag::trace | log | {logfile, string()} | statistics | debug,
212      Options::[{timeout, timeout()} | {debug, [Flag]}].
213start(Name, Mod, Args, Options) ->
214    gen_response(gen:start(?MODULE, nolink, Name, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
215
216%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
217%% new process.
218-spec start_link(Mod, Args, Options) -> wxWindow:wxWindow()  | {error, term()} when
219      Mod::atom(),
220      Args::term(),
221      Flag::trace | log | {logfile, string()} | statistics | debug,
222      Options::[{timeout, timeout()} | {debug, [Flag]}].
223start_link(Mod, Args, Options) ->
224    gen_response(gen:start(?MODULE, link, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
225
226%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
227%% new process.
228-spec start_link(Name, Mod, Args, Options) -> wxWindow:wxWindow()  | {error, term()} when
229      Name::{local, atom()},
230      Mod::atom(),
231      Args::term(),
232      Flag::trace | log | {logfile, string()} | statistics | debug,
233      Options::[{timeout, timeout()} | {debug, [Flag]}].
234start_link(Name, Mod, Args, Options) ->
235    gen_response(gen:start(?MODULE, link, Name, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
236
237gen_response({ok, Pid}) ->
238    receive {ack, Pid, Ref = #wx_ref{}} -> Ref end;
239gen_response(Reply) ->
240    Reply.
241
242%% @doc Stops a generic wx_object server with reason 'normal'.
243%% Invokes terminate(Reason,State) in the server. The call waits until
244%% the process is terminated. If the process does not exist, an
245%% exception is raised.
246-spec stop(Obj) -> ok when
247      Obj::wx:wx_object()|atom()|pid().
248stop(Ref = #wx_ref{state=Pid}) when is_pid(Pid) ->
249    try
250	gen:stop(Pid)
251    catch _:ExitReason ->
252	    erlang:error({ExitReason, {?MODULE, stop, [Ref]}})
253    end;
254stop(Name) when is_atom(Name) orelse is_pid(Name) ->
255    try
256	gen:stop(Name)
257    catch _:ExitReason ->
258	    erlang:error({ExitReason, {?MODULE, stop, [Name]}})
259    end.
260
261%% @doc Stops a generic wx_object server with the given Reason.
262%% Invokes terminate(Reason,State) in the server. The call waits until
263%% the process is terminated. If the call times out, or if the process
264%% does not exist, an exception is raised.
265-spec stop(Obj, Reason, Timeout) -> ok when
266      Obj::wx:wx_object()|atom()|pid(),
267      Reason::term(),
268      Timeout::timeout().
269stop(Ref = #wx_ref{state=Pid}, Reason, Timeout) when is_pid(Pid) ->
270    try
271	gen:stop(Pid, Reason, Timeout)
272    catch _:ExitReason ->
273	    erlang:error({ExitReason, {?MODULE, stop, [Ref, Reason, Timeout]}})
274    end;
275stop(Name, Reason, Timeout) when is_atom(Name) orelse is_pid(Name) ->
276    try
277	gen:stop(Name, Reason, Timeout)
278    catch _:ExitReason ->
279	    erlang:error({ExitReason, {?MODULE, stop, [Name, Reason, Timeout]}})
280    end.
281
282%% @doc Make a call to a wx_object server.
283%% The call waits until it gets a result.
284%% Invokes handle_call(Request, From, State) in the server
285-spec call(Obj, Request) -> term() when
286      Obj::wx:wx_object()|atom()|pid(),
287      Request::term().
288call(Ref = #wx_ref{state=Pid}, Request) when is_pid(Pid) ->
289    try
290	{ok,Res} = gen:call(Pid, '$gen_call', Request, infinity),
291	Res
292    catch _:Reason ->
293	    erlang:error({Reason, {?MODULE, call, [Ref, Request]}})
294    end;
295call(Name, Request) when is_atom(Name)  orelse is_pid(Name) ->
296    try
297        {ok,Res} = gen:call(Name, '$gen_call', Request, infinity),
298        Res
299    catch _:Reason ->
300            erlang:error({Reason, {?MODULE, call, [Name, Request]}})
301    end.
302
303%% @doc Make a call to a wx_object server with a timeout.
304%% Invokes handle_call(Request, From, State) in server
305-spec call(Obj, Request, Timeout) -> term() when
306      Obj::wx:wx_object()|atom()|pid(),
307      Request::term(),
308      Timeout::integer().
309call(Ref = #wx_ref{state=Pid}, Request, Timeout) when is_pid(Pid) ->
310    try
311	{ok,Res} = gen:call(Pid, '$gen_call', Request, Timeout),
312	Res
313    catch _:Reason ->
314	    erlang:error({Reason, {?MODULE, call, [Ref, Request, Timeout]}})
315    end;
316call(Name, Request, Timeout) when is_atom(Name) orelse is_pid(Name) ->
317    try
318        {ok,Res} = gen:call(Name, '$gen_call', Request, Timeout),
319        Res
320    catch _:Reason ->
321            erlang:error({Reason, {?MODULE, call, [Name, Request, Timeout]}})
322    end.
323
324%% @doc Make an send_request to a generic server.
325%% and return a RequestId which can/should be used with wait_response/[1|2].
326%% Invokes handle_call(Request, From, State) in server.
327-spec send_request(Obj, Request::term()) -> request_id() when
328      Obj::wx:wx_object()|atom()|pid().
329send_request(#wx_ref{state=Pid}, Request) ->
330    gen:send_request(Pid, '$gen_call', Request);
331send_request(Pid, Request) when is_atom(Pid) orelse is_pid(Pid) ->
332    gen:send_request(Pid, '$gen_call', Request).
333
334%% @doc Wait infinitely for a reply from a generic server.
335-spec wait_response(RequestId::request_id()) ->
336        {reply, Reply::term()} | {error, {term(), server_ref()}}.
337wait_response(RequestId) ->
338    gen:wait_response(RequestId, infinity).
339
340%% @doc Wait 'timeout' for a reply from a generic server.
341-spec wait_response(Key::request_id(), timeout()) ->
342        {reply, Reply::term()} | 'timeout' | {error, {term(), server_ref()}}.
343wait_response(RequestId, Timeout) ->
344    gen:wait_response(RequestId, Timeout).
345
346%% @doc Check if a received message was a reply to a RequestId
347-spec check_response(Msg::term(), Key::request_id()) ->
348        {reply, Reply::term()} | 'false' | {error, {term(), server_ref()}}.
349check_response(Msg, RequestId) ->
350    gen:check_response(Msg, RequestId).
351
352%% @doc Make a cast to a wx_object server.
353%% Invokes handle_cast(Request, State) in the server
354-spec cast(Obj, Request) -> ok when
355      Obj::wx:wx_object()|atom()|pid(),
356      Request::term().
357cast(#wx_ref{state=Pid}, Request) when is_pid(Pid) ->
358    Pid ! {'$gen_cast',Request},
359    ok;
360cast(Name, Request) when is_atom(Name) orelse is_pid(Name) ->
361    Name ! {'$gen_cast',Request},
362    ok.
363
364%% @doc Get the pid of the object handle.
365-spec get_pid(Obj) -> pid() when
366      Obj::wx:wx_object()|atom()|pid().
367get_pid(#wx_ref{state=Pid}) when is_pid(Pid) ->
368    Pid.
369
370%% @doc Sets the controlling process of the object handle.
371-spec set_pid(Obj, pid()) -> wx:wx_object() when
372      Obj::wx:wx_object()|atom()|pid().
373set_pid(#wx_ref{}=R, Pid) when is_pid(Pid) ->
374    R#wx_ref{state=Pid}.
375
376%% -----------------------------------------------------------------
377%% Send a reply to the client.
378%% -----------------------------------------------------------------
379%% @doc Get the pid of the object handle.
380-spec reply({pid(), Tag::term()}, Reply::term()) -> pid().
381reply({To, Tag}, Reply) ->
382    catch To ! {Tag, Reply}.
383
384%%%========================================================================
385%%% Gen-callback functions
386%%%========================================================================
387%%% ---------------------------------------------------
388%%% Initiate the new process.
389%%% Register the name using the Rfunc function
390%%% Calls the Mod:init/Args function.
391%%% Finally an acknowledge is sent to Parent and the main
392%%% loop is entered.
393%%% ---------------------------------------------------
394%% @hidden
395init_it(Starter, self, Name, Mod, Args, Options) ->
396    init_it(Starter, self(), Name, Mod, Args, Options);
397init_it(Starter, Parent, Name, Mod, Args, [WxEnv|Options]) ->
398    case WxEnv of
399	undefined -> ok;
400	_ -> wx:set_env(WxEnv)
401    end,
402    put('_wx_object_', {Mod,'_wx_init_'}),
403    Debug = debug_options(Name, Options),
404    case catch Mod:init(Args) of
405	{#wx_ref{} = Ref, State} ->
406	    init_it2(Ref, Starter, Parent, Name, State, Mod, infinity, Debug);
407	{#wx_ref{} = Ref, State, Timeout} ->
408	    init_it2(Ref, Starter, Parent, Name, State, Mod, Timeout, Debug);
409	{stop, Reason} ->
410	    proc_lib:init_ack(Starter, {error, Reason}),
411	    exit(Reason);
412	ignore ->
413	    proc_lib:init_ack(Starter, ignore),
414	    exit(normal);
415	{'EXIT', Reason} ->
416	    proc_lib:init_ack(Starter, {error, Reason}),
417	    exit(Reason);
418	Else ->
419	    Error = {bad_return_value, Else},
420	    proc_lib:init_ack(Starter, {error, Error}),
421	    exit(Error)
422    end.
423%% @hidden
424init_it2(Ref, Starter, Parent, Name, State, Mod, Timeout, Debug) ->
425    ok = wxe_util:register_pid(Ref),
426    case ?CLASS_T(Ref#wx_ref.type, wxWindow) of
427	false ->
428	    Reason = {Ref, "not a wxWindow subclass"},
429	    proc_lib:init_ack(Starter, {error, Reason}),
430	    exit(Reason);
431	true ->
432	    proc_lib:init_ack(Starter, {ok, self()}),
433	    proc_lib:init_ack(Starter, Ref#wx_ref{state=self()}),
434	    loop(Parent, Name, State, Mod, Timeout, Debug)
435    end.
436
437%%%========================================================================
438%%% Internal functions
439%%%========================================================================
440%%% ---------------------------------------------------
441%%% The MAIN loop.
442%%% ---------------------------------------------------
443%% @hidden
444loop(Parent, Name, State, Mod, Time, Debug) ->
445    put('_wx_object_', {Mod,State}),
446    Msg = receive
447	      Input ->
448		  Input
449	  after Time ->
450		  timeout
451	  end,
452    case Msg of
453	{system, From, Req} ->
454	    sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
455				  [Name, State, Mod, Time]);
456	{'EXIT', Parent, Reason} ->
457	    terminate(Reason, Name, Msg, Mod, State, Debug);
458	{'_wxe_destroy_', _Me} ->
459	    terminate(wx_deleted, Name, Msg, Mod, State, Debug);
460	_Msg when Debug =:= [] ->
461	    handle_msg(Msg, Parent, Name, State, Mod);
462	_Msg ->
463	    Debug1 = sys:handle_debug(Debug, fun print_event/3,
464				      Name, {in, Msg}),
465	    handle_msg(Msg, Parent, Name, State, Mod, Debug1)
466    end.
467
468%%% ---------------------------------------------------
469%%% Message handling functions
470%%% ---------------------------------------------------
471%% @hidden
472dispatch({'$gen_cast', Msg}, Mod, State) ->
473    Mod:handle_cast(Msg, State);
474dispatch(Msg = #wx{}, Mod, State) ->
475    Mod:handle_event(Msg, State);
476dispatch(Info, Mod, State) ->
477    Mod:handle_info(Info, State).
478
479%% @hidden
480handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod) ->
481    case catch Mod:handle_call(Msg, From, State) of
482	{reply, Reply, NState} ->
483	    reply(From, Reply),
484	    loop(Parent, Name, NState, Mod, infinity, []);
485	{reply, Reply, NState, Time1} ->
486	    reply(From, Reply),
487	    loop(Parent, Name, NState, Mod, Time1, []);
488	{noreply, NState} ->
489	    loop(Parent, Name, NState, Mod, infinity, []);
490	{noreply, NState, Time1} ->
491	    loop(Parent, Name, NState, Mod, Time1, []);
492	{stop, Reason, Reply, NState} ->
493	    {'EXIT', R} =
494		(catch terminate(Reason, Name, Msg, Mod, NState, [])),
495	    reply(From, Reply),
496	    exit(R);
497	Other -> handle_common_reply(Other, Name, Msg, Mod, State, [])
498    end;
499handle_msg(Msg, Parent, Name, State, Mod) ->
500    case catch dispatch(Msg, Mod, State) of
501        {'EXIT', {undef, [{Mod, handle_info, [_,_], _}|_]}} ->
502            handle_no_reply({noreply, State}, Parent, Name, Msg, Mod, State, []);
503        Reply ->
504            handle_no_reply(Reply, Parent, Name, Msg, Mod, State, [])
505    end.
506
507%% @hidden
508handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod, Debug) ->
509    case catch Mod:handle_call(Msg, From, State) of
510	{reply, Reply, NState} ->
511	    Debug1 = reply(Name, From, Reply, NState, Debug),
512	    loop(Parent, Name, NState, Mod, infinity, Debug1);
513	{reply, Reply, NState, Time1} ->
514	    Debug1 = reply(Name, From, Reply, NState, Debug),
515	    loop(Parent, Name, NState, Mod, Time1, Debug1);
516	{noreply, NState} ->
517	    Debug1 = sys:handle_debug(Debug, fun print_event/3,
518				      Name, {noreply, NState}),
519	    loop(Parent, Name, NState, Mod, infinity, Debug1);
520	{noreply, NState, Time1} ->
521	    Debug1 = sys:handle_debug(Debug, fun print_event/3,
522				      Name, {noreply, NState}),
523	    loop(Parent, Name, NState, Mod, Time1, Debug1);
524	{stop, Reason, Reply, NState} ->
525	    {'EXIT', R} =
526		(catch terminate(Reason, Name, Msg, Mod, NState, Debug)),
527	    _ = reply(Name, From, Reply, NState, Debug),
528	    exit(R);
529	Other ->
530	    handle_common_reply(Other, Name, Msg, Mod, State, Debug)
531    end;
532handle_msg(Msg, Parent, Name, State, Mod, Debug) ->
533    Reply = (catch dispatch(Msg, Mod, State)),
534    handle_no_reply(Reply, Parent, Name, Msg, Mod, State, Debug).
535%% @hidden
536handle_no_reply({noreply, NState}, Parent, Name, _Msg, Mod, _State, []) ->
537    loop(Parent, Name, NState, Mod, infinity, []);
538handle_no_reply({noreply, NState, Time1}, Parent, Name, _Msg, Mod, _State, []) ->
539    loop(Parent, Name, NState, Mod, Time1, []);
540handle_no_reply({noreply, NState}, Parent, Name, _Msg, Mod, _State, Debug) ->
541    Debug1 = sys:handle_debug(Debug, fun print_event/3,
542			      Name, {noreply, NState}),
543    loop(Parent, Name, NState, Mod, infinity, Debug1);
544handle_no_reply({noreply, NState, Time1}, Parent, Name, _Msg, Mod, _State, Debug) ->
545    Debug1 = sys:handle_debug(Debug, fun print_event/3,
546			      Name, {noreply, NState}),
547    loop(Parent, Name, NState, Mod, Time1, Debug1);
548handle_no_reply(Reply, _Parent, Name, Msg, Mod, State, Debug) ->
549    handle_common_reply(Reply, Name, Msg, Mod, State,Debug).
550
551%% @hidden
552-spec handle_common_reply(_, _, _, _, _, _) -> no_return().
553handle_common_reply(Reply, Name, Msg, Mod, State, Debug) ->
554    case Reply of
555	{stop, Reason, NState} ->
556	    terminate(Reason, Name, Msg, Mod, NState, Debug);
557	{'EXIT', What} ->
558	    terminate(What, Name, Msg, Mod, State, Debug);
559	_ ->
560	    terminate({bad_return_value, Reply}, Name, Msg, Mod, State, Debug)
561    end.
562
563%% @hidden
564reply(Name, {To, Tag}, Reply, State, Debug) ->
565    reply({To, Tag}, Reply),
566    sys:handle_debug(Debug, fun print_event/3,
567		     Name, {out, Reply, To, State}).
568
569
570%%-----------------------------------------------------------------
571%% Callback functions for system messages handling.
572%%-----------------------------------------------------------------
573%% @hidden
574system_continue(Parent, Debug, [Name, State, Mod, Time]) ->
575    loop(Parent, Name, State, Mod, Time, Debug).
576
577%% @hidden
578-spec system_terminate(_, _, _, [_]) -> no_return().
579system_terminate(Reason, _Parent, Debug, [Name, State, Mod, _Time]) ->
580    terminate(Reason, Name, [], Mod, State, Debug).
581
582%% @hidden
583system_code_change([Name, State, Mod, Time], _Module, OldVsn, Extra) ->
584    case catch Mod:code_change(OldVsn, State, Extra) of
585        {ok, NewState} -> {ok, [Name, NewState, Mod, Time]};
586        Else -> Else
587    end.
588
589%%-----------------------------------------------------------------
590%% Format debug messages.  Print them as the call-back module sees
591%% them, not as the real erlang messages.  Use trace for that.
592%%-----------------------------------------------------------------
593print_event(Dev, {in, Msg}, Name) ->
594    case Msg of
595	{'$gen_call', {From, _Tag}, Call} ->
596	    io:format(Dev, "*DBG* ~tp got call ~tp from ~w~n",
597		      [Name, Call, From]);
598	{'$gen_cast', Cast} ->
599	    io:format(Dev, "*DBG* ~tp got cast ~tp~n",
600		      [Name, Cast]);
601	_ ->
602	    io:format(Dev, "*DBG* ~tp got ~tp~n", [Name, Msg])
603    end;
604print_event(Dev, {out, Msg, To, State}, Name) ->
605    io:format(Dev, "*DBG* ~tp sent ~tp to ~w, new state ~tp~n",
606	      [Name, Msg, To, State]);
607print_event(Dev, {noreply, State}, Name) ->
608    io:format(Dev, "*DBG* ~tp new state ~tp~n", [Name, State]);
609print_event(Dev, Event, Name) ->
610    io:format(Dev, "*DBG* ~tp dbg  ~tp~n", [Name, Event]).
611
612%%% ---------------------------------------------------
613%%% Terminate the server.
614%%% ---------------------------------------------------
615%% @hidden
616terminate(Reason, Name, Msg, Mod, State, Debug) ->
617    case try_terminate(Mod, Reason, State) of
618	{'EXIT', R} ->
619	    error_info(R, Name, Msg, State, Debug),
620	    exit(R);
621	_ ->
622	    case Reason of
623		normal ->
624		    exit(normal);
625		shutdown ->
626		    exit(shutdown);
627		wx_deleted ->
628		    exit(normal);
629		_ ->
630		    error_info(Reason, Name, Msg, State, Debug),
631		    exit(Reason)
632	    end
633    end.
634
635try_terminate(Mod, Reason, State) ->
636    case erlang:function_exported(Mod, terminate, 2) of
637        true ->
638            catch Mod:terminate(Reason, State);
639        _ ->
640            ok
641    end.
642
643%% @hidden
644error_info(_Reason, application_controller, _Msg, _State, _Debug) ->
645    ok;
646error_info(Reason, Name, Msg, State, Debug) ->
647    Reason1 =
648	case Reason of
649	    {undef,[{M,F,A,L}|MFAs]} ->
650		case code:is_loaded(M) of
651		    false ->
652			{'module could not be loaded',[{M,F,A,L}|MFAs]};
653		    _ ->
654			case erlang:function_exported(M, F, length(A)) of
655			    true ->
656				Reason;
657			    false ->
658				{'function not exported',[{M,F,A,L}|MFAs]}
659			end
660		end;
661	    _ ->
662		Reason
663	end,
664    format("** wx object server ~tp terminating \n"
665           "** Last message in was ~tp~n"
666           "** When Server state == ~tp~n"
667           "** Reason for termination == ~n** ~tp~n",
668	   [Name, Msg, State, Reason1]),
669    sys:print_log(Debug),
670    ok.
671
672%%% ---------------------------------------------------
673%%% Misc. functions.
674%%% ---------------------------------------------------
675%% @hidden
676opt(Op, [{Op, Value}|_]) ->
677    {ok, Value};
678opt(Op, [_|Options]) ->
679    opt(Op, Options);
680opt(_, []) ->
681    false.
682%% @hidden
683debug_options(Name, Opts) ->
684    case opt(debug, Opts) of
685	{ok, Options} -> dbg_opts(Name, Options);
686	_ -> []
687    end.
688%% @hidden
689dbg_opts(Name, Opts) ->
690    case catch sys:debug_options(Opts) of
691	{'EXIT',_} ->
692	    format("~tp: ignoring erroneous debug options - ~tp~n",
693		   [Name, Opts]),
694	    [];
695	Dbg ->
696	    Dbg
697    end.
698
699%% @hidden
700%%-----------------------------------------------------------------
701%% Status information
702%%-----------------------------------------------------------------
703format_status(Opt, StatusData) ->
704    [PDict, SysState, Parent, Debug, [Name, State, Mod, _Time]] = StatusData,
705    Header = gen:format_status_header("Status for wx object ", Name),
706    Log = sys:get_log(Debug),
707    Specific = case format_status(Opt, Mod, PDict, State) of
708                   S when is_list(S) -> S;
709                   S -> [S]
710               end,
711    [{header, Header},
712     {data, [{"Status", SysState},
713	     {"Parent", Parent},
714	     {"Logged events", format_log_state(Mod, Log)}]} |
715     Specific].
716
717format_log_state(Mod, Log) ->
718    [case Event of
719         {out,Msg,From,State} ->
720             {out,Msg,From,format_status(terminate, Mod, get(), State)};
721         {noreply,State} ->
722             {noreply,format_status(terminate, Mod, get(), State)};
723         _ -> Event
724     end || Event <- Log].
725
726format_status(Opt, Mod, PDict, State) ->
727    DefStatus = case Opt of
728		    terminate -> State;
729		    _ -> [{data, [{"State", State}]}]
730		end,
731    case erlang:function_exported(Mod, format_status, 2) of
732	true ->
733	    case catch Mod:format_status(Opt, [PDict, State]) of
734		{'EXIT', _} -> DefStatus;
735		Else -> Else
736	    end;
737	_ ->
738	    DefStatus
739    end.
740