1//============================================================================
2//
3// SSSS tt lll lll
4// SS SS tt ll ll
5// SS tttttt eeee ll ll aaaa
6// SSSS tt ee ee ll ll aa
7// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8// SS SS tt ee ll ll aa aa
9// SSSS ttt eeeee llll llll aaaaa
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//============================================================================
17
18#include <sstream>
19
20#include "OSystem.hxx"
21#include "FrameBuffer.hxx"
22#include "FBSurface.hxx"
23#include "Widget.hxx"
24#include "GuiObject.hxx"
25#include "ContextMenu.hxx"
26#include "TiaZoomWidget.hxx"
27#include "Debugger.hxx"
28#include "DebuggerParser.hxx"
29#include "PNGLibrary.hxx"
30#include "TIADebug.hxx"
31#include "TIASurface.hxx"
32#include "TIA.hxx"
33#include "TimerManager.hxx"
34
35#include "TiaOutputWidget.hxx"
36
37// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
38TiaOutputWidget::TiaOutputWidget(GuiObject* boss, const GUI::Font& font,
39 int x, int y, int w, int h)
40 : Widget(boss, font, x, y, w, h),
41 CommandSender(boss),
42 myZoom(nullptr),
43 myClickX(0),
44 myClickY(0)
45{
46 // Create context menu for commands
47 VariantList l;
48 VarList::push_back(l, "Fill to scanline", "scanline");
49 VarList::push_back(l, "Toggle breakpoint", "bp");
50 VarList::push_back(l, "Set zoom position", "zoom");
51#ifdef PNG_SUPPORT
52 VarList::push_back(l, "Save snapshot", "snap");
53#endif
54 myMenu = make_unique<ContextMenu>(this, font, l);
55}
56
57// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
58void TiaOutputWidget::loadConfig()
59{
60 setDirty();
61}
62
63// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
64void TiaOutputWidget::saveSnapshot(int execDepth, const string& execPrefix)
65{
66#ifdef PNG_SUPPORT
67 if(execDepth > 0)
68 drawWidget(false);
69
70 ostringstream sspath;
71 sspath << instance().snapshotSaveDir()
72 << instance().console().properties().get(PropType::Cart_Name);
73 sspath << "_dbg_";
74 if (execDepth > 0 && !execPrefix.empty()) {
75 sspath << execPrefix << "_";
76 }
77 sspath << std::hex << std::setw(8) << std::setfill('0')
78 << uInt32(TimerManager::getTicks()/1000) << ".png";
79
80 const uInt32 width = instance().console().tia().width(),
81 height = instance().console().tia().height();
82 FBSurface& s = dialog().surface();
83
84 // to skip borders, add 1 to origin
85 int x = _x + 1, y = _y + 1;
86 Common::Rect rect(x, y, x + width*2, y + height);
87 string message = "Snapshot saved";
88 try
89 {
90 instance().png().saveImage(sspath.str(), s, rect);
91 }
92 catch(const runtime_error& e)
93 {
94 message = e.what();
95 }
96 if (execDepth == 0) {
97 instance().frameBuffer().showMessage(message);
98 }
99#else
100 instance().frameBuffer().showMessage("PNG image saving not supported");
101#endif
102}
103
104// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
105void TiaOutputWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount)
106{
107 if (b == MouseButton::LEFT)
108 myZoom->setPos(x, y);
109 // Grab right mouse button for command context menu
110 else if(b == MouseButton::RIGHT)
111 {
112 myClickX = x;
113 myClickY = y;
114
115 // Add menu at current x,y mouse location
116 myMenu->show(x + getAbsX(), y + getAbsY(), dialog().surface().dstRect());
117 }
118}
119
120// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
121void TiaOutputWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
122{
123 uInt32 ystart = instance().console().tia().ystart();
124
125 switch(cmd)
126 {
127 case ContextMenu::kItemSelectedCmd:
128 {
129 const string& rmb = myMenu->getSelectedTag().toString();
130
131 if(rmb == "scanline")
132 {
133 ostringstream command;
134 int lines = myClickY + ystart - instance().console().tia().scanlines();
135
136 if(lines < 0)
137 lines += instance().console().tia().scanlinesLastFrame();
138 if(lines > 0)
139 {
140 command << "scanline #" << lines;
141 string message = instance().debugger().parser().run(command.str());
142 instance().frameBuffer().showMessage(message);
143 }
144 }
145 else if(rmb == "bp")
146 {
147 ostringstream command;
148 int scanline = myClickY + ystart;
149 command << "breakif _scan==#" << scanline;
150 string message = instance().debugger().parser().run(command.str());
151 instance().frameBuffer().showMessage(message);
152 }
153 else if(rmb == "zoom")
154 {
155 if(myZoom)
156 myZoom->setPos(myClickX, myClickY);
157 }
158 else if(rmb == "snap")
159 {
160 instance().debugger().parser().run("savesnap");
161 }
162 break;
163 }
164 }
165}
166
167// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
168void TiaOutputWidget::drawWidget(bool hilite)
169{
170//cerr << "TiaOutputWidget::drawWidget\n";
171 const uInt32 width = instance().console().tia().width(),
172 height = instance().console().tia().height();
173 FBSurface& s = dialog().surface();
174
175 s.vLine(_x + _w + 1, _y, height, kColor);
176 s.hLine(_x, _y + height + 1, _x +_w + 1, kColor);
177
178 // Get current scanline position
179 // This determines where the frame greying should start, and where a
180 // scanline 'pointer' should be drawn
181 uInt32 scanx, scany, scanoffset;
182 bool visible = instance().console().tia().electronBeamPos(scanx, scany);
183 scanoffset = width * scany + scanx;
184 uInt8* tiaOutputBuffer = instance().console().tia().outputBuffer();
185 TIASurface& tiaSurface(instance().frameBuffer().tiaSurface());
186
187 for(uInt32 y = 0, i = 0; y < height; ++y)
188 {
189 uInt32* line_ptr = myLineBuffer;
190 for(uInt32 x = 0; x < width; ++x, ++i)
191 {
192 uInt8 shift = i >= scanoffset ? 1 : 0;
193 uInt32 pixel = tiaSurface.mapIndexedPixel(tiaOutputBuffer[i], shift);
194 *line_ptr++ = pixel;
195 *line_ptr++ = pixel;
196 }
197 s.drawPixels(myLineBuffer, _x + 1, _y + 1 + y, width << 1);
198 }
199
200 // Show electron beam position
201 if(visible && scanx < width && scany+2u < height)
202 s.fillRect(_x + 1 + (scanx<<1), _y + 1 + scany, 3, 3, kColorInfo);
203}
204