WIP: Adding config interface
This commit is contained in:
parent
bccabcb1c0
commit
f901ba6c9d
@ -30,8 +30,10 @@
|
||||
exec_dir = platform_dir() :: file:filename(),
|
||||
acc = none :: none | binary(),
|
||||
keys = [] :: [],
|
||||
mem_limit = 3550722201 :: pos_integer(),
|
||||
prefs = #{} :: #{}}).
|
||||
max_cores = 2 :: pos_integer(),
|
||||
max_mem = 3550722201 :: pos_integer(),
|
||||
prefs = #{} :: #{},
|
||||
gmc_conf = none :: none | reference()}).
|
||||
|
||||
-type state() :: #s{}.
|
||||
|
||||
@ -51,19 +53,18 @@ gajudesk() ->
|
||||
gen_server:cast(?MODULE, gajudesk).
|
||||
|
||||
|
||||
-spec conf() -> {Account, Keys, Network}
|
||||
when Account :: none | binary(), % <<"ak_...">>
|
||||
Keys :: [binary()],
|
||||
Network :: binary(). % <<"mainnet">> | <<"testnet">> | <<"devnet">>
|
||||
-spec conf() -> ok.
|
||||
|
||||
conf() ->
|
||||
gen_server:call(?MODULE, conf).
|
||||
gen_server:cast(?MODULE, conf).
|
||||
|
||||
|
||||
-spec conf({Account, Keys, Network}) -> ok
|
||||
-spec conf({Account, Keys, Network, MaxCores, MaxMem}) -> ok
|
||||
when Account :: none | binary(), % <<"ak_...">>
|
||||
Keys :: [binary()],
|
||||
Network :: binary(). % <<"mainnet">> | <<"testnet">> | <<"devnet">>
|
||||
Keys :: [binary()], % [<<"ak_...">>]
|
||||
Network :: binary(), % <<"mainnet">> | <<"testnet">> | <<"devnet">>
|
||||
MaxCores :: none | integer(),
|
||||
MaxMem :: none | integer().
|
||||
|
||||
conf(Info) ->
|
||||
gen_server:cast(?MODULE, {conf, Info}).
|
||||
@ -109,17 +110,18 @@ start_link() ->
|
||||
init(none) ->
|
||||
ok = log(info, "Starting"),
|
||||
_ = process_flag(sensitive, true),
|
||||
{Acc, Keys, Network, MemLimit} =
|
||||
{Acc, Keys, Network, MaxCores, MaxMem} =
|
||||
case read_conf() of
|
||||
{ok, C} ->
|
||||
{maps:get(account, C, none),
|
||||
maps:get(keys, C, []),
|
||||
maps:get(network, C, <<"mainnet">>),
|
||||
maps:get(mem_limit, C, 3550722201)};
|
||||
maps:get(max_cores, C, 2),
|
||||
maps:get(max_mem, C, 3550722201)};
|
||||
none ->
|
||||
{none, [], <<"mainnet">>, 3550722201}
|
||||
{none, [], <<"mainnet">>, 2, 3550722201}
|
||||
end,
|
||||
State = #s{network = Network, acc = Acc, keys = Keys, mem_limit = MemLimit},
|
||||
State = #s{network = Network, acc = Acc, keys = Keys, max_cores = MaxCores, max_mem = MaxMem},
|
||||
{ok, start_gui(State)}.
|
||||
|
||||
|
||||
@ -154,8 +156,6 @@ start_gui(State) ->
|
||||
|
||||
handle_call(network, _, State = #s{network = Network}) ->
|
||||
{reply, Network, State};
|
||||
handle_call(conf, _, State = #s{acc = Acc, keys = Keys, network = Network}) ->
|
||||
{reply, {Acc, Keys, Network}, State};
|
||||
handle_call(bin_dir, _, State = #s{exec_dir = BinDir}) ->
|
||||
{reply, BinDir, State};
|
||||
handle_call(Unexpected, From, State) ->
|
||||
@ -169,6 +169,12 @@ handle_cast(start_stop, State) ->
|
||||
handle_cast(gajudesk, State) ->
|
||||
ok = do_gajudesk(),
|
||||
{noreply, State};
|
||||
handle_cast(conf, State) ->
|
||||
NewState = run_gmc_conf(State),
|
||||
{noreply, NewState};
|
||||
handle_cast({conf, Info}, State) ->
|
||||
NewState = do_conf(Info, State),
|
||||
{noreply, NewState};
|
||||
handle_cast(stop, State) ->
|
||||
ok = do_stop(),
|
||||
ok = log(info, "Received a 'stop' message."),
|
||||
@ -181,6 +187,10 @@ handle_cast(Unexpected, State) ->
|
||||
handle_info({gproc_ps_event, Event, Data}, State) ->
|
||||
ok = gmc_gui:message({Event, Data}),
|
||||
{noreply, State};
|
||||
handle_info({'DOWN', Mon, process, PID, Info}, State = #s{gmc_conf = Mon}) ->
|
||||
ok = log(info, "gmc_conf ~p closed with ~p", [PID, Info]),
|
||||
NewState = State#s{gmc_conf = none},
|
||||
{noreply, NewState};
|
||||
handle_info(Unexpected, State) ->
|
||||
ok = log(warning, "Unexpected info: ~tp~n", [Unexpected]),
|
||||
{noreply, State}.
|
||||
@ -211,7 +221,7 @@ do_stop() ->
|
||||
|
||||
%%% Doers
|
||||
|
||||
do_start_stop(#s{acc = PubKey, network = Network, mem_limit = MemLimit}) ->
|
||||
do_start_stop(#s{acc = PubKey, network = Network, max_mem = MaxMem}) ->
|
||||
% smrt guy stuff happens here
|
||||
{Bits, Eureka} =
|
||||
case Network of
|
||||
@ -238,8 +248,8 @@ do_start_stop(#s{acc = PubKey, network = Network, mem_limit = MemLimit}) ->
|
||||
end
|
||||
end,
|
||||
Miner = unicode:characters_to_binary([Fatness, Bits, "-", Type]),
|
||||
Count = optimize_count(MemLimit),
|
||||
Instance = #{<<"executable">> => Miner, <<"executable_group">> => <<"gajumine">>},
|
||||
Count = optimize_count(MaxMem),
|
||||
Instance = #{<<"executable">> => Miner},
|
||||
Workers = lists:duplicate(Count, Instance),
|
||||
Profile =
|
||||
[{pubkey, PubKey},
|
||||
@ -260,8 +270,18 @@ do_start_stop(#s{acc = PubKey, network = Network, mem_limit = MemLimit}) ->
|
||||
disconnected],
|
||||
lists:foreach(fun gmhc_events:ensure_subscribed/1, Events).
|
||||
|
||||
optimize_count(MemLimit) ->
|
||||
{Procs, Memory} =
|
||||
optimize_count(MaxMem) ->
|
||||
{Procs, Memory} = proc_mem(),
|
||||
MeanMaps = min(MaxMem, Memory) div 3550722201,
|
||||
Recommended = max(min(Procs, MeanMaps) - 1, 1),
|
||||
Notice = fun(F, A) -> gmc_gui:message({notice, io_lib:format(F, A)}) end,
|
||||
ok = Notice("Physical Processors: ~p", [Procs]),
|
||||
ok = Notice("Physical Memory: ~p", [Memory]),
|
||||
ok = Notice("29-bit Mean Map Space: ~p", [MeanMaps]),
|
||||
ok = Notice("Workers: ~p", [Recommended]),
|
||||
Recommended.
|
||||
|
||||
proc_mem() ->
|
||||
case os:type() of
|
||||
{unix, linux} ->
|
||||
{processor, Cores} = hd(erlang:system_info(cpu_topology)),
|
||||
@ -276,15 +296,7 @@ optimize_count(MemLimit) ->
|
||||
P = list_to_integer(os:getenv("NUMBER_OF_PROCESSORS")),
|
||||
M = list_to_integer(string:strip(lists:nth(2, string:split(os:cmd("wmic computersystem get TotalPhysicalMemory"), "\r\r\n", all)))),
|
||||
{P, M}
|
||||
end,
|
||||
MeanMaps = min(MemLimit, Memory) div 3550722201,
|
||||
Recommended = max(min(Procs, MeanMaps) - 1, 1),
|
||||
Notice = fun(F, A) -> gmc_gui:message({notice, io_lib:format(F, A)}) end,
|
||||
ok = Notice("Physical Processors: ~p", [Procs]),
|
||||
ok = Notice("Physical Memory: ~p", [Memory]),
|
||||
ok = Notice("29-bit Mean Map Space: ~p", [MeanMaps]),
|
||||
ok = Notice("Workers: ~p", [Recommended]),
|
||||
Recommended.
|
||||
end.
|
||||
|
||||
|
||||
do_gajudesk() ->
|
||||
@ -307,13 +319,31 @@ run_gajudesk() ->
|
||||
end.
|
||||
|
||||
|
||||
run_gmc_conf(State = #s{gmc_conf = none, network = Net, acc = Acc, keys = Keys,
|
||||
max_cores = MProcs, max_mem = MMem,
|
||||
prefs = Prefs}) ->
|
||||
{AProcs, AMem} = proc_mem(),
|
||||
Win = gmc_conf:start_link({Prefs, {Net, Acc, Keys, {AProcs, AMem, MProcs, MMem}}}),
|
||||
PID = wx_object:get_pid(Win),
|
||||
Mon = monitor(process, PID),
|
||||
ok = gmc_conf:to_front(),
|
||||
State#s{gmc_conf = Mon};
|
||||
run_gmc_conf(State) ->
|
||||
ok = gmc_conf:to_front(),
|
||||
State.
|
||||
|
||||
|
||||
do_conf({Network, AccID, Keys, MaxCores, MaxMem}, State) ->
|
||||
State#s{network = Network, acc = AccID, keys = Keys, max_cores = MaxCores, max_mem = MaxMem}.
|
||||
|
||||
|
||||
%%% Utils
|
||||
|
||||
|
||||
%% Paths and Names
|
||||
|
||||
persist(#s{network = Network, acc = Acc, keys = Keys, mem_limit = MemLimit, prefs = Prefs}) ->
|
||||
Update = #{network => Network, account => Acc, keys => Keys, mem_limit => MemLimit},
|
||||
persist(#s{network = Network, acc = Acc, keys = Keys, max_cores = MaxCores, max_mem = MaxMem, prefs = Prefs}) ->
|
||||
Update = #{network => Network, account => Acc, keys => Keys, max_cores => MaxCores, max_mem => MaxMem},
|
||||
Conf = maps:to_list(maps:merge(Prefs, Update)),
|
||||
Path = gmc_prefs_path(),
|
||||
ok = filelib:ensure_dir(Path),
|
||||
|
249
src/gmc_conf.erl
Normal file
249
src/gmc_conf.erl
Normal file
@ -0,0 +1,249 @@
|
||||
%%% @doc
|
||||
%%% GajuMine Configuration GUI
|
||||
%%% @end
|
||||
|
||||
-module(gmc_conf).
|
||||
-vsn("0.2.0").
|
||||
-author("Craig Everett <craigeverett@qpq.swiss>").
|
||||
-copyright("Craig Everett <craigeverett@qpq.swiss>").
|
||||
-license("GPL-3.0-or-later").
|
||||
|
||||
-behavior(wx_object).
|
||||
-include_lib("wx/include/wx.hrl").
|
||||
-export([to_front/0, trouble/1]).
|
||||
-export([start_link/1]).
|
||||
-export([init/1, terminate/2, code_change/3,
|
||||
handle_call/3, handle_cast/2, handle_info/2, handle_event/2]).
|
||||
-include("$zx_include/zx_logger.hrl").
|
||||
|
||||
|
||||
-record(s,
|
||||
{wx = none :: none | wx:wx_object(),
|
||||
frame = none :: none | wx:wx_object(),
|
||||
lang = en :: en | jp,
|
||||
j = none :: none | fun(),
|
||||
net = none :: none | wx:wx_object(),
|
||||
acc = none :: none | wx:wx_object(),
|
||||
keys = none :: none | wx:wx_object(),
|
||||
cores = none :: none | wx:wx_object(),
|
||||
memory = none :: none | wx:wx_object()}).
|
||||
|
||||
|
||||
|
||||
%%% Interface functions
|
||||
|
||||
|
||||
-spec to_front() -> ok.
|
||||
|
||||
to_front() ->
|
||||
wx_object:cast(?MODULE, to_front).
|
||||
|
||||
|
||||
-spec trouble(term()) -> ok.
|
||||
|
||||
trouble(Info) ->
|
||||
wx_object:cast(?MODULE, {trouble, Info}).
|
||||
|
||||
|
||||
|
||||
%%% Startup Functions
|
||||
|
||||
start_link(Prefs) ->
|
||||
wx_object:start_link({local, ?MODULE}, ?MODULE, Prefs, []).
|
||||
|
||||
|
||||
init({Prefs, {Net, Acc, Keys, {AProcs, AMem, MProcs, MMem}}}) ->
|
||||
ok = log(info, "GUI starting..."),
|
||||
Lang = maps:get(lang, Prefs, en_US),
|
||||
Trans = gd_jt:read_translations(?MODULE),
|
||||
J = gd_jt:j(Lang, Trans),
|
||||
Label = J("Configure GajuMine"),
|
||||
|
||||
WX = wx:new(),
|
||||
Frame = wxFrame:new(WX, ?wxID_ANY, Label),
|
||||
MainSz = wxBoxSizer:new(?wxVERTICAL),
|
||||
|
||||
AccSz = wxStaticBoxSizer:new(?wxVERTICAL, Frame, [{label, J("GajuMining Account ID")}]),
|
||||
AccTx = wxTextCtrl:new(Frame, ?wxID_ANY, [{value, acc(Acc)}]),
|
||||
_ = wxStaticBoxSizer:add(AccSz, AccTx, wide(5)),
|
||||
KeysSz = wxStaticBoxSizer:new(?wxVERTICAL, Frame, [{label, J("Additional Account IDs (optional)")}]),
|
||||
KeysTx = wxTextCtrl:new(Frame, ?wxID_ANY, [{style, ?wxTE_MULTILINE}, {value, keys(Keys)}]),
|
||||
_ = wxStaticBoxSizer:add(KeysSz, KeysTx, wide(5)),
|
||||
StatSz = wxStaticBoxSizer:new(?wxVERTICAL, Frame, [{label, J("Max System Committment (optional)")}]),
|
||||
AProcsS = integer_to_list(AProcs),
|
||||
MProcsS = integer_to_list(MProcs),
|
||||
AMemS = gigs(AMem),
|
||||
MMemS = gigs(MMem),
|
||||
Labels = [{J("HW Cores"), J("Max Cores"), AProcsS, MProcsS}, {J("Memory (GB)"), J("Max Memory"), AMemS, MMemS}],
|
||||
{Grid, [CoresTx, MemoryTx]} = display_box(Frame, Labels),
|
||||
Network = wxRadioBox:new(Frame, ?wxID_ANY, J("Network"), {0, 0}, {50, 50}, [J("Mainnet"), J("Testnet")]),
|
||||
ok = wxRadioBox:setSelection(Network, net_num(Net)),
|
||||
|
||||
ButtSz = wxBoxSizer:new(?wxHORIZONTAL),
|
||||
Affirm = wxButton:new(Frame, ?wxID_OK),
|
||||
Cancel = wxButton:new(Frame, ?wxID_CANCEL),
|
||||
_ = wxBoxSizer:add(StatSz, Grid, wide(5)),
|
||||
_ = wxBoxSizer:add(ButtSz, Affirm, wide(5)),
|
||||
_ = wxBoxSizer:add(ButtSz, Cancel, wide(5)),
|
||||
_ = wxBoxSizer:add(MainSz, AccSz, base(5)),
|
||||
_ = wxBoxSizer:add(MainSz, KeysSz, wide(5)),
|
||||
_ = wxBoxSizer:add(MainSz, StatSz, base(5)),
|
||||
_ = wxBoxSizer:add(MainSz, Network, base(5)),
|
||||
_ = wxBoxSizer:add(MainSz, ButtSz, base(5)),
|
||||
ok = wxFrame:setSizer(Frame, MainSz),
|
||||
ok = wxBoxSizer:layout(MainSz),
|
||||
ok = wxFrame:connect(Frame, command_button_clicked),
|
||||
ok = wxFrame:connect(Frame, close_window),
|
||||
ok = wxFrame:setSize(Frame, {500, 500}),
|
||||
ok = wxFrame:center(Frame),
|
||||
true = wxFrame:show(Frame),
|
||||
State =
|
||||
#s{wx = WX, frame = Frame,
|
||||
lang = Lang, j = J,
|
||||
net = Network, acc = AccTx, keys = KeysTx,
|
||||
cores = CoresTx, memory = MemoryTx},
|
||||
{Frame, State}.
|
||||
|
||||
|
||||
base(Padding) -> [{proportion, 0}, {flag, ?wxEXPAND bor ?wxALL}, {border, Padding}].
|
||||
|
||||
wide(Padding) -> [{proportion, 1}, {flag, ?wxEXPAND bor ?wxALL}, {border, Padding}].
|
||||
|
||||
center() -> [{proportion, 0}, {flag, ?wxEXPAND bor ?wxALIGN_CENTER_VERTICAL}].
|
||||
|
||||
gigs(Bytes) ->
|
||||
float_to_list(Bytes / gig(), [{decimals, 2}, compact]).
|
||||
|
||||
gig() -> 1_073_741_824.
|
||||
|
||||
display_box(Parent, Labels) ->
|
||||
Grid = wxFlexGridSizer:new(2, 4, 4),
|
||||
ok = wxFlexGridSizer:setFlexibleDirection(Grid, ?wxHORIZONTAL),
|
||||
ok = wxFlexGridSizer:addGrowableCol(Grid, 1),
|
||||
Make =
|
||||
fun({SO, SI, VS, VM}) ->
|
||||
LO = [SO, ":"],
|
||||
LI = [SI, ":"],
|
||||
T_LO = wxStaticText:new(Parent, ?wxID_ANY, LO),
|
||||
T_CO = wxStaticText:new(Parent, ?wxID_ANY, VS, [{style, ?wxALIGN_LEFT}]),
|
||||
T_LI = wxStaticText:new(Parent, ?wxID_ANY, LI),
|
||||
T_CI = wxTextCtrl:new(Parent, ?wxID_ANY, [{value, VM}]),
|
||||
_ = wxFlexGridSizer:add(Grid, T_LO, center()),
|
||||
_ = wxFlexGridSizer:add(Grid, T_CO, center()),
|
||||
_ = wxFlexGridSizer:add(Grid, T_LI, center()),
|
||||
_ = wxFlexGridSizer:add(Grid, T_CI, zxw:flags(wide)),
|
||||
T_CI
|
||||
end,
|
||||
Controls = lists:map(Make, Labels),
|
||||
{Grid, Controls}.
|
||||
|
||||
|
||||
|
||||
handle_call(Unexpected, From, State) ->
|
||||
ok = log(warning, "Unexpected call from ~tp: ~tp~n", [From, Unexpected]),
|
||||
{noreply, State}.
|
||||
|
||||
|
||||
handle_cast(to_front, State = #s{frame = Frame}) ->
|
||||
ok = wxFrame:raise(Frame),
|
||||
{noreply, State};
|
||||
handle_cast({trouble, Info}, State) ->
|
||||
ok = handle_troubling(State, Info),
|
||||
{noreply, State};
|
||||
handle_cast(Unexpected, State) ->
|
||||
ok = log(warning, "Unexpected cast: ~tp~n", [Unexpected]),
|
||||
{noreply, State}.
|
||||
|
||||
|
||||
handle_info(Unexpected, State) ->
|
||||
ok = log(warning, "Unexpected info: ~tp~n", [Unexpected]),
|
||||
{noreply, State}.
|
||||
|
||||
|
||||
handle_event(#wx{event = #wxCommand{type = command_button_clicked}, id = ID}, State) ->
|
||||
ok =
|
||||
case ID of
|
||||
?wxID_OK -> done(State);
|
||||
?wxID_CANCEL -> buh_bye(State)
|
||||
end,
|
||||
{noreply, State};
|
||||
handle_event(#wx{event = #wxClose{}}, State) ->
|
||||
ok = buh_bye(State),
|
||||
{noreply, State};
|
||||
handle_event(Event, State) ->
|
||||
ok = tell(info, "Unexpected event ~tp State: ~tp~n", [Event, State]),
|
||||
{noreply, State}.
|
||||
|
||||
|
||||
handle_troubling(#s{frame = Frame}, Info) ->
|
||||
zxw:show_message(Frame, Info).
|
||||
|
||||
|
||||
code_change(_, State, _) ->
|
||||
{ok, State}.
|
||||
|
||||
|
||||
terminate(Reason, State) ->
|
||||
ok = log(info, "Reason: ~tp, State: ~tp", [Reason, State]),
|
||||
wx:destroy().
|
||||
|
||||
buh_bye(#s{frame = Frame}) ->
|
||||
wxWindow:destroy(Frame).
|
||||
|
||||
|
||||
%%% Doers
|
||||
|
||||
done(State = #s{net = Network, acc = AccTx, keys = KeysTx, cores = CoresTx, memory = MemTx}) ->
|
||||
Net =
|
||||
case wxRadioBox:getSelection(Network) of
|
||||
0 -> <<"mainnet">>;
|
||||
1 -> <<"testnet">>;
|
||||
_ -> <<"mainnet">>
|
||||
end,
|
||||
AccID = wxTextCtrl:getValue(AccTx),
|
||||
MOAR_IDs = wxTextCtrl:getValue(KeysTx),
|
||||
CoreS = wxTextCtrl:getValue(CoresTx),
|
||||
GigsS = wxTextCtrl:getValue(MemTx),
|
||||
ok = gmc_con:conf({Net, list_to_binary(AccID), binify_keys(MOAR_IDs), cores(CoreS), bytes(GigsS)}),
|
||||
buh_bye(State).
|
||||
|
||||
cores("") ->
|
||||
none;
|
||||
cores(CoreS) ->
|
||||
try
|
||||
list_to_integer(CoreS)
|
||||
catch
|
||||
_:_ -> none
|
||||
end.
|
||||
|
||||
bytes("") ->
|
||||
none;
|
||||
bytes(GigsS) ->
|
||||
try
|
||||
list_to_integer(GigsS) * gig()
|
||||
catch
|
||||
_:_ ->
|
||||
try
|
||||
trunc(list_to_float(GigsS) * gig())
|
||||
catch
|
||||
_:_ -> none
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
% NOTE: 32 is space, 12288 is full-width space.
|
||||
binify_keys(MOAR) ->
|
||||
[list_to_binary(K) || K <- string:lexemes(MOAR, [$\r, $\n, 32, 12288, $\t, $,, $;])].
|
||||
|
||||
|
||||
acc(none) -> "";
|
||||
acc(AKID) -> AKID.
|
||||
|
||||
|
||||
keys(List) ->
|
||||
unicode:characters_to_list(lists:join("\n", List)).
|
||||
|
||||
|
||||
net_num(<<"mainnet">>) -> 0;
|
||||
net_num(<<"testnet">>) -> 1;
|
||||
net_num(_) -> 0.
|
@ -169,8 +169,8 @@ handle_call(Unexpected, From, State) ->
|
||||
|
||||
|
||||
handle_cast(ask_conf, State) ->
|
||||
ok = conf(State),
|
||||
{noreply, State};
|
||||
NewState = conf(State),
|
||||
{noreply, NewState};
|
||||
handle_cast({set_account, ID}, State) ->
|
||||
ok = set_account(ID, State),
|
||||
{noreply, State};
|
||||
@ -379,68 +379,10 @@ explorer_url() ->
|
||||
end.
|
||||
|
||||
|
||||
conf(State = #s{frame = Frame, j = J}) ->
|
||||
Outcome = conf(Frame, J),
|
||||
ok = tell("Outcome: ~p", [Outcome]),
|
||||
conf(State) ->
|
||||
ok = gmc_con:conf(),
|
||||
State.
|
||||
|
||||
conf(Frame, J) ->
|
||||
{Acc, Keys, NetNum} = get_conf(),
|
||||
Label = J("Configure GajuMine"),
|
||||
Dialog = wxDialog:new(Frame, ?wxID_ANY, Label, [{size, {400, 400}}]),
|
||||
Sizer = wxBoxSizer:new(?wxVERTICAL),
|
||||
Network = wxRadioBox:new(Dialog, ?wxID_ANY, J("Network"), {0, 0}, {50, 50}, [J("Mainnet"), J("Testnet")]),
|
||||
ok = wxRadioBox:setSelection(Network, NetNum),
|
||||
GMAccSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("GajuMining Account ID")}]),
|
||||
GMAccTx = wxTextCtrl:new(Dialog, ?wxID_ANY, [{style, ?wxTE_PASSWORD}, {value, Acc}]),
|
||||
_ = wxStaticBoxSizer:add(GMAccSz, GMAccTx, zxw:flags(wide)),
|
||||
AccsSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Additional Account IDs (optional)")}]),
|
||||
AccsTx = wxTextCtrl:new(Dialog, ?wxID_ANY, [{style, ?wxTE_MULTILINE}, {value, Keys}]),
|
||||
_ = wxStaticBoxSizer:add(AccsSz, AccsTx, zxw:flags(wide)),
|
||||
ButtSz = wxBoxSizer:new(?wxHORIZONTAL),
|
||||
Affirm = wxButton:new(Dialog, ?wxID_OK),
|
||||
Cancel = wxButton:new(Dialog, ?wxID_CANCEL),
|
||||
_ = wxBoxSizer:add(ButtSz, Affirm, zxw:flags(wide)),
|
||||
_ = wxBoxSizer:add(ButtSz, Cancel, zxw:flags(wide)),
|
||||
_ = wxBoxSizer:add(Sizer, Network, zxw:flags(base)),
|
||||
_ = wxBoxSizer:add(Sizer, GMAccSz, zxw:flags(base)),
|
||||
_ = wxBoxSizer:add(Sizer, AccsSz, zxw:flags(base)),
|
||||
_ = wxBoxSizer:add(Sizer, ButtSz, zxw:flags(wide)),
|
||||
ok = wxDialog:setSizer(Dialog, Sizer),
|
||||
ok = wxBoxSizer:layout(Sizer),
|
||||
ok = wxFrame:center(Dialog),
|
||||
Outcome =
|
||||
case wxDialog:showModal(Dialog) of
|
||||
?wxID_OK ->
|
||||
Net =
|
||||
case wxRadioBox:getSelection(Network) of
|
||||
0 -> <<"mainnet">>;
|
||||
1 -> <<"testnet">>;
|
||||
_ -> <<"mainnet">>
|
||||
end,
|
||||
AccID = wxTextCtrl:getValue(GMAccTx),
|
||||
MOAR_IDs = wxTextCtrl:getValue(AccsTx),
|
||||
{Net, AccID, MOAR_IDs};
|
||||
?wxID_CANCEL ->
|
||||
cancel
|
||||
end,
|
||||
ok = wxDialog:destroy(Dialog),
|
||||
Outcome.
|
||||
|
||||
get_conf() ->
|
||||
{ID, List, Network} = gmc_con:conf(),
|
||||
{acc(ID), keys(List), net_num(Network)}.
|
||||
|
||||
acc(none) -> "";
|
||||
acc(AKID) -> AKID.
|
||||
|
||||
keys(List) ->
|
||||
unicode:characters_to_list(lists:join("\n", List)).
|
||||
|
||||
net_num(<<"mainnet">>) -> 0;
|
||||
net_num(<<"testnet">>) -> 1;
|
||||
net_num(_) -> 0.
|
||||
|
||||
|
||||
open_browser(Frame, J, URL) ->
|
||||
case wx_misc:launchDefaultBrowser(URL) of
|
||||
|
@ -1,327 +0,0 @@
|
||||
%%% @doc
|
||||
%%% GajuMine Setup GUI
|
||||
%%%
|
||||
%%% In the common case, when a user first runs the program they will not have any
|
||||
%%% accounrts, keys, or mining system setup. The purpose of this program is to provide
|
||||
%%% a first-run guide for them to get set up for mining and explain to the user what is
|
||||
%%% going on.
|
||||
%%%
|
||||
%%% There are two cases to cover:
|
||||
%%% 1. A new miner who doesn't know how anything works and needs to get set up from scratch.
|
||||
%%% 2. A miner who does know a bit about how things work and wants to import a key.
|
||||
%%% @end
|
||||
|
||||
-module(gmc_setup).
|
||||
-vsn("0.2.0").
|
||||
-author("Craig Everett <craigeverett@qpq.swiss>").
|
||||
-copyright("Craig Everett <craigeverett@qpq.swiss>").
|
||||
-license("GPL-3.0-or-later").
|
||||
|
||||
-behavior(wx_object).
|
||||
-include_lib("wx/include/wx.hrl").
|
||||
-export([trouble/1, done/0, done/1]).
|
||||
-export([start_link/1]).
|
||||
-export([init/1, terminate/2, code_change/3,
|
||||
handle_call/3, handle_cast/2, handle_info/2, handle_event/2]).
|
||||
-include("$zx_include/zx_logger.hrl").
|
||||
|
||||
|
||||
-record(w,
|
||||
{name = none :: atom(),
|
||||
id = 0 :: integer(),
|
||||
wx = none :: none | wx:wx_object()}).
|
||||
|
||||
-record(s,
|
||||
{wx = none :: none | wx:wx_object(),
|
||||
frame = none :: none | wx:wx_object(),
|
||||
lang = en :: en | jp,
|
||||
j = none :: none | fun(),
|
||||
buttons = [] :: [#w{}]}).
|
||||
|
||||
|
||||
|
||||
%%% Interface functions
|
||||
|
||||
trouble(Info) ->
|
||||
wx_object:cast(?MODULE, {trouble, Info}).
|
||||
|
||||
|
||||
done() ->
|
||||
wx_object:cast(?MODULE, done).
|
||||
|
||||
|
||||
done(Mnemonic) ->
|
||||
wx_object:cast(?MODULE, {done, Mnemonic}).
|
||||
|
||||
|
||||
|
||||
%%% Startup Functions
|
||||
|
||||
start_link(Prefs) ->
|
||||
wx_object:start_link({local, ?MODULE}, ?MODULE, Prefs, []).
|
||||
|
||||
|
||||
init(Prefs) ->
|
||||
ok = log(info, "GUI starting..."),
|
||||
Lang = maps:get(lang, Prefs, en_US),
|
||||
Trans = gd_jt:read_translations(?MODULE),
|
||||
J = gd_jt:j(Lang, Trans),
|
||||
|
||||
WX = wx:new(),
|
||||
Frame = wxFrame:new(WX, ?wxID_ANY, J("GajuMine Setup")),
|
||||
MainSz = wxBoxSizer:new(?wxVERTICAL),
|
||||
|
||||
ButtonTemplates =
|
||||
[{new_key, J("Create a New Account")},
|
||||
{recover, J("Recover Existing Account")}],
|
||||
|
||||
MakeButton =
|
||||
fun({Name, Label}) ->
|
||||
B = wxButton:new(Frame, ?wxID_ANY, [{label, Label}]),
|
||||
#w{name = Name, id = wxButton:getId(B), wx = B}
|
||||
end,
|
||||
|
||||
Buttons = lists:map(MakeButton, ButtonTemplates),
|
||||
|
||||
Add = fun(#w{wx = Button}) -> wxBoxSizer:add(MainSz, Button, zxw:flags(wide)) end,
|
||||
ok = lists:foreach(Add, Buttons),
|
||||
|
||||
ok = wxFrame:setSizer(Frame, MainSz),
|
||||
ok = wxSizer:layout(MainSz),
|
||||
|
||||
ok = wxFrame:connect(Frame, command_button_clicked),
|
||||
ok = wxFrame:connect(Frame, close_window),
|
||||
ok = wxFrame:setSize(Frame, {300, 300}),
|
||||
ok = wxFrame:center(Frame),
|
||||
true = wxFrame:show(Frame),
|
||||
State =
|
||||
#s{wx = WX, frame = Frame,
|
||||
lang = Lang, j = J,
|
||||
buttons = Buttons},
|
||||
{Frame, State}.
|
||||
|
||||
|
||||
handle_call(Unexpected, From, State) ->
|
||||
ok = log(warning, "Unexpected call from ~tp: ~tp~n", [From, Unexpected]),
|
||||
{noreply, State}.
|
||||
|
||||
|
||||
handle_cast({trouble, Info}, State) ->
|
||||
ok = handle_troubling(State, Info),
|
||||
{noreply, State};
|
||||
handle_cast(done, State = #s{frame = Frame}) ->
|
||||
ok = gmc_con:end_setup(),
|
||||
ok = wxWindow:destroy(Frame),
|
||||
{noreply, State};
|
||||
handle_cast({done, Mnemonic}, State) ->
|
||||
ok = done(Mnemonic, State),
|
||||
{noreply, State};
|
||||
handle_cast(Unexpected, State) ->
|
||||
ok = log(warning, "Unexpected cast: ~tp~n", [Unexpected]),
|
||||
{noreply, State}.
|
||||
|
||||
|
||||
handle_info(Unexpected, State) ->
|
||||
ok = log(warning, "Unexpected info: ~tp~n", [Unexpected]),
|
||||
{noreply, State}.
|
||||
|
||||
|
||||
handle_event(#wx{event = #wxCommand{type = command_button_clicked},
|
||||
id = ID},
|
||||
State = #s{buttons = Buttons}) ->
|
||||
ok =
|
||||
case lists:keyfind(ID, #w.id, Buttons) of
|
||||
#w{name = new_key} -> new_key(State);
|
||||
#w{name = recover} -> recover(State)
|
||||
end,
|
||||
{noreply, State};
|
||||
handle_event(#wx{event = #wxClose{}}, State = #s{frame = Frame}) ->
|
||||
ok = gmc_con:stop(),
|
||||
ok = wxWindow:destroy(Frame),
|
||||
{noreply, State};
|
||||
handle_event(Event, State) ->
|
||||
ok = tell(info, "Unexpected event ~tp State: ~tp~n", [Event, State]),
|
||||
{noreply, State}.
|
||||
|
||||
|
||||
handle_troubling(#s{frame = Frame}, Info) ->
|
||||
zxw:show_message(Frame, Info).
|
||||
|
||||
|
||||
code_change(_, State, _) ->
|
||||
{ok, State}.
|
||||
|
||||
|
||||
terminate(Reason, State) ->
|
||||
ok = log(info, "Reason: ~tp, State: ~tp", [Reason, State]),
|
||||
wx:destroy().
|
||||
|
||||
|
||||
new_key(State = #s{frame = Frame, j = J}) ->
|
||||
case new_key(Frame, J) of
|
||||
{ok, Phrase, Network} ->
|
||||
gmc_con:make_key(Phrase, Network);
|
||||
mismatch ->
|
||||
ok = zxw:show_message(Frame, J("Password entered incorrectly. Try again.")),
|
||||
new_key(State);
|
||||
cancel ->
|
||||
ok
|
||||
end.
|
||||
|
||||
|
||||
new_key(Frame, J) ->
|
||||
Label = J("Generate New Account Key"),
|
||||
Dialog = wxDialog:new(Frame, ?wxID_ANY, Label, [{size, {500, 180}}]),
|
||||
Sizer = wxBoxSizer:new(?wxVERTICAL),
|
||||
Network = wxRadioBox:new(Dialog, ?wxID_ANY, J("Network"), {0, 0}, {50, 50}, [J("Mainnet"), J("Testnet")]),
|
||||
PassSz1 = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Create Password")}]),
|
||||
PassTx1 = wxTextCtrl:new(Dialog, ?wxID_ANY, [{style, ?wxTE_PASSWORD}]),
|
||||
_ = wxStaticBoxSizer:add(PassSz1, PassTx1, zxw:flags(wide)),
|
||||
PassSz2 = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Confirm Password")}]),
|
||||
PassTx2 = wxTextCtrl:new(Dialog, ?wxID_ANY, [{style, ?wxTE_PASSWORD}]),
|
||||
_ = wxStaticBoxSizer:add(PassSz2, PassTx2, zxw:flags(wide)),
|
||||
ButtSz = wxBoxSizer:new(?wxHORIZONTAL),
|
||||
Affirm = wxButton:new(Dialog, ?wxID_OK),
|
||||
Cancel = wxButton:new(Dialog, ?wxID_CANCEL),
|
||||
_ = wxBoxSizer:add(ButtSz, Affirm, zxw:flags(wide)),
|
||||
_ = wxBoxSizer:add(ButtSz, Cancel, zxw:flags(wide)),
|
||||
_ = wxBoxSizer:add(Sizer, Network, zxw:flags(base)),
|
||||
_ = wxBoxSizer:add(Sizer, PassSz1, zxw:flags(base)),
|
||||
_ = wxBoxSizer:add(Sizer, PassSz2, zxw:flags(base)),
|
||||
_ = wxBoxSizer:add(Sizer, ButtSz, zxw:flags(wide)),
|
||||
ok = wxDialog:setSizer(Dialog, Sizer),
|
||||
ok = wxDialog:setSize(Dialog, {400, 300}),
|
||||
ok = wxBoxSizer:layout(Sizer),
|
||||
ok = wxFrame:center(Dialog),
|
||||
Outcome =
|
||||
case wxDialog:showModal(Dialog) of
|
||||
?wxID_OK ->
|
||||
Net =
|
||||
case wxRadioBox:getSelection(Network) of
|
||||
0 -> mainnet;
|
||||
1 -> testnet;
|
||||
_ -> mainnet
|
||||
end,
|
||||
Pass1 = wxTextCtrl:getValue(PassTx1),
|
||||
Pass2 = wxTextCtrl:getValue(PassTx2),
|
||||
case Pass1 =:= Pass2 of
|
||||
true -> {ok, Pass1, Net};
|
||||
false -> mismatch
|
||||
end;
|
||||
?wxID_CANCEL ->
|
||||
cancel
|
||||
end,
|
||||
ok = wxDialog:destroy(Dialog),
|
||||
Outcome.
|
||||
|
||||
|
||||
recover(State = #s{frame = Frame, j = J}) ->
|
||||
case recover(Frame, J) of
|
||||
{ok, Phrase, Mnemonic, Network} ->
|
||||
gmc_con:load_key(Phrase, Mnemonic, Network);
|
||||
mismatch ->
|
||||
ok = zxw:show_message(Frame, J("Password entered incorrectly. Try again.")),
|
||||
recover(State);
|
||||
cancel ->
|
||||
ok
|
||||
end.
|
||||
|
||||
recover(Frame, J) ->
|
||||
Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Import Account Key")),
|
||||
Sizer = wxBoxSizer:new(?wxVERTICAL),
|
||||
Network = wxRadioBox:new(Dialog, ?wxID_ANY, J("Network"), {0, 0}, {50, 50}, [J("Mainnet"), J("Testnet")]),
|
||||
PassSz1 = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Create Password")}]),
|
||||
PassTx1 = wxTextCtrl:new(Dialog, ?wxID_ANY, [{style, ?wxTE_PASSWORD}]),
|
||||
_ = wxStaticBoxSizer:add(PassSz1, PassTx1, zxw:flags(wide)),
|
||||
PassSz2 = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Confirm Password")}]),
|
||||
PassTx2 = wxTextCtrl:new(Dialog, ?wxID_ANY, [{style, ?wxTE_PASSWORD}]),
|
||||
_ = wxStaticBoxSizer:add(PassSz2, PassTx2, zxw:flags(wide)),
|
||||
MnemSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Recovery Phrase")}]),
|
||||
MnemTx = wxTextCtrl:new(Dialog, ?wxID_ANY, [{style, ?wxTE_MULTILINE}]),
|
||||
_ = wxStaticBoxSizer:add(MnemSz, MnemTx, zxw:flags(wide)),
|
||||
ButtSz = wxBoxSizer:new(?wxHORIZONTAL),
|
||||
Affirm = wxButton:new(Dialog, ?wxID_OK),
|
||||
Cancel = wxButton:new(Dialog, ?wxID_CANCEL),
|
||||
_ = wxBoxSizer:add(ButtSz, Affirm, zxw:flags(wide)),
|
||||
_ = wxBoxSizer:add(ButtSz, Cancel, zxw:flags(wide)),
|
||||
_ = wxBoxSizer:add(Sizer, Network, zxw:flags(base)),
|
||||
_ = wxBoxSizer:add(Sizer, PassSz1, zxw:flags(base)),
|
||||
_ = wxBoxSizer:add(Sizer, PassSz2, zxw:flags(base)),
|
||||
_ = wxBoxSizer:add(Sizer, MnemSz, zxw:flags(wide)),
|
||||
_ = wxBoxSizer:add(Sizer, ButtSz, zxw:flags(base)),
|
||||
ok = wxDialog:setSizer(Dialog, Sizer),
|
||||
ok = wxDialog:setSize(Dialog, {400, 400}),
|
||||
ok = wxBoxSizer:layout(Sizer),
|
||||
ok = wxFrame:center(Dialog),
|
||||
Outcome =
|
||||
case wxDialog:showModal(Dialog) of
|
||||
?wxID_OK ->
|
||||
Pass1 = wxTextCtrl:getValue(PassTx1),
|
||||
Pass2 = wxTextCtrl:getValue(PassTx2),
|
||||
case Pass1 =:= Pass2 of
|
||||
true ->
|
||||
Net =
|
||||
case wxRadioBox:getSelection(Network) of
|
||||
0 -> mainnet;
|
||||
1 -> testnet;
|
||||
_ -> mainnet
|
||||
end,
|
||||
Mnemonic = wxTextCtrl:getValue(MnemTx),
|
||||
{ok, Pass1, Mnemonic, Net};
|
||||
false ->
|
||||
mismatch
|
||||
end;
|
||||
?wxID_CANCEL ->
|
||||
cancel
|
||||
end,
|
||||
ok = wxDialog:destroy(Dialog),
|
||||
Outcome.
|
||||
|
||||
|
||||
|
||||
done(Mnemonic, #s{frame = Frame, j = J}) ->
|
||||
Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Mnemonic")),
|
||||
Sizer = wxBoxSizer:new(?wxVERTICAL),
|
||||
MnemSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Recovery Phrase")}]),
|
||||
Options = [{value, Mnemonic}, {style, ?wxTE_MULTILINE bor ?wxTE_READONLY}],
|
||||
MnemTx = wxTextCtrl:new(Dialog, ?wxID_ANY, Options),
|
||||
_ = wxStaticBoxSizer:add(MnemSz, MnemTx, zxw:flags(wide)),
|
||||
ButtSz = wxBoxSizer:new(?wxHORIZONTAL),
|
||||
CloseB = wxButton:new(Dialog, ?wxID_CANCEL, [{label, J("Close")}]),
|
||||
CopyB = wxButton:new(Dialog, ?wxID_OK, [{label, J("Copy to Clipboard")}]),
|
||||
_ = wxBoxSizer:add(ButtSz, CloseB, zxw:flags(wide)),
|
||||
_ = wxBoxSizer:add(ButtSz, CopyB, zxw:flags(wide)),
|
||||
_ = wxBoxSizer:add(Sizer, MnemSz, zxw:flags(wide)),
|
||||
_ = wxBoxSizer:add(Sizer, ButtSz, zxw:flags(base)),
|
||||
ok = wxDialog:setSizer(Dialog, Sizer),
|
||||
ok = wxBoxSizer:layout(Sizer),
|
||||
ok = wxFrame:center(Dialog),
|
||||
ok = wxStyledTextCtrl:setFocus(MnemTx),
|
||||
ok =
|
||||
case wxDialog:showModal(Dialog) of
|
||||
?wxID_CANCEL -> ok;
|
||||
?wxID_OK -> copy_to_clipboard(Mnemonic)
|
||||
end,
|
||||
ok = wxDialog:destroy(Dialog),
|
||||
ok = gmc_con:end_setup(),
|
||||
ok = wxWindow:destroy(Frame),
|
||||
ok.
|
||||
|
||||
|
||||
copy_to_clipboard(String) ->
|
||||
CB = wxClipboard:get(),
|
||||
case wxClipboard:open(CB) of
|
||||
true ->
|
||||
Text = wxTextDataObject:new([{text, String}]),
|
||||
case wxClipboard:setData(CB, Text) of
|
||||
true ->
|
||||
R = wxClipboard:flush(CB),
|
||||
log(info, "String copied to system clipboard. Flushed: ~p", [R]);
|
||||
false ->
|
||||
log(info, "Failed to copy to clipboard")
|
||||
end,
|
||||
ok = wxClipboard:close(CB);
|
||||
false ->
|
||||
log(info, "Failed to acquire the clipboard.")
|
||||
end.
|
||||
|
Loading…
x
Reference in New Issue
Block a user