Index: test/Makefile.in =================================================================== --- test/Makefile.in (revision 54) +++ test/Makefile.in (working copy) @@ -3,7 +3,7 @@ EBIN = . ERLC = @ERLC@ ERLINC = ../include -ERL_FLAGS = -I$(ERLINC) +inline +warn_unused_vars +ERL_FLAGS = -I$(ERLINC) -pa ../ebin +inline +warn_unused_vars Mods = basic bgl image Index: test/basic.erl =================================================================== --- test/basic.erl (revision 54) +++ test/basic.erl (working copy) @@ -6,16 +6,23 @@ %%% Created : 22 Feb 2007 by Dan Gudmundsson %%%------------------------------------------------------------------- -module(basic). +-behaviour(wx_app). +%% Behaviour Exports +-export([on_init/2, + on_event/2, + terminate/2, + code_change/3]). + -compile(export_all). -include("../src/wxe.hrl"). %% For debugging you shouldn't match #wxe_ref -include("../include/wx.hrl"). -start() -> - #wx_ref{} = WX = wx:new(), +on_init(AppContext, _AppArgs) -> + #wx_ref{} = AppContext, wx:debug(2), - #wx_ref{} = Frame = wxFrame:new(WX,1,"Hello World",[]), + #wx_ref{} = Frame = wxFrame:new(AppContext,1,"Hello World",[]), ok = wxFrame:connect(Frame, close_window), #wx_ref{} = Menu = wxMenu:new([]), #wx_ref{} = MenuBar = wxMenuBar:new(), @@ -60,30 +67,36 @@ WxEnv = wx:get_env(), spawn(fun() -> wx:set_env(WxEnv), event_tester(Test) end), - test_loop(Frame). + {ok, Frame}. -test_loop(Frame) -> - receive - #wx{id=11} -> - wxWindow:'Destroy'(Frame), - io:format("~p Closing window ~n",[self()]); - #wx{event=#wxClose{}} -> - %% Do not call 'Destroy' here since it will be automaticly destroyed - %% by wxwidgets default wxClose handler - %% wxWindow:'Destroy'(Frame), - io:format("~p Closing window ~n",[self()]); - #wx{event=#wxMouse{type=motion,x=X,y=Y}} -> - Str = lists:flatten(io_lib:format("Mouse {~p,~p}", [X,Y])), - wxFrame:setStatusText(Frame, Str,[]), - test_loop(Frame); - Wx = #wx{} -> - io:format("~p Received wx record ~p~n",[self(), Wx]), - test_loop(Frame); - Kalle -> - io:format("Received ~p~n",[Kalle]), - test_loop(Frame) - end. +on_event(#wx{id=11}, Frame) -> + wxWindow:'Destroy'(Frame), + io:format("~p Closing window (1)~n",[self()]), + {stop, normal, Frame}; +on_event(#wx{event=#wxClose{}},_State) -> + %% Do not call 'Destroy' here since it will be automaticly destroyed + %% by wxwidgets default wxClose handler + %% wxWindow:'Destroy'(Frame), + io:format("~p Closing window (2)~n",[self()]), + {stop, normal, _State}; +on_event(#wx{event=#wxMouse{type=motion,x=X,y=Y}},Frame) -> + Str = lists:flatten(io_lib:format("Mouse {~p,~p}", [X,Y])), + wxFrame:setStatusText(Frame, Str,[]), + {ok, Frame}; +on_event(Wx = #wx{},_State) -> + io:format("~p Received wx record ~p~n",[self(), Wx]), + {ok, _State}; +on_event(Kalle,_State) -> + io:format("Received ~p~n",[Kalle]), + {stop, unhandled_event, _State}. +terminate(Reason,_State) -> + io:format("Got terminate because: ~p~n", [Reason]), + ok. + +code_change(_OldVsn, _State, _Extra) -> + {ok,_State}. + ev_callback() -> Self = self(), fun(#wx{event=Ev}, Event) -> Index: src/wx_app.erl =================================================================== --- src/wx_app.erl (revision 0) +++ src/wx_app.erl (revision 0) @@ -0,0 +1,324 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(wx_app). + +%%% --------------------------------------------------- +%%% +%%% This code copied form gen_server.erl +%%% +%%% It implements a wxApp server as a behaviour. In the spirit of +%%% wxWidgets the the programmer performs initialisation in an on_init/2 +%%% callback which is the analog of wxApp::OnInit(). +%%% +%%% The user module should export: +%%% +%%% on_init(AppContext, AppArgs) +%%% +%%% ==> {ok, State} +%%% ignore +%%% {stop, Reason} +%%% +%%% on_event(Event, State) +%%% +%%% ==> {ok, State} +%%% {stop, Reason, State} +%%% Reason = normal | shutdown | Term terminate(Reason,State) is called +%%% +%%% terminate(Reason, State) Let the user module clean up +%%% always called when server terminates +%%% +%%% ==> ok +%%% +%%% code_change(OldVsn, State, Extra) +%%% +%%% ==> {ok, State} +%%% +%%% --------------------------------------------------- + +%% API +-export([start/3, start/4, + start_link/3, start_link/4]). + +-export([behaviour_info/1]). + +%% System exports +-export([system_continue/3, + system_terminate/4, + system_code_change/4, + format_status/2]). + +%% Internal exports +-export([init_it/6, print_event/3]). + +-import(error_logger, [format/2]). + +%%%========================================================================= +%%% API +%%%========================================================================= + +behaviour_info(callbacks) -> + [{on_init,2}, + {on_event,2}, + {terminate,2}, + {code_change,3}]; +behaviour_info(_Other) -> + undefined. + +%%% ----------------------------------------------------------------- +%%% Starts a wx server. +%%% start(Mod, Args, Options) +%%% start(Name, Mod, Args, Options) +%%% start_link(Mod, Args, Options) +%%% start_link(Name, Mod, Args, Options) where: +%%% Name ::= {local, atom()} | {global, atom()} +%%% Mod ::= atom(), callback module implementing the 'real' server +%%% Args ::= term(), scond argument to on_init (to Mod:on_init/2) +%%% Options ::= [{debug, [Flag]}] +%%% Flag ::= trace | log | {logfile, File} | statistics | debug +%%% (debug == log && statistics) +%%% Returns: {ok, Pid} | +%%% {error, {already_started, Pid}} | +%%% {error, Reason} +%%% ----------------------------------------------------------------- +start(Mod, Args, Options) -> + gen:start(?MODULE, nolink, Mod, Args, Options). + +start(Name, Mod, Args, Options) -> + gen:start(?MODULE, nolink, Name, Mod, Args, Options). + +start_link(Mod, Args, Options) -> + gen:start(?MODULE, link, Mod, Args, Options). + +start_link(Name, Mod, Args, Options) -> + gen:start(?MODULE, link, Name, Mod, Args, Options). + + +%%%======================================================================== +%%% Gen-callback functions +%%%======================================================================== + +%%% --------------------------------------------------- +%%% Initiate the new process. +%%% Register the name using the Rfunc function +%%% Calls the Mod:init/Args function. +%%% Finally an acknowledge is sent to Parent and the main +%%% loop is entered. +%%% --------------------------------------------------- +init_it(Starter, self, Name, Mod, Args, Options) -> + init_it(Starter, self(), Name, Mod, Args, Options); +init_it(Starter, Parent, Name, Mod, Args, Options) -> + Debug = debug_options(Name, Options), + case catch Mod:on_init(wx:new(), Args) of + {ok, State} -> + proc_lib:init_ack(Starter, {ok, self()}), + loop(Parent, Name, State, Mod, Debug); + {stop, Reason} -> + proc_lib:init_ack(Starter, {error, Reason}), + exit(Reason); + ignore -> + proc_lib:init_ack(Starter, ignore), + exit(normal); + {'EXIT', Reason} -> + proc_lib:init_ack(Starter, {error, Reason}), + exit(Reason); + Else -> + Error = {bad_return_value, Else}, + proc_lib:init_ack(Starter, {error, Error}), + exit(Error) + end. + +%%%======================================================================== +%%% Internal functions +%%%======================================================================== +%%% --------------------------------------------------- +%%% The MAIN loop. +%%% --------------------------------------------------- +loop(Parent, Name, State, Mod, Debug) -> + receive + {system, From, Req} -> + sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug, + [Name, State, Mod]); + Msg = {'EXIT', Parent, Reason} -> + terminate(Reason, Name, Msg, Mod, State, Debug); + Msg when Debug =:= [] -> + on_event(Msg, Parent, Name, State, Mod, Debug); + Msg -> + Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, + Name, Msg), + on_event(Msg, Parent, Name, State, Mod, Debug1) + end. + +%%% Process events received from system and widgets +on_event(Msg, Parent, Name, State, Mod, Debug) -> + case catch Mod:on_event(Msg, State) of + {ok, NewState} when Debug =:= [] -> + loop(Parent, Name, NewState, Mod, Debug); + {ok, NewState} -> + Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name, Msg), + loop(Parent, Name, NewState, Mod, Debug1); + {stop, Reason, NewState} -> + terminate(Reason, Name, Msg, Mod, NewState, Debug); + {'EXIT', What} -> + terminate(What, Name, Msg, Mod, State, Debug); + Reply -> + terminate({bad_return_value, Reply}, Name, Msg, Mod, State, Debug) + end. + +%%----------------------------------------------------------------- +%% Callback functions for system messages handling. +%%----------------------------------------------------------------- +system_continue(Parent, Debug, [Name, State, Mod]) -> + loop(Parent, Name, State, Mod, Debug). + +-spec(system_terminate/4 :: (_, _, _, [_]) -> no_return()). + +system_terminate(Reason, _Parent, Debug, [Name, State, Mod]) -> + terminate(Reason, Name, [], Mod, State, Debug). + +system_code_change([Name, State, Mod], _Module, OldVsn, Extra) -> + case catch Mod:code_change(OldVsn, State, Extra) of + {ok, NewState} -> {ok, [Name, NewState, Mod]}; + Else -> Else + end. +%%----------------------------------------------------------------- +%% Format debug messages. Print them as the call-back module sees +%% them, not as the real erlang messages. Use trace for that. +%%----------------------------------------------------------------- +print_event(Dev, Event, Name) -> + io:format(Dev, "*DBG* name:~p event:~p~n", [Name, Event]). + + +%%% --------------------------------------------------- +%%% Terminate the server. +%%% --------------------------------------------------- +terminate(Reason, Name, Msg, Mod, State, Debug) -> + case catch Mod:terminate(Reason, State) of + {'EXIT', R} -> + error_info(R, Name, Msg, State, Debug), + exit(R); + _ -> + case Reason of + normal -> + exit(normal); + shutdown -> + exit(shutdown); + _ -> + error_info(Reason, Name, Msg, State, Debug), + exit(Reason) + end + end. + +error_info(_Reason, application_controller, _Msg, _State, _Debug) -> + %% OTP-5811 Don't send an error report if it's the system process + %% application_controller which is terminating - let init take care + %% of it instead + ok; +error_info(Reason, Name, Msg, State, Debug) -> + Reason1 = + case Reason of + {undef,[{M,F,A}|MFAs]} -> + case code:is_loaded(M) of + false -> + {'module could not be loaded',[{M,F,A}|MFAs]}; + _ -> + case erlang:function_exported(M, F, length(A)) of + true -> + Reason; + false -> + {'function not exported',[{M,F,A}|MFAs]} + end + end; + _ -> + Reason + end, + format("** wx_app server ~p terminating \n" + "** Last message in was ~p~n" + "** When Server state == ~p~n" + "** Reason for termination == ~n** ~p~n", + [Name, Msg, State, Reason1]), + sys:print_log(Debug), + ok. + + +%%% --------------------------------------------------- +%%% Misc. functions. +%%% --------------------------------------------------- + +opt(Op, [{Op, Value}|_]) -> + {ok, Value}; +opt(Op, [_|Options]) -> + opt(Op, Options); +opt(_, []) -> + false. + +debug_options(Name, Opts) -> + case opt(debug, Opts) of + {ok, Options} -> dbg_options(Name, Options); + _ -> dbg_options(Name, []) + end. + +dbg_options(Name, []) -> + Opts = + case init:get_argument(generic_debug) of + error -> + []; + _ -> + [log, statistics] + end, + dbg_opts(Name, Opts); +dbg_options(Name, Opts) -> + dbg_opts(Name, Opts). + +dbg_opts(Name, Opts) -> + case catch sys:debug_options(Opts) of + {'EXIT',_} -> + format("~p: ignoring erroneous debug options - ~p~n", + [Name, Opts]), + []; + Dbg -> + Dbg + end. + + +%%----------------------------------------------------------------- +%% Status information +%%----------------------------------------------------------------- +format_status(Opt, StatusData) -> + [PDict, SysState, Parent, Debug, [Name, State, Mod, _Time]] = StatusData, + NameTag = if is_pid(Name) -> + pid_to_list(Name); + is_atom(Name) -> + Name + end, + Header = lists:concat(["Status for wx_app server ", NameTag]), + Log = sys:get_debug(log, Debug, []), + Specfic = + case erlang:function_exported(Mod, format_status, 2) of + true -> + case catch Mod:format_status(Opt, [PDict, State]) of + {'EXIT', _} -> [{data, [{"State", State}]}]; + Else -> Else + end; + _ -> + [{data, [{"State", State}]}] + end, + [{header, Header}, + {data, [{"Status", SysState}, + {"Parent", Parent}, + {"Logged events", Log}]} | + Specfic]. Index: src/Makefile.in =================================================================== --- src/Makefile.in (revision 54) +++ src/Makefile.in (working copy) @@ -7,6 +7,7 @@ ErlMods = \ wx \ + wx_app \ wxe_master \ wxe_server \ wxe_util