diff options
Diffstat (limited to 'lib/debugger/src/dbg_ui_mon_win.erl')
-rw-r--r-- | lib/debugger/src/dbg_ui_mon_win.erl | 564 |
1 files changed, 564 insertions, 0 deletions
diff --git a/lib/debugger/src/dbg_ui_mon_win.erl b/lib/debugger/src/dbg_ui_mon_win.erl new file mode 100644 index 0000000000..ba2f94c550 --- /dev/null +++ b/lib/debugger/src/dbg_ui_mon_win.erl @@ -0,0 +1,564 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-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(dbg_ui_mon_win). + +%% External exports +-export([init/0]). +-export([create_win/3, get_window/1, + show_option/3, + enable/2, is_enabled/1, select/2, + add_module/3, delete_module/2, + add_process/6, update_process/4, clear_processes/1, + add_break/3, update_break/2, delete_break/2, + clear_breaks/1, clear_breaks/2, + handle_event/2 + ]). + +-define(default_rows,50). + +-record(moduleInfo, {module, menubtn}). +-record(procInfo, {pid, row}). +-record(breakInfo, {point, status, break}). +-record(winInfo, {window, % gsobj() + grid, % gsobj() + row, % int() Last row in grid + + focus, % int() Selected row in grid + + modules=[], % [#moduleInfo{}] Known modules + processes=[], % [#procInfo{}] Known processes + breaks=[], % [#breakInfo{}] Known breakpoints + + listbox, % gsobj() Listinng known modules + + %% Auto attach buttons + fbutton, % gsobj() + bbutton, % gsobj() + ebutton, % gsobj() + selected=[], % ['First Call'|'On Break'|'On Exit'] + + slabel, % showing Stack Trace option + blabel % showing Back Trace Size + }). + +%%==================================================================== +%% External exports +%%==================================================================== + +init() -> + dbg_ui_win:init(). + +%%-------------------------------------------------------------------- +%% create_win(GS, Title, Menus) -> #winInfo{} +%% GS = gsobj() +%% Title = string() +%% Menus = [menu()] See dbg_ui_win.erl +%%-------------------------------------------------------------------- + +-define(PAD, 5). +-define(Wf, 150). +-define(Wg, 770). +-define(W, 800). +-define(H, 390). + +create_win(GS, Title, Menus) -> + Win = gs:window(GS, [{title, Title}, + {width, ?W}, {height, ?H}, + {configure,true}, {destroy,true}, + {keypress,true}, {motion,true}]), + + MenuBar = gs:menubar(Win, []), + dbg_ui_win:create_menus(MenuBar, Menus), + dbg_ui_winman:windows_menu(MenuBar), + + Font = dbg_ui_win:font(normal), + + Frame = gs:frame(Win, [{x, ?PAD}, {y, 30}, + {width, ?Wf}, {height, ?H}]), + Hlb = 200, + Listbox = gs:listbox(Frame, [{x, 0}, {y, 0}, + {width, ?Wf}, {height, Hlb}, + {data, listbox}, + {doubleclick, true}, + {items, []}]), + gs:label(Frame, [{x, 0}, {y, Hlb}, {width, ?Wf}, {height, 20}, + {align, w}, + {label, {text, "Auto Attach:"}}, {font, Font}]), + Fbtn = gs:checkbutton(Frame, [{x, 0}, {y, Hlb+20}, + {width, ?Wf}, {height, 20}, + {label, {text, 'First Call'}}, + {align, w}, {font, Font}, + {data, autoattach}]), + Bbtn = gs:checkbutton(Frame, [{x, 0}, {y, Hlb+40}, + {width, ?Wf}, {height, 20}, + {label, {text, 'On Break'}}, + {align, w}, {font, Font}, + {data, autoattach}]), + Ebtn = gs:checkbutton(Frame, [{x, 0}, {y, Hlb+60}, + {width, ?Wf}, {height, 20}, + {label, {text, 'On Exit'}}, + {align, w}, {font, Font}, + {data, autoattach}]), + SLabel = gs:label(Frame, [{x, 0}, {y, Hlb+80}, + {width, ?Wf}, {height, 40}, + {font, Font}, {align, w}]), + BLabel = gs:label(Frame, [{x, 0}, {y, Hlb+120}, + {width, ?Wf}, {height, 40}, + {font, Font}, {align, w}]), + + Grid = gs:grid(Win, [{x, 2*?PAD+?Wf}, {y, 30}, + {width, ?W-(2*?PAD+?Wf)}, {height, ?H-30}, + {bg, grey}, {fg, black}, + {vscroll, right}, {hscroll, bottom}, + calc_columnwidths(?Wg), + {rows, {1,?default_rows}}]), + gs:gridline(Grid, [{row, 1}, {bw, 5}, {fg, blue}, + {font, Font}, + {text, {1,"Pid"}}, {text, {2,"Initial Call"}}, + {text, {3,"Name"}}, {text, {4,"Status"}}, + {text, {5,"Information"}}]), + + gs:config(Win, {map, true}), + #winInfo{window=Win, grid=Grid, row=1, focus=0, + listbox=Listbox, + fbutton=Fbtn, bbutton=Bbtn, ebutton=Ebtn, + slabel=SLabel, blabel=BLabel}. + +%%-------------------------------------------------------------------- +%% get_window(WinInfo) -> Window +%% WinInfo = #winInfo{} +%% Window = gsobj() +%%-------------------------------------------------------------------- +get_window(WinInfo) -> + WinInfo#winInfo.window. + +%%-------------------------------------------------------------------- +%% show_option(WinInfo, Option, Value) -> void() +%% WinInfo = #winInfo{} +%% Option = auto_attach | stack_trace | back_trace +%% Value = [Flag] % Option==auto_attach +%% Flag = init | break | exit +%% | true | all | no_tail | false % Option==stack_trace +%% | int() % Option==back_trace +%%-------------------------------------------------------------------- +show_option(WinInfo, Option, Value) -> + case Option of + + auto_attach -> + lists:foreach(fun (Button) -> + gs:config(Button, {select, false}) + end, + option_buttons(WinInfo, [init, break, exit])), + lists:foreach(fun (Button) -> + gs:config(Button, {select, true}) + end, + option_buttons(WinInfo, Value)); + + stack_trace -> + Text = case Value of + all -> "Stack Trace:\n On (with tail)"; + true -> "Stack Trace:\n On (with tail)"; + no_tail -> "Stack Trace:\n On (no tail)"; + false -> "Stack Trace:\n Off" + end, + gs:config(WinInfo#winInfo.slabel, {label, {text, Text}}); + + back_trace -> + Text = "Back Trace Size:\n " ++ integer_to_list(Value), + gs:config(WinInfo#winInfo.blabel, {label, {text, Text}}) + end. + +option_buttons(WinInfo, [init|Flags]) -> + [WinInfo#winInfo.fbutton|option_buttons(WinInfo, Flags)]; +option_buttons(WinInfo, [break|Flags]) -> + [WinInfo#winInfo.bbutton|option_buttons(WinInfo, Flags)]; +option_buttons(WinInfo, [exit|Flags]) -> + [WinInfo#winInfo.ebutton|option_buttons(WinInfo, Flags)]; +option_buttons(_WinInfo, []) -> + []. + +%%-------------------------------------------------------------------- +%% enable([MenuItem], Bool) +%% is_enabled(MenuItem) -> Bool +%% MenuItem = atom() +%% Bool = boolean() +%%-------------------------------------------------------------------- +enable(MenuItems, Bool) -> + lists:foreach(fun(MenuItem) -> + gs:config(MenuItem, {enable, Bool}) + end, + MenuItems). + +is_enabled(MenuItem) -> + gs:read(MenuItem, enable). + +%%-------------------------------------------------------------------- +%% select(MenuItem, Bool) +%% MenuItem = atom() +%% Bool = boolean() +%%-------------------------------------------------------------------- +select(MenuItem, Bool) -> + dbg_ui_win:select(MenuItem, Bool). + +%%-------------------------------------------------------------------- +%% add_module(WinInfo, Name, Mod) -> WinInfo +%% WinInfo = #winInfo{} +%% Name = atom() +%% Mod = atom() +%%-------------------------------------------------------------------- +add_module(WinInfo, Menu, Mod) -> + Modules = WinInfo#winInfo.modules, + case lists:keysearch(Mod, #moduleInfo.module, Modules) of + {value, _ModInfo} -> WinInfo; + false -> + %% Create a menu for the module + Font = dbg_ui_win:font(normal), + MenuBtn = gs:menuitem(Menu, [{label, {text,Mod}}, + {font, Font}, + {itemtype, cascade}]), + SubMenu = gs:menu(MenuBtn, []), + gs:menuitem(SubMenu, [{label, {text,"View"}}, + {font, Font}, + {data, {module,Mod,view}}]), + gs:menuitem(SubMenu, [{label, {text,"Delete"}}, + {font, Font}, + {data, {module,Mod,delete}}]), + + %% Add the module to the listbox + gs:config(WinInfo#winInfo.listbox, {add, Mod}), + + ModInfo = #moduleInfo{module=Mod, menubtn=MenuBtn}, + WinInfo#winInfo{modules=[ModInfo | Modules]} + end. + +%%-------------------------------------------------------------------- +%% delete_module(WinInfo, Mod) -> WinInfo +%% WinInfo = #winInfo{} +%% Mod = atom() +%%-------------------------------------------------------------------- +delete_module(WinInfo, Mod) -> + {value, ModInfo} = lists:keysearch(Mod, #moduleInfo.module, + WinInfo#winInfo.modules), + gs:destroy(ModInfo#moduleInfo.menubtn), + delete_module(WinInfo#winInfo.listbox, atom_to_list(Mod), 0), + WinInfo#winInfo{modules=lists:keydelete(Mod, #moduleInfo.module, + WinInfo#winInfo.modules)}. + +delete_module(Listbox, ModS, Index) -> + case gs:read(Listbox, {get, Index}) of + ModS -> + gs:config(Listbox, {del, Index}); + _OtherModS -> + delete_module(Listbox, ModS, Index+1) + end. + +%%-------------------------------------------------------------------- +%% add_process(WinInfo, Pid, Name, Function, Status, Info) -> WinInfo +%% WinInfo = #winInfo{} +%% Pid = pid() +%% Name = undefined | atom() +%% Function = {Mod, Func, Args} +%% Status = idle | running | break | exit +%% Info = {} | term() +%%-------------------------------------------------------------------- +add_process(WinInfo, Pid, Name, {Mod,Func,Args}, Status, Info) -> + Grid = WinInfo#winInfo.grid, + Row = (WinInfo#winInfo.row)+1, + GridLine = case gs:read(Grid, {obj_at_row, Row}) of + undefined -> + if Row>?default_rows -> + gs:config(Grid,[{rows,{1,Row}}]); + true -> ok + end, + gs:gridline(Grid,[{row,Row}, {bw,5}, {fg,black}, + {font,dbg_ui_win:font(normal)}, + {click, true}, + {doubleclick, true}]); + GSObj -> + GSObj + end, + Name2 = case Name of undefined -> ""; _ -> Name end, + FuncS = io_lib:format("~w:~w/~w", [Mod, Func, length(Args)]), + Info2 = case Info of {} -> ""; _ -> Info end, + Options = [{text, {1,Pid}}, {text, {2,FuncS}}, {text, {3,Name2}}, + {text, {4,Status}}, {text, {5,Info2}}, + {data, {gridline, Pid}}], + gs:config(GridLine, Options), + + ProcInfo = #procInfo{pid=Pid, row=Row}, + WinInfo#winInfo{processes=[ProcInfo|WinInfo#winInfo.processes], + row=Row}. + +%%-------------------------------------------------------------------- +%% update_process(WinInfo, Pid, Status, Info) +%% WinInfo = #winInfo{} +%% Pid = pid() +%% Status = idle | running | break | exit +%% Info = {} | term() +%%-------------------------------------------------------------------- +update_process(WinInfo, Pid, Status, Info) -> + {value, ProcInfo} = lists:keysearch(Pid, #procInfo.pid, + WinInfo#winInfo.processes), + + Grid = WinInfo#winInfo.grid, + GridLine = gs:read(Grid, {obj_at_row, ProcInfo#procInfo.row}), + + Info2 = case Info of {} -> ""; _ -> Info end, + gs:config(GridLine, [{text, {4,Status}}, {text, {5,Info2}}]). + +%%-------------------------------------------------------------------- +%% clear_processes(WinInfo) -> WinInfo +%% WinInfo = #winInfo{} +%%-------------------------------------------------------------------- +clear_processes(WinInfo) -> + Grid = WinInfo#winInfo.grid, + Max = WinInfo#winInfo.row, + clear_processes(Grid, 2, Max), + gs:config(Grid,[{rows,{1,?default_rows}}]), + WinInfo#winInfo{row=1, focus=0, processes=[]}. + +clear_processes(Grid, Row, Max) when Row=<Max -> + GridLine = gs:read(Grid, {obj_at_row, Row}), + case gs:read(GridLine,{text,4}) of + "exit" -> + Pid = list_to_pid(gs:read(GridLine,{text,1})), + dbg_ui_winman:clear_process(dbg_ui_trace:title(Pid)); + _ -> + ok + end, + + Options = [{fg, black}, + {{text,1}, ""}, {{text,2},""}, {{text,3},""}, + {{text,4}, ""}, {{text,5},""}, + {data, []}], + gs:config(GridLine, Options), + clear_processes(Grid, Row+1, Max); +clear_processes(_Grid, Row, Max) when Row>Max -> + done. + +%%-------------------------------------------------------------------- +%% add_break(WinInfo, Name, {Point, Options}) -> WinInfo +%% WinInfo = #winInfo{} +%% Name = atom() +%% Point = {Mod, Line} +%% Options = [Status, Action, Mods, Cond] +%% Status = active | inactive +%% Action = enable | disable | delete +%% Mods = null (not used) +%% Cond = null | {Mod, Func} +%%-------------------------------------------------------------------- +add_break(WinInfo, Menu, {Point, Options}) -> + Break = dbg_ui_win:add_break(Menu, Point), + dbg_ui_win:update_break(Break, Options), + BreakInfo = #breakInfo{point=Point, break=Break}, + WinInfo#winInfo{breaks=[BreakInfo|WinInfo#winInfo.breaks]}. + +%%-------------------------------------------------------------------- +%% update_break(WinInfo, {Point, Options}) +%% WinInfo = #winInfo{} +%% Point = {Mod, Line} +%% Options = [Status, Action, Mods, Cond] +%% Status = active | inactive +%% Action = enable | disable | delete +%% Mods = null (not used) +%% Cond = null | {Mod, Func} +%%-------------------------------------------------------------------- +update_break(WinInfo, {Point, Options}) -> + {value, BreakInfo} = lists:keysearch(Point, #breakInfo.point, + WinInfo#winInfo.breaks), + dbg_ui_win:update_break(BreakInfo#breakInfo.break, Options). + +%%-------------------------------------------------------------------- +%% delete_break(WinInfo, Point) -> WinInfo +%% WinInfo = #winInfo{} +%% Point = {Mod, Line} +%%-------------------------------------------------------------------- +delete_break(WinInfo, Point) -> + {value, BreakInfo} = lists:keysearch(Point, #breakInfo.point, + WinInfo#winInfo.breaks), + dbg_ui_win:delete_break(BreakInfo#breakInfo.break), + WinInfo#winInfo{breaks=lists:keydelete(Point, #breakInfo.point, + WinInfo#winInfo.breaks)}. + +%%-------------------------------------------------------------------- +%% clear_breaks(WinInfo) -> WinInfo +%% clear_breaks(WinInfo, Mod) -> WinInfo +%% WinInfo = #winInfo{} +%%-------------------------------------------------------------------- +clear_breaks(WinInfo) -> + lists:foreach(fun(BreakInfo) -> + dbg_ui_win:delete_break(BreakInfo#breakInfo.break) + end, + WinInfo#winInfo.breaks), + WinInfo#winInfo{breaks=[]}. +clear_breaks(WinInfo, Mod) -> + Fun = + fun(BreakInfo) -> + case BreakInfo#breakInfo.point of + {Mod, _Line} -> + dbg_ui_win:delete_break(BreakInfo#breakInfo.break), + false; + _ -> true + end + end, + Breaks = lists:filter(Fun, WinInfo#winInfo.breaks), + WinInfo#winInfo{breaks=Breaks}. + +%%-------------------------------------------------------------------- +%% handle_event(GSEvent, WinInfo) -> Command +%% GSEvent = {gs, Id, Event, Data, Arg} +%% WinInfo = #winInfo{} +%% Command = ignore +%% | stopped +%% | {coords, {X,Y}} +%% +%% | {shortcut, Key} +%% | MenuItem | {Menu, [MenuItem]} +%% MenuItem = Menu = atom() +%% | {break, Point, What} +%% What = delete | {status, Status} | {trigger, Trigger} +%% | {module, Mod, What} +%% What = view | delete +%% +%% | {focus, Pid, WinInfo} +%% | default +%%-------------------------------------------------------------------- +%% Window events +handle_event({gs, _Id, configure, _Data, [W, H |_]}, WinInfo) -> + configure(WinInfo, {W, H}), + ignore; +handle_event({gs, _Id, destroy, _Data, _Arg}, _WinInfo) -> + stopped; +handle_event({gs, _Id, motion, _Data, [X,Y]}, WinInfo) -> + {LastX, LastY} = dbg_ui_win:motion(X, Y), + Win = WinInfo#winInfo.window, + {coords, {gs:read(Win, x)+LastX-5, gs:read(Win, y)+LastY-5}}; + +%% Menus and keyboard shortcuts +handle_event({gs, _Id, keypress, _Data, [Key,_,_,1]}, _WinInfo) when + Key/='Up', Key/='Down', Key/=p, Key/=n -> + {shortcut, Key}; +handle_event({gs, _Id, click, {dbg_ui_winman, Win}, _Arg}, _WinInfo) -> + dbg_ui_winman:raise(Win), + ignore; +handle_event({gs, _Id, click, {menuitem, Name}, _Arg}, _WinInfo) -> + Name; +handle_event({gs, _Id, click, {menu, Menu}, _Arg}, _WinInfo) -> + Names = dbg_ui_win:selected(Menu), + {Menu, Names}; +handle_event({gs, _Id, click, {break, Point, What}, _Arg}, _WinInfo) -> + {break, Point, What}; +handle_event({gs, _Id, click, {module, Mod, What}, _Arg}, _WinInfo) -> + {module, Mod, What}; + +%% Listbox +handle_event({gs, _Id, doubleclick, listbox, [_Index, ModS|_]}, _WI) -> + {module, list_to_atom(ModS), view}; + +%% Auto attach buttons +handle_event({gs, _Id, click, autoattach, _Arg}, WinInfo) -> + Names = lists:foldl(fun (Button, NamesAcc) -> + case gs:read(Button, select) of + true -> + {text, Name} = + gs:read(Button, label), + [list_to_atom(Name)|NamesAcc]; + false -> + NamesAcc + end + end, + [], + [WinInfo#winInfo.ebutton, + WinInfo#winInfo.bbutton, + WinInfo#winInfo.fbutton]), + {'Auto Attach', Names}; + +%% Process grid +handle_event({gs, _Id, keypress, _Data, [Key|_]}, WinInfo) when + Key=='Up'; Key=='Down' -> + Dir = if Key=='Up' -> up; Key=='Down' -> down end, + Row = move(WinInfo, Dir), + + if Row>1 -> + WinInfo2 = highlight(WinInfo, Row), + {value, #procInfo{pid=Pid}} = + lists:keysearch(Row, #procInfo.row, WinInfo#winInfo.processes), + {focus, Pid, WinInfo2}; + true -> + ignore + end; +handle_event({gs, _Id, click, {gridline, Pid}, [_Col,Row|_]}, WinInfo) -> + WinInfo2 = highlight(WinInfo, Row), + {focus, Pid, WinInfo2}; +handle_event({gs, _Id, doubleclick, _Data, _Arg}, _WinInfo) -> + default; + +handle_event(_GSEvent, _WinInfo) -> + ignore. + +move(WinInfo, Dir) -> + Row = WinInfo#winInfo.focus, + Last = WinInfo#winInfo.row, + + if + Dir==up, Row>1 -> Row-1; + Dir==down, Row<Last -> Row+1; + true -> Row + end. + +highlight(WinInfo, Row) -> + Grid = WinInfo#winInfo.grid, + case WinInfo#winInfo.focus of + 0 -> ignore; + Focus -> + GridLine1 = gs:read(Grid, {obj_at_row, Focus}), + gs:config(GridLine1, {fg, black}) + end, + GridLine2 = gs:read(Grid, {obj_at_row, Row}), + gs:config(GridLine2, {fg, white}), + WinInfo#winInfo{focus=Row}. + + +%%==================================================================== +%% Internal functions +%%==================================================================== + +configure(WinInfo, {W, H}) -> + Grid = WinInfo#winInfo.grid, + NewW = W - (2*?PAD+?Wf), + Dx = NewW - gs:read(Grid, width), + Dy = H-42 - gs:read(Grid, height), + if + (Dx+Dy)=/=0 -> + gs:config(Grid, [{width, NewW}, {height, H-30}]), + Cols = calc_columnwidths(NewW), + gs:config(Grid, Cols); + true -> + ok + end. + +calc_columnwidths(Width) -> + W = if + Width=<?Wg -> ?Wg; + true -> Width + end, + First = lists:map(fun (X) -> round(X) end, + [0.13*W, 0.27*W, 0.18*W, 0.18*W]), + Last = W - lists:sum(First) - 30, + {columnwidths, First++[Last]}. |