aboutsummaryrefslogtreecommitdiffstats
path: root/lib/gs/contribs/bonk/bonk.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gs/contribs/bonk/bonk.erl')
-rw-r--r--lib/gs/contribs/bonk/bonk.erl579
1 files changed, 579 insertions, 0 deletions
diff --git a/lib/gs/contribs/bonk/bonk.erl b/lib/gs/contribs/bonk/bonk.erl
new file mode 100644
index 0000000000..12d94f6c5e
--- /dev/null
+++ b/lib/gs/contribs/bonk/bonk.erl
@@ -0,0 +1,579 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(bonk).
+-export([run/0, run/1,bonk_dir/0,start/0]).
+
+-record(colors, {miss, x, bomb, face}).
+-record(scores, {points, level, bombs, hits, showed, bonus}).
+
+start() ->
+ spawn(bonk, run, []).
+
+run() ->
+ run(color).
+
+run([ColorMode]) -> % This is for the start script...
+ run(ColorMode);
+
+run(ColorMode) when atom(ColorMode) ->
+ GS = gs:start(),
+ SoundPid=spawn_link(bonk_sound,start,[]),
+ {H,M,S}=time(),
+ random:seed(H*13,M*7,S*3),
+ {SqrPids, Bmps, Colors} = create_board(GS, ColorMode),
+ {ScoreL,_File} = get_highscore(),
+ display_highscore(ScoreL),
+ put(colormode, ColorMode),
+ SoundPid ! music,
+ sleep(6500),
+ gs:config(aboutButton, [{enable,true}]),
+ gs:config(newButton, [{enable,true}]),
+ gs:config(quitButton, [{enable,true}]),
+ idle(SoundPid, SqrPids, Bmps, Colors).
+
+%% This is not an application so we don't have their way of knowing
+%% a private data directory where the GIF files are located (this directory).
+%% We can find GS and makes it relative from there /kgb
+%%
+%% Note the silly slash that is added. The rest of the code uses
+%% append to contruct file names and assumes that the directory ends
+%% in slash. If filename:join was used and the problem is gone.
+
+-define(EbinFromGsPriv,"../contribs/bonk").
+
+bonk_dir() ->
+ GsPrivDir = code:priv_dir(gs),
+ filename:join(GsPrivDir,?EbinFromGsPriv) ++ "/".
+
+
+idle(SoundPid, SqrPids, Bmps, Colors) ->
+ receive
+ {gs, newButton, click, _Data, _Args} ->
+ init(SoundPid, SqrPids, Bmps, Colors);
+ {gs, aboutButton, click, _Data, _Args} ->
+ display_about(),
+ idle(SoundPid, SqrPids, Bmps, Colors);
+ {gs, quitButton, click, _Data, _Args} ->
+ SoundPid ! quit,
+ send_to_all(SqrPids, quit);
+ _Other ->
+ %%io:format("Got ~w in idle~n", [_Other]),
+ idle(SoundPid, SqrPids, Bmps, Colors)
+ end.
+
+
+
+init(SoundPid, SqrPids, Bmps, Colors) ->
+ clear_board(Bmps),
+ SoundPid ! start,
+ gs:config(newButton, [{enable,false}]),
+ gs:config(endButton, [{enable,true}]),
+ gs:config(aboutButton, [{enable,false}]),
+ Scores = #scores{points=0, level=1, bombs=0, hits=0, showed=0, bonus=10},
+ clear_scores(Scores),
+ flush(),
+ send_to_all(SqrPids, start),
+ game(SoundPid, SqrPids, Bmps, Colors, Scores).
+
+
+game(SoundPid, SqrPids, Bmps, Colors, Scores) ->
+ receive
+ {gs, _Square, buttonpress, SqrPid, [1 | _Rest]} when pid(SqrPid) ->
+ SqrPid ! bonk,
+ game(SoundPid, SqrPids, Bmps, Colors, Scores);
+ {gs, _Id, buttonpress, _Data, [Butt | _Rest]} when Butt =/= 1 ->
+ NewScores = bomb(SoundPid, SqrPids, Scores),
+ game(SoundPid, SqrPids, Bmps, Colors, NewScores);
+ {show, Square, Rect} ->
+ NewScores = show_face(Square, Rect, Colors, Scores),
+ game(SoundPid, SqrPids, Bmps, Colors, NewScores);
+ {hide, Square, Rect} ->
+ NewScores = hide_face(Square, Rect, Colors, Scores),
+ game(SoundPid, SqrPids, Bmps, Colors, NewScores);
+ {missed, Square, Rect} ->
+ case miss_face(SoundPid, Square, Rect, Colors, Scores) of
+ {continue, NewScores} ->
+ game(SoundPid, SqrPids, Bmps, Colors, NewScores);
+ {game_over, NewScores} ->
+ game_over(SoundPid, SqrPids, Bmps, Colors, NewScores)
+ end;
+ {bonked, Square, Rect} ->
+ NewScores = bonked(SoundPid, SqrPids, Square, Rect, Scores, Colors),
+ game(SoundPid, SqrPids, Bmps, Colors, NewScores);
+ {bombed, Square, Rect} ->
+ NewScores = bombed(SoundPid, SqrPids, Square, Rect, Scores, Colors),
+ game(SoundPid, SqrPids, Bmps, Colors, NewScores);
+ {gs, endButton, click, _Data, _Args} ->
+ game_over(SoundPid, SqrPids, Bmps, Colors, Scores);
+ {gs, quitButton, click, _Data, _Args} ->
+ quit(SoundPid, SqrPids, Bmps, Colors, Scores);
+ _Other ->
+ game(SoundPid, SqrPids, Bmps, Colors, Scores)
+ end.
+
+
+
+game_over(SoundPid, SqrPids, Bmps, Colors, Scores) ->
+ SoundPid ! game_over,
+ send_to_all(SqrPids, stop),
+ flush(),
+ sleep(2000),
+ update_scorelist(SoundPid, Scores),
+ gs:config(newButton, [{enable,true}]),
+ gs:config(endButton, [{enable,false}]),
+ gs:config(aboutButton, [{enable,true}]),
+ idle(SoundPid, SqrPids, Bmps, Colors).
+
+
+quit(SoundPid, SqrPids, _Bmps, _Colors, _Scores) ->
+ SoundPid ! quit,
+ send_to_all(SqrPids, quit),
+ true.
+
+
+
+bomb(SoundPid, SqrPids, Scores) ->
+ case Scores#scores.bombs of
+ Bombs when Bombs > 0 ->
+ send_to_all(SqrPids, bomb),
+ SoundPid ! bomb,
+ gs:config(bombOut,[{text,integer_to_list(Bombs-1)}]),
+ Scores#scores{bombs=Bombs-1};
+ _Other ->
+ Scores
+ end.
+
+show_face(Square, Rect, Colors, Scores) ->
+ Showed = Scores#scores.showed,
+ if
+ Showed == Scores#scores.level+1 ->
+ Square ! sleep,
+ Scores;
+ true ->
+ FaceColors = Colors#colors.face,
+ FaceColor = lists:nth(random:uniform(length(FaceColors)), FaceColors),
+ gs:config(Rect, [{bitmap,lists:append(bonk_dir(),"bitmaps/bonkface")},{fg, FaceColor}]),
+ Scores#scores{showed=Showed+1}
+ end.
+
+hide_face(_Square, Rect, _Colors, Scores) ->
+ Showed = Scores#scores.showed,
+ gs:config(Rect, [{bitmap,lists:append(bonk_dir(),"bitmaps/bonktom")}]),
+ Scores#scores{showed=Showed-1}.
+
+
+miss_face(SoundPid, _Square, Rect, Colors, Scores) ->
+ SoundPid ! missed,
+ gs:config(Rect, [{bitmap,lists:append(bonk_dir(),"bitmaps/bonkmiss")}, {fg, Colors#colors.miss}]),
+ Bonus = Scores#scores.bonus,
+ if
+ Bonus > 1 ->
+ gs:config(bonusOut, [{text,integer_to_list(Bonus-1)}]),
+ {continue, Scores#scores{bonus=Bonus-1}};
+ true ->
+ gs:config(bonusOut, [{text,"0"}]),
+ {game_over, Scores}
+ end.
+
+bonked(SoundPid, SqrPids, _Square, Rect, Scores, Colors) ->
+ gs:config(Rect, [{bitmap,lists:append(bonk_dir(),"bitmaps/bonkx")}, {fg, Colors#colors.x}]),
+ SoundPid ! bonk,
+ update_score(SoundPid, SqrPids, Scores).
+
+bombed(SoundPid, SqrPids, _Square, Rect, Scores, Colors) ->
+ gs:config(Rect, [{bitmap,lists:append(bonk_dir(),"bitmaps/bonkbomb")}, {fg, Colors#colors.bomb}]),
+ update_score(SoundPid, SqrPids, Scores).
+
+
+update_score(SoundPid, SqrPids, Scores) ->
+ Points = Scores#scores.points,
+ Level = Scores#scores.level,
+ NewPoints = Points+Level,
+ gs:config(scoreOut,[{text,integer_to_list(NewPoints)}]),
+ case Scores#scores.hits of
+ 24 ->
+ SoundPid ! new_level,
+ NewLevel = Level+1,
+ NewBombs = Scores#scores.bombs+1,
+ send_to_all(SqrPids, {new_level, NewLevel}),
+ gs:config(levelOut,[{text,integer_to_list(NewLevel)}]),
+ gs:config(bombOut,[{text,integer_to_list(NewBombs)}]),
+ Scores#scores{points=NewPoints, level=NewLevel, hits=0, bombs=NewBombs};
+ Hits ->
+ Scores#scores{points=NewPoints, hits=Hits+1}
+ end.
+
+
+send_to_all([], _Msg) ->
+ true;
+
+send_to_all([Pid|Rest],Msg) when pid(Pid) ->
+ Pid ! Msg,
+ send_to_all(Rest,Msg);
+
+send_to_all([_Else|Rest],Msg) ->
+ send_to_all(Rest,Msg).
+
+
+create_board(GS, ColorMode) ->
+ Colors =
+ case ColorMode of
+ bw -> #colors{miss=white, x=white, bomb=white, face=[white]};
+ _Color -> #colors{miss=red, x=green, bomb=white,
+ face=[lightblue, orange, magenta, peachpuff, pink]}
+ end,
+ BGCol = if ColorMode==bw -> black; true -> black end, % background color
+ TextCol = if ColorMode==bw -> white; true -> pink end, % status texts
+ NrCol = if ColorMode==bw -> white; true -> purple end, % status figures
+ LogoCol = if ColorMode==bw -> white; true -> green end, % bonk logo
+ BLineCol = if ColorMode==bw -> white; true -> grey end, % button line
+ SLineCol = if ColorMode==bw -> white; true -> red end, % status line
+ BTextCol = if ColorMode==bw -> white; true -> orange end, % button text
+ HiHeadCol = if ColorMode==bw -> white; true -> red end, % high score label
+ HiCol = if ColorMode==bw -> white; true -> cyan end, % high scores
+ SquareCol = if ColorMode==bw -> white; true -> yellow end, % game squares
+ ErlFgCol = if ColorMode==bw -> white; true -> red end, %
+ ErlBGCol = if ColorMode==bw -> black; true -> white end, %
+ ErlTxtCol = if ColorMode==bw -> white; true -> black end, %
+
+ Width = 550, % width of bonk window
+ Height = 550, % Height of bonk window
+
+ BX = 0, % x-pos for first button
+ DBX = 100, % space between buttons
+ BY = 0, % y-pos for buttons
+ BLineY = 30, % y-pos of button line
+ LogoX = (Width-320) div 2, % x-pos of bonk logo (logo is 320 pix wide)
+ LogoY = BLineY+2, % y-pos of bonk logo
+ ErlLogoX = LogoX + 200, % x-pos of Erlang e
+ ErlLogoY = LogoY + 10, % y-pos of Erlang e
+ SLineY = Height-22, % status line position
+ TextWidth = 50, % text width of status items
+ SX = 2, % x-pos for first status item
+ DSX = TextWidth+94, % pixels between status items
+ SY = SLineY+2, % y-pos status items
+ HiWidth = 100, % width of high score field
+ _HiHeight = 180, % height of the same
+ HiX = Width-HiWidth, % high score text position
+ HiY = BLineY+10,
+ DHY = 20, % space between title & scores
+ SquareSize = 76, % size of each game square
+ SquareSpace = 1, % space between game squares
+ SquareX = 40,
+ SquareY = 65,
+
+ gs:create(window, bonkWin, GS, [{width, Width}, {height, Height},
+ {bg, BGCol},
+ {title, "Bonk the game"},
+ {iconname, "Bonk!"},
+ {map, false}]),
+ gs:create(canvas, bonkCanvas, bonkWin, [{width, Width},
+ {height, Height},
+ {bg, BGCol}]),
+ gs:create(image, bonkCanvas, [{bitmap,lists:append(bonk_dir(), "bitmaps/bonklogo")},
+ {coords, [{LogoX, LogoY}]},
+ {fg, LogoCol},
+ {bg, BGCol}]),
+ gs:create(image, bonkCanvas, [{bitmap,lists:append(bonk_dir(), "bitmaps/erl-e")},
+ {coords, [{ErlLogoX, ErlLogoY}]},
+ {fg, ErlFgCol},
+ {bg, ErlBGCol}]),
+ gs:create(image, bonkCanvas, [{bitmap,lists:append(bonk_dir(), "bitmaps/erl-text")},
+ {coords, [{ErlLogoX, ErlLogoY}]},
+ {fg, ErlTxtCol}]),
+ gs:create(line, bLine, bonkCanvas, [{coords, [{0,BLineY}, {Width,BLineY}]},
+ {fg, BLineCol},
+ {width, 2}]),
+ gs:create(line, bLine, bonkCanvas, [{coords, [{0,SLineY}, {Width, SLineY}]},
+ {fg, SLineCol},
+ {width, 2}]),
+ gs:create(text, scoreText, bonkCanvas, [{coords, [{SX, SY}]},
+ {fg, TextCol},
+ {text, "Score:"}]),
+ gs:create(text, scoreOut, bonkCanvas, [{coords, [{SX+TextWidth, SY}]},
+ {fg, NrCol},
+ {width, DSX-TextWidth},
+ {text, ""}]),
+ gs:create(text, bombText, bonkCanvas, [{coords, [{SX+DSX, SY}]},
+ {fg, TextCol},
+ {text, "Bombs:"}]),
+ gs:create(text, bombOut, bonkCanvas, [{coords, [{SX+DSX+TextWidth, SY}]},
+ {fg, NrCol},
+ {width, DSX-TextWidth},
+ {text, ""}]),
+ gs:create(text, bonusText, bonkCanvas, [{coords, [{SX+2*DSX, SY}]},
+ {fg, TextCol},
+ {text, "Bonus:"}]),
+ gs:create(text, bonusOut, bonkCanvas, [{coords, [{SX+2*DSX+TextWidth, SY}]},
+ {fg, NrCol},
+ {width, DSX-TextWidth},
+ {text, ""}]),
+ gs:create(text, levelText,bonkCanvas, [{coords, [{SX+3*DSX, SY}]},
+ {fg, TextCol},
+ {text, "Level:"}]),
+ gs:create(text, levelOut, bonkCanvas, [{coords, [{SX+3*DSX+TextWidth, SY}]},
+ {fg, NrCol},
+ {width, DSX-TextWidth},
+ {text, ""}]),
+ gs:create(text, hiScoreText, bonkCanvas, [{coords, [{HiX, HiY}]},
+ {fg, HiHeadCol},
+ {text, "High Scores"}]),
+ gs:create(text, hiScoreOut, bonkCanvas, [{coords, [{HiX, HiY+DHY}]},
+ {fg, HiCol},
+ {justify, left},
+ {width, HiWidth}]),
+ gs:create(button, newButton,bonkWin, [{x, BX},{y, BY},
+ {enable,false},
+ {label, {text, "New Game"}},
+ {click, true},
+ {fg, BTextCol},
+ {bg, BGCol},
+ {relief, flat},
+ {activefg, BTextCol},
+ {activebg, BGCol},
+ {align, center}]),
+ gs:create(button, endButton,bonkWin, [{x, BX+DBX},{y, BY},
+ {enable,false},
+ {label, {text, "End Game"}},
+ {click, true},
+ {fg, BTextCol},
+ {bg, BGCol},
+ {relief, flat},
+ {activefg, BTextCol},
+ {activebg, BGCol},
+ {align, center}]),
+ gs:create(button, aboutButton,bonkWin, [{x, BX+2*DBX},{y, BY},
+ {enable,false},
+ {label, {text, "About"}},
+ {click, true},
+ {fg, BTextCol},
+ {bg, BGCol},
+ {relief, flat},
+ {activefg, BTextCol},
+ {activebg, BGCol},
+ {align, center}]),
+ gs:create(button, quitButton, bonkWin, [{x, BX+3*DBX},{y, BY},
+ {enable,false},
+ {label, {text, "Quit"}},
+ {click, true},
+ {fg, BTextCol},
+ {bg, BGCol},
+ {relief, flat},
+ {activefg, BTextCol},
+ {activebg, BGCol},
+ {align, center}]),
+
+ {SqrPids, Bmps} =
+ create_squares(SquareX, SquareY, SquareSize, SquareCol, SquareSpace),
+ gs:config(bonkWin, [{map, true}]),
+ {SqrPids, Bmps, Colors}.
+
+
+
+create_squares(X, Y, Size, Color, Spc) ->
+ create_squares(X, Y, Size, Color, Spc, 1, 1, [], []).
+
+
+create_squares(_X, _Y, _Size, _Color, _Spc, 4, 5, Pids, Bmps) ->
+ {Pids, Bmps};
+
+create_squares(X, Y, Size, Color, Spc, Row, 5, Pids, Bmps) ->
+ create_squares(X, Y, Size, Color, Spc, Row+1, 1, Pids, Bmps);
+
+create_squares(X, Y, Size, Color, Spc, Row, Col, Pids, Bmps) ->
+ Xpos = X+Col*Size+(Col-1)*Spc,
+ Ypos = Y+Row*Size+(Row-1)*Spc,
+ gs:create(rectangle, bonkCanvas,
+ [{coords, [{Xpos,Ypos},{Xpos+Size, Ypos+Size}]},
+ {bw, 2},{fg, Color},{buttonpress,true}]),
+ Bmp = gs:create(image, bonkCanvas,
+ [{coords, [{Xpos+1, Ypos+1}]},
+ {bitmap,lists:append(bonk_dir(), "bitmaps/bonktom")},
+ {buttonpress, true},{fg, Color}]),
+ Pid = bonk_square:start(Bmp),
+ gs:config(Bmp, [{data, Pid}]),
+ create_squares(X, Y, Size, Color, Spc, Row, Col+1, [Pid|Pids], [Bmp|Bmps]).
+
+
+
+clear_board([]) ->
+ true;
+clear_board([Square | Rest]) ->
+ gs:config(Square, [{bitmap,lists:append(bonk_dir(), "bitmaps/bonktom")}]),
+ clear_board(Rest).
+
+
+%% Prints the list on the screen.
+%% The list is on the form [[Score,Name],[Score,Name]..].
+
+display_highscore(ScoreList) ->
+ display_highscore("",ScoreList,0).
+
+display_highscore(Scores,[],_N) ->
+ gs:config(hiScoreOut,[{text,Scores}]);
+
+display_highscore(Scores,_ScoreList,10) -> % This is max number of items.
+ display_highscore(Scores,[], 10);
+
+display_highscore(Scores,[[Score,Name]|Rest],N) ->
+ NewScores = lists:append(Scores,lists:append(lists:append(Score, [32 | Name]), [10])),
+ display_highscore(NewScores,Rest,N+1).
+
+
+%% Reads the highscorelist from the file "bonk.score".
+%% The list should be an sorted erlang-list.
+
+get_highscore() ->
+ case file:consult("bonk.score") of
+ {ok,[Score_list]} ->
+ {Score_list,"./bonk.score"};
+ {error,_} ->
+ {[],"./bonk.score"}
+ end.
+
+
+%% Prints out the highscorelist and places the new score in the
+%% list if it is high enough.
+
+update_scorelist(SoundPid, Scores) ->
+ case Scores#scores.points of
+ 0 -> true;
+ Score ->
+ {ScoreL,FileName} = get_highscore(),
+ New_scorelist=update_scorelist_2(ScoreL, Score, 0, SoundPid),
+ display_highscore(New_scorelist),
+ case file:open(FileName, write) of
+ {error,_} ->
+ true;
+ {ok,FD} ->
+ io:format(FD,"~w~s~n",[New_scorelist,"."]),
+ file:close(FD)
+ end
+ end.
+
+
+update_scorelist_2([], Score, N, _SoundPid) when N < 10 ->
+ [[integer_to_list(Score),getuser()]];
+
+update_scorelist_2(_, _, N, _SoundPid) when N >= 10 ->
+ [];
+
+update_scorelist_2([[Sc, Name] | Rest], Score, N, SoundPid) ->
+ case list_to_integer(Sc) of
+ Sc_int when Sc_int < Score ->
+ if
+ N == 0 -> SoundPid ! best_score;
+ true -> SoundPid ! high_score
+ end,
+ lists:append([[integer_to_list(Score),getuser()]],
+ update_scorelist_3([[Sc,Name]|Rest],N+1));
+ _Other ->
+ lists:append([[Sc,Name]],update_scorelist_2(Rest, Score, N+1, SoundPid))
+ end.
+
+
+update_scorelist_3([],_) ->
+ [];
+
+update_scorelist_3(_,N) when N >= 10 ->
+ [];
+
+update_scorelist_3([Item|Rest],N) ->
+ lists:append([Item],update_scorelist_3(Rest,N+1)).
+
+getuser() ->
+ case os:type() of
+ {unix,_} ->
+ lists:delete(10,os:cmd("whoami"));
+ _ ->
+ "Unknown"
+ end.
+
+%% Prints out the initial values of scores, bonus, level and bombs.
+
+clear_scores(Scores) ->
+ Score = integer_to_list(Scores#scores.points),
+ Bombs = integer_to_list(Scores#scores.bombs),
+ Bonus = integer_to_list(Scores#scores.bonus),
+ Level = integer_to_list(Scores#scores.level),
+ gs:config(scoreOut,{text,Score}),
+ gs:config(bombOut,{text,Bombs}),
+ gs:config(bonusOut,{text,Bonus}),
+ gs:config(levelOut,{text,Level}).
+
+
+%% Removes everything that is present in the message-que.
+
+flush() ->
+ receive
+ _X ->
+ flush()
+ after
+ 0 ->
+ true
+ end.
+
+sleep(X) ->
+ receive after X -> true end.
+
+%% Opens a window and shows the contents of the file: "bonk.txt".
+%% The window will be removed before the function ends.
+
+display_about() ->
+ {BGColor,TextColor,Bfg} =
+ case get(colormode) of
+ bw -> {black, white, white};
+ _Color -> {black, peachpuff1, orange}
+ end,
+ Wid = 500, Hei = 635,
+ GS = gs:start(),
+ gs:create(window, aboutWin, GS, [{width, Wid}, {height,Hei},
+ {map, false},
+ {bg, BGColor},
+ {title, "About Bonk!"}]),
+ gs:create(canvas, aboutCan, aboutWin, [{width, Wid},{height, Hei},
+ {bg, black}]),
+ gs:create(button, okButton, aboutWin, [{x, Wid div 2 - 50},{y, Hei - 40},
+ {label,{text, "Ok"}}, {click, true},
+ {fg, Bfg}, {bg, BGColor},
+ {relief, flat},
+ {activefg, Bfg},
+ {activebg, BGColor}]),
+ gs:create(text, aboutText, aboutCan, [{width, Wid-30}, {coords, [{15, 0}]},
+ {fg, TextColor}, {justify, center}]),
+ case file:open(lists:append(bonk_dir(),"bonk.txt"), read) of
+ {ok, Fd} ->
+ write_text(Fd, "", io:get_line(Fd, "")),
+ file:close(Fd);
+ {error, _Reason} ->
+ gs:config(aboutText, {text, "Error: could not read the about file"})
+ end,
+
+ gs:config(aboutWin, [{map,true}]),
+ receive
+ {gs, okButton, click, _, _} ->
+ gs:destroy(aboutWin)
+ end.
+
+write_text(_Fd, Text, eof) ->
+ gs:config(aboutText, {text, Text});
+write_text(Fd, Text, More) ->
+ write_text(Fd, lists:append(Text, More), io:get_line(Fd, "")).