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	 cast/2,
121	 reply/2,
122	 get_pid/1,
123	 set_pid/2
124	]).
125
126%% -export([behaviour_info/1]).
127-callback init(Args :: term()) ->
128    {#wx_ref{}, State :: term()} | {#wx_ref{}, State :: term(), timeout() | 'hibernate'} |
129    {'stop', Reason :: term()} | 'ignore'.
130-callback handle_event(Request :: #wx{}, State :: term()) ->
131    {'noreply', NewState :: term()} |
132    {'noreply', NewState :: term(), timeout() | 'hibernate'} |
133    {'stop', Reason :: term(), NewState :: term()}.
134-callback handle_call(Request :: term(), From :: {pid(), Tag :: term()},
135                      State :: term()) ->
136    {'reply', Reply :: term(), NewState :: term()} |
137    {'reply', Reply :: term(), NewState :: term(), timeout() | 'hibernate'} |
138    {'noreply', NewState :: term()} |
139    {'noreply', NewState :: term(), timeout() | 'hibernate'} |
140    {'stop', Reason :: term(), Reply :: term(), NewState :: term()} |
141    {'stop', Reason :: term(), NewState :: term()}.
142-callback handle_cast(Request :: term(), State :: term()) ->
143    {'noreply', NewState :: term()} |
144    {'noreply', NewState :: term(), timeout() | 'hibernate'} |
145    {'stop', Reason :: term(), NewState :: term()}.
146-callback handle_info(Info :: timeout() | term(), State :: term()) ->
147    {'noreply', NewState :: term()} |
148    {'noreply', NewState :: term(), timeout() | 'hibernate'} |
149    {'stop', Reason :: term(), NewState :: term()}.
150-callback handle_sync_event(Request :: #wx{}, Ref :: #wx_ref{}, State :: term()) ->
151    ok.
152-callback terminate(Reason :: ('normal' | 'shutdown' | {'shutdown', term()} |
153                               term()),
154                    State :: term()) ->
155    term().
156-callback code_change(OldVsn :: (term() | {'down', term()}), State :: term(),
157                      Extra :: term()) ->
158    {'ok', NewState :: term()} | {'error', Reason :: term()}.
159
160-optional_callbacks(
161    [handle_call/3, handle_cast/2, handle_info/2,
162     handle_sync_event/3, terminate/2, code_change/3]).
163
164%% System exports
165-export([system_continue/3,
166	 system_terminate/4,
167	 system_code_change/4,
168	 format_status/2]).
169
170%% Internal exports
171-export([init_it/6]).
172
173-import(error_logger, [format/2]).
174
175%%%=========================================================================
176%%%  API
177%%%=========================================================================
178%% @hidden
179%% behaviour_info(callbacks) ->
180%%     [{init,1},
181%%      {handle_call,3},
182%%      {handle_info,2},
183%%      {handle_event,2},
184%%      {terminate,2},
185%%      {code_change,3}];
186%% behaviour_info(_Other) ->
187%%     undefined.
188
189
190%%  -----------------------------------------------------------------
191%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
192%% new process.
193-spec start(Mod, Args, Options) -> wxWindow:wxWindow() | {error, term()} when
194      Mod::atom(),
195      Args::term(),
196      Flag::trace | log | {logfile, string()} | statistics | debug,
197      Options::[{timeout, timeout()} | {debug, [Flag]}].
198start(Mod, Args, Options) ->
199    gen_response(gen:start(?MODULE, nolink, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
200
201%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
202%% new process.
203-spec start(Name, Mod, Args, Options) -> wxWindow:wxWindow()  | {error, term()} when
204      Name::{local, atom()},
205      Mod::atom(),
206      Args::term(),
207      Flag::trace | log | {logfile, string()} | statistics | debug,
208      Options::[{timeout, timeout()} | {debug, [Flag]}].
209start(Name, Mod, Args, Options) ->
210    gen_response(gen:start(?MODULE, nolink, Name, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
211
212%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
213%% new process.
214-spec start_link(Mod, Args, Options) -> wxWindow:wxWindow()  | {error, term()} when
215      Mod::atom(),
216      Args::term(),
217      Flag::trace | log | {logfile, string()} | statistics | debug,
218      Options::[{timeout, timeout()} | {debug, [Flag]}].
219start_link(Mod, Args, Options) ->
220    gen_response(gen:start(?MODULE, link, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
221
222%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
223%% new process.
224-spec start_link(Name, Mod, Args, Options) -> wxWindow:wxWindow()  | {error, term()} when
225      Name::{local, atom()},
226      Mod::atom(),
227      Args::term(),
228      Flag::trace | log | {logfile, string()} | statistics | debug,
229      Options::[{timeout, timeout()} | {debug, [Flag]}].
230start_link(Name, Mod, Args, Options) ->
231    gen_response(gen:start(?MODULE, link, Name, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
232
233gen_response({ok, Pid}) ->
234    receive {ack, Pid, Ref = #wx_ref{}} -> Ref end;
235gen_response(Reply) ->
236    Reply.
237
238%% @doc Stops a generic wx_object server with reason 'normal'.
239%% Invokes terminate(Reason,State) in the server. The call waits until
240%% the process is terminated. If the process does not exist, an
241%% exception is raised.
242-spec stop(Obj) -> ok when
243      Obj::wx:wx_object()|atom()|pid().
244stop(Ref = #wx_ref{state=Pid}) when is_pid(Pid) ->
245    try
246	gen:stop(Pid)
247    catch _:ExitReason ->
248	    erlang:error({ExitReason, {?MODULE, stop, [Ref]}})
249    end;
250stop(Name) when is_atom(Name) orelse is_pid(Name) ->
251    try
252	gen:stop(Name)
253    catch _:ExitReason ->
254	    erlang:error({ExitReason, {?MODULE, stop, [Name]}})
255    end.
256
257%% @doc Stops a generic wx_object server with the given Reason.
258%% Invokes terminate(Reason,State) in the server. The call waits until
259%% the process is terminated. If the call times out, or if the process
260%% does not exist, an exception is raised.
261-spec stop(Obj, Reason, Timeout) -> ok when
262      Obj::wx:wx_object()|atom()|pid(),
263      Reason::term(),
264      Timeout::timeout().
265stop(Ref = #wx_ref{state=Pid}, Reason, Timeout) when is_pid(Pid) ->
266    try
267	gen:stop(Pid, Reason, Timeout)
268    catch _:ExitReason ->
269	    erlang:error({ExitReason, {?MODULE, stop, [Ref, Reason, Timeout]}})
270    end;
271stop(Name, Reason, Timeout) when is_atom(Name) orelse is_pid(Name) ->
272    try
273	gen:stop(Name, Reason, Timeout)
274    catch _:ExitReason ->
275	    erlang:error({ExitReason, {?MODULE, stop, [Name, Reason, Timeout]}})
276    end.
277
278%% @doc Make a call to a wx_object server.
279%% The call waits until it gets a result.
280%% Invokes handle_call(Request, From, State) in the server
281-spec call(Obj, Request) -> term() when
282      Obj::wx:wx_object()|atom()|pid(),
283      Request::term().
284call(Ref = #wx_ref{state=Pid}, Request) when is_pid(Pid) ->
285    try
286	{ok,Res} = gen:call(Pid, '$gen_call', Request, infinity),
287	Res
288    catch _:Reason ->
289	    erlang:error({Reason, {?MODULE, call, [Ref, Request]}})
290    end;
291call(Name, Request) when is_atom(Name)  orelse is_pid(Name) ->
292    try
293        {ok,Res} = gen:call(Name, '$gen_call', Request, infinity),
294        Res
295    catch _:Reason ->
296            erlang:error({Reason, {?MODULE, call, [Name, Request]}})
297    end.
298
299%% @doc Make a call to a wx_object server with a timeout.
300%% Invokes handle_call(Request, From, State) in server
301-spec call(Obj, Request, Timeout) -> term() when
302      Obj::wx:wx_object()|atom()|pid(),
303      Request::term(),
304      Timeout::integer().
305call(Ref = #wx_ref{state=Pid}, Request, Timeout) when is_pid(Pid) ->
306    try
307	{ok,Res} = gen:call(Pid, '$gen_call', Request, Timeout),
308	Res
309    catch _:Reason ->
310	    erlang:error({Reason, {?MODULE, call, [Ref, Request, Timeout]}})
311    end;
312call(Name, Request, Timeout) when is_atom(Name) orelse is_pid(Name) ->
313    try
314        {ok,Res} = gen:call(Name, '$gen_call', Request, Timeout),
315        Res
316    catch _:Reason ->
317            erlang:error({Reason, {?MODULE, call, [Name, Request, Timeout]}})
318    end.
319
320%% @doc Make a cast to a wx_object server.
321%% Invokes handle_cast(Request, State) in the server
322-spec cast(Obj, Request) -> ok when
323      Obj::wx:wx_object()|atom()|pid(),
324      Request::term().
325cast(#wx_ref{state=Pid}, Request) when is_pid(Pid) ->
326    Pid ! {'$gen_cast',Request},
327    ok;
328cast(Name, Request) when is_atom(Name) orelse is_pid(Name) ->
329    Name ! {'$gen_cast',Request},
330    ok.
331
332%% @doc Get the pid of the object handle.
333-spec get_pid(Obj) -> pid() when
334      Obj::wx:wx_object()|atom()|pid().
335get_pid(#wx_ref{state=Pid}) when is_pid(Pid) ->
336    Pid.
337
338%% @doc Sets the controlling process of the object handle.
339-spec set_pid(Obj, pid()) -> wx:wx_object() when
340      Obj::wx:wx_object()|atom()|pid().
341set_pid(#wx_ref{}=R, Pid) when is_pid(Pid) ->
342    R#wx_ref{state=Pid}.
343
344%% -----------------------------------------------------------------
345%% Send a reply to the client.
346%% -----------------------------------------------------------------
347%% @doc Get the pid of the object handle.
348-spec reply({pid(), Tag::term()}, Reply::term()) -> pid().
349reply({To, Tag}, Reply) ->
350    catch To ! {Tag, Reply}.
351
352%%%========================================================================
353%%% Gen-callback functions
354%%%========================================================================
355%%% ---------------------------------------------------
356%%% Initiate the new process.
357%%% Register the name using the Rfunc function
358%%% Calls the Mod:init/Args function.
359%%% Finally an acknowledge is sent to Parent and the main
360%%% loop is entered.
361%%% ---------------------------------------------------
362%% @hidden
363init_it(Starter, self, Name, Mod, Args, Options) ->
364    init_it(Starter, self(), Name, Mod, Args, Options);
365init_it(Starter, Parent, Name, Mod, Args, [WxEnv|Options]) ->
366    case WxEnv of
367	undefined -> ok;
368	_ -> wx:set_env(WxEnv)
369    end,
370    put('_wx_object_', {Mod,'_wx_init_'}),
371    Debug = debug_options(Name, Options),
372    case catch Mod:init(Args) of
373	{#wx_ref{} = Ref, State} ->
374	    init_it2(Ref, Starter, Parent, Name, State, Mod, infinity, Debug);
375	{#wx_ref{} = Ref, State, Timeout} ->
376	    init_it2(Ref, Starter, Parent, Name, State, Mod, Timeout, Debug);
377	{stop, Reason} ->
378	    proc_lib:init_ack(Starter, {error, Reason}),
379	    exit(Reason);
380	ignore ->
381	    proc_lib:init_ack(Starter, ignore),
382	    exit(normal);
383	{'EXIT', Reason} ->
384	    proc_lib:init_ack(Starter, {error, Reason}),
385	    exit(Reason);
386	Else ->
387	    Error = {bad_return_value, Else},
388	    proc_lib:init_ack(Starter, {error, Error}),
389	    exit(Error)
390    end.
391%% @hidden
392init_it2(Ref, Starter, Parent, Name, State, Mod, Timeout, Debug) ->
393    ok = wxe_util:register_pid(Ref),
394    case ?CLASS_T(Ref#wx_ref.type, wxWindow) of
395	false ->
396	    Reason = {Ref, "not a wxWindow subclass"},
397	    proc_lib:init_ack(Starter, {error, Reason}),
398	    exit(Reason);
399	true ->
400	    proc_lib:init_ack(Starter, {ok, self()}),
401	    proc_lib:init_ack(Starter, Ref#wx_ref{state=self()}),
402	    loop(Parent, Name, State, Mod, Timeout, Debug)
403    end.
404
405%%%========================================================================
406%%% Internal functions
407%%%========================================================================
408%%% ---------------------------------------------------
409%%% The MAIN loop.
410%%% ---------------------------------------------------
411%% @hidden
412loop(Parent, Name, State, Mod, Time, Debug) ->
413    put('_wx_object_', {Mod,State}),
414    Msg = receive
415	      Input ->
416		  Input
417	  after Time ->
418		  timeout
419	  end,
420    case Msg of
421	{system, From, Req} ->
422	    sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
423				  [Name, State, Mod, Time]);
424	{'EXIT', Parent, Reason} ->
425	    terminate(Reason, Name, Msg, Mod, State, Debug);
426	{'_wxe_destroy_', _Me} ->
427	    terminate(wx_deleted, Name, Msg, Mod, State, Debug);
428	_Msg when Debug =:= [] ->
429	    handle_msg(Msg, Parent, Name, State, Mod);
430	_Msg ->
431	    Debug1 = sys:handle_debug(Debug, fun print_event/3,
432				      Name, {in, Msg}),
433	    handle_msg(Msg, Parent, Name, State, Mod, Debug1)
434    end.
435
436%%% ---------------------------------------------------
437%%% Message handling functions
438%%% ---------------------------------------------------
439%% @hidden
440dispatch({'$gen_cast', Msg}, Mod, State) ->
441    Mod:handle_cast(Msg, State);
442dispatch(Msg = #wx{}, Mod, State) ->
443    Mod:handle_event(Msg, State);
444dispatch(Info, Mod, State) ->
445    Mod:handle_info(Info, State).
446
447%% @hidden
448handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod) ->
449    case catch Mod:handle_call(Msg, From, State) of
450	{reply, Reply, NState} ->
451	    reply(From, Reply),
452	    loop(Parent, Name, NState, Mod, infinity, []);
453	{reply, Reply, NState, Time1} ->
454	    reply(From, Reply),
455	    loop(Parent, Name, NState, Mod, Time1, []);
456	{noreply, NState} ->
457	    loop(Parent, Name, NState, Mod, infinity, []);
458	{noreply, NState, Time1} ->
459	    loop(Parent, Name, NState, Mod, Time1, []);
460	{stop, Reason, Reply, NState} ->
461	    {'EXIT', R} =
462		(catch terminate(Reason, Name, Msg, Mod, NState, [])),
463	    reply(From, Reply),
464	    exit(R);
465	Other -> handle_common_reply(Other, Name, Msg, Mod, State, [])
466    end;
467handle_msg(Msg, Parent, Name, State, Mod) ->
468    case catch dispatch(Msg, Mod, State) of
469        {'EXIT', {undef, [{Mod, handle_info, [_,_], _}|_]}} ->
470            handle_no_reply({noreply, State}, Parent, Name, Msg, Mod, State, []);
471        Reply ->
472            handle_no_reply(Reply, Parent, Name, Msg, Mod, State, [])
473    end.
474
475%% @hidden
476handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod, Debug) ->
477    case catch Mod:handle_call(Msg, From, State) of
478	{reply, Reply, NState} ->
479	    Debug1 = reply(Name, From, Reply, NState, Debug),
480	    loop(Parent, Name, NState, Mod, infinity, Debug1);
481	{reply, Reply, NState, Time1} ->
482	    Debug1 = reply(Name, From, Reply, NState, Debug),
483	    loop(Parent, Name, NState, Mod, Time1, Debug1);
484	{noreply, NState} ->
485	    Debug1 = sys:handle_debug(Debug, fun print_event/3,
486				      Name, {noreply, NState}),
487	    loop(Parent, Name, NState, Mod, infinity, Debug1);
488	{noreply, NState, Time1} ->
489	    Debug1 = sys:handle_debug(Debug, fun print_event/3,
490				      Name, {noreply, NState}),
491	    loop(Parent, Name, NState, Mod, Time1, Debug1);
492	{stop, Reason, Reply, NState} ->
493	    {'EXIT', R} =
494		(catch terminate(Reason, Name, Msg, Mod, NState, Debug)),
495	    _ = reply(Name, From, Reply, NState, Debug),
496	    exit(R);
497	Other ->
498	    handle_common_reply(Other, Name, Msg, Mod, State, Debug)
499    end;
500handle_msg(Msg, Parent, Name, State, Mod, Debug) ->
501    Reply = (catch dispatch(Msg, Mod, State)),
502    handle_no_reply(Reply, Parent, Name, Msg, Mod, State, Debug).
503%% @hidden
504handle_no_reply({noreply, NState}, Parent, Name, _Msg, Mod, _State, []) ->
505    loop(Parent, Name, NState, Mod, infinity, []);
506handle_no_reply({noreply, NState, Time1}, Parent, Name, _Msg, Mod, _State, []) ->
507    loop(Parent, Name, NState, Mod, Time1, []);
508handle_no_reply({noreply, NState}, Parent, Name, _Msg, Mod, _State, Debug) ->
509    Debug1 = sys:handle_debug(Debug, fun print_event/3,
510			      Name, {noreply, NState}),
511    loop(Parent, Name, NState, Mod, infinity, Debug1);
512handle_no_reply({noreply, NState, Time1}, Parent, Name, _Msg, Mod, _State, Debug) ->
513    Debug1 = sys:handle_debug(Debug, fun print_event/3,
514			      Name, {noreply, NState}),
515    loop(Parent, Name, NState, Mod, Time1, Debug1);
516handle_no_reply(Reply, _Parent, Name, Msg, Mod, State, Debug) ->
517    handle_common_reply(Reply, Name, Msg, Mod, State,Debug).
518
519%% @hidden
520-spec handle_common_reply(_, _, _, _, _, _) -> no_return().
521handle_common_reply(Reply, Name, Msg, Mod, State, Debug) ->
522    case Reply of
523	{stop, Reason, NState} ->
524	    terminate(Reason, Name, Msg, Mod, NState, Debug);
525	{'EXIT', What} ->
526	    terminate(What, Name, Msg, Mod, State, Debug);
527	_ ->
528	    terminate({bad_return_value, Reply}, Name, Msg, Mod, State, Debug)
529    end.
530
531%% @hidden
532reply(Name, {To, Tag}, Reply, State, Debug) ->
533    reply({To, Tag}, Reply),
534    sys:handle_debug(Debug, fun print_event/3,
535		     Name, {out, Reply, To, State}).
536
537
538%%-----------------------------------------------------------------
539%% Callback functions for system messages handling.
540%%-----------------------------------------------------------------
541%% @hidden
542system_continue(Parent, Debug, [Name, State, Mod, Time]) ->
543    loop(Parent, Name, State, Mod, Time, Debug).
544
545%% @hidden
546-spec system_terminate(_, _, _, [_]) -> no_return().
547system_terminate(Reason, _Parent, Debug, [Name, State, Mod, _Time]) ->
548    terminate(Reason, Name, [], Mod, State, Debug).
549
550%% @hidden
551system_code_change([Name, State, Mod, Time], _Module, OldVsn, Extra) ->
552    case catch Mod:code_change(OldVsn, State, Extra) of
553        {ok, NewState} -> {ok, [Name, NewState, Mod, Time]};
554        Else -> Else
555    end.
556
557%%-----------------------------------------------------------------
558%% Format debug messages.  Print them as the call-back module sees
559%% them, not as the real erlang messages.  Use trace for that.
560%%-----------------------------------------------------------------
561print_event(Dev, {in, Msg}, Name) ->
562    case Msg of
563	{'$gen_call', {From, _Tag}, Call} ->
564	    io:format(Dev, "*DBG* ~tp got call ~tp from ~w~n",
565		      [Name, Call, From]);
566	{'$gen_cast', Cast} ->
567	    io:format(Dev, "*DBG* ~tp got cast ~tp~n",
568		      [Name, Cast]);
569	_ ->
570	    io:format(Dev, "*DBG* ~tp got ~tp~n", [Name, Msg])
571    end;
572print_event(Dev, {out, Msg, To, State}, Name) ->
573    io:format(Dev, "*DBG* ~tp sent ~tp to ~w, new state ~tp~n",
574	      [Name, Msg, To, State]);
575print_event(Dev, {noreply, State}, Name) ->
576    io:format(Dev, "*DBG* ~tp new state ~tp~n", [Name, State]);
577print_event(Dev, Event, Name) ->
578    io:format(Dev, "*DBG* ~tp dbg  ~tp~n", [Name, Event]).
579
580%%% ---------------------------------------------------
581%%% Terminate the server.
582%%% ---------------------------------------------------
583%% @hidden
584terminate(Reason, Name, Msg, Mod, State, Debug) ->
585    case try_terminate(Mod, Reason, State) of
586	{'EXIT', R} ->
587	    error_info(R, Name, Msg, State, Debug),
588	    exit(R);
589	_ ->
590	    case Reason of
591		normal ->
592		    exit(normal);
593		shutdown ->
594		    exit(shutdown);
595		wx_deleted ->
596		    exit(normal);
597		_ ->
598		    error_info(Reason, Name, Msg, State, Debug),
599		    exit(Reason)
600	    end
601    end.
602
603try_terminate(Mod, Reason, State) ->
604    case erlang:function_exported(Mod, terminate, 2) of
605        true ->
606            catch Mod:terminate(Reason, State);
607        _ ->
608            ok
609    end.
610
611%% @hidden
612error_info(_Reason, application_controller, _Msg, _State, _Debug) ->
613    ok;
614error_info(Reason, Name, Msg, State, Debug) ->
615    Reason1 =
616	case Reason of
617	    {undef,[{M,F,A,L}|MFAs]} ->
618		case code:is_loaded(M) of
619		    false ->
620			{'module could not be loaded',[{M,F,A,L}|MFAs]};
621		    _ ->
622			case erlang:function_exported(M, F, length(A)) of
623			    true ->
624				Reason;
625			    false ->
626				{'function not exported',[{M,F,A,L}|MFAs]}
627			end
628		end;
629	    _ ->
630		Reason
631	end,
632    format("** wx object server ~tp terminating \n"
633           "** Last message in was ~tp~n"
634           "** When Server state == ~tp~n"
635           "** Reason for termination == ~n** ~tp~n",
636	   [Name, Msg, State, Reason1]),
637    sys:print_log(Debug),
638    ok.
639
640%%% ---------------------------------------------------
641%%% Misc. functions.
642%%% ---------------------------------------------------
643%% @hidden
644opt(Op, [{Op, Value}|_]) ->
645    {ok, Value};
646opt(Op, [_|Options]) ->
647    opt(Op, Options);
648opt(_, []) ->
649    false.
650%% @hidden
651debug_options(Name, Opts) ->
652    case opt(debug, Opts) of
653	{ok, Options} -> dbg_opts(Name, Options);
654	_ -> []
655    end.
656%% @hidden
657dbg_opts(Name, Opts) ->
658    case catch sys:debug_options(Opts) of
659	{'EXIT',_} ->
660	    format("~tp: ignoring erroneous debug options - ~tp~n",
661		   [Name, Opts]),
662	    [];
663	Dbg ->
664	    Dbg
665    end.
666
667%% @hidden
668%%-----------------------------------------------------------------
669%% Status information
670%%-----------------------------------------------------------------
671format_status(Opt, StatusData) ->
672    [PDict, SysState, Parent, Debug, [Name, State, Mod, _Time]] = StatusData,
673    StatusHdr = "Status for wx object ",
674    Header = if
675		 is_pid(Name) ->
676		     lists:concat([StatusHdr, pid_to_list(Name)]);
677		 is_atom(Name); is_list(Name) ->
678		     lists:concat([StatusHdr, Name]);
679		 true ->
680		     {StatusHdr, Name}
681	     end,
682    Log = sys:get_debug(log, Debug, []),
683    Specfic =
684	case erlang:function_exported(Mod, format_status, 2) of
685	    true ->
686		case catch Mod:format_status(Opt, [PDict, State]) of
687		    {'EXIT', _} -> [{data, [{"State", State}]}];
688		    Else -> Else
689		end;
690	    _ ->
691		[{data, [{"State", State}]}]
692	end,
693    [{header, Header},
694     {data, [{"Status", SysState},
695	     {"Parent", Parent},
696	     {"Logged events", Log}]} |
697     Specfic].
698