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