blob: 9eccd8b0a203681ca570a0a12687cd62fcd7cee8 [file] [log] [blame]
Adrien Destuguesc7b42182012-01-29 17:20:30 +00001/* CrO2 datassette emulator
2 * Copyright 2012, Adrien Destugues <pulkomandy@pulkomandy.ath.cx>
3 *
4 * Distributed under the terms of the MIT licence.
5 */
6
7#include "gui.h"
8
9#include "device.h"
10#include "k5.h"
11
12#include <stdint.h>
Adrien Destuguesc2a98542012-02-04 18:43:03 +000013#include <string.h>
14#include <sstream>
Adrien Destugues53c4be32012-02-05 14:25:32 +000015#include <ios>
Adrien Destuguesc2a98542012-02-04 18:43:03 +000016
Adrien Destuguesc7b42182012-01-29 17:20:30 +000017#include <iupcontrols.h>
18
Adrien Destuguesc7b42182012-01-29 17:20:30 +000019 // Start status poller "thread"
Adrien Destugues192e2992012-02-03 18:37:06 +000020int pollStatus(Ihandle* ih)
21{
Adrien Destugues6bd71442013-01-22 17:25:57 +000022 uint8_t status;
Adrien Destugues192e2992012-02-03 18:37:06 +000023 try {
Adrien Destugues6bd71442013-01-22 17:25:57 +000024 Device& dev = Device::getDevice();
25 status = dev.getStatus();
26 } catch(const char* ex) {
Adrien Destugues192e2992012-02-03 18:37:06 +000027 // Silently ignore exception if device is not available - not a good
28 // idea to handle it from a timer...
29 // Keep the timer running so it starts working when the device is
30 // plugged
Adrien Destugues6bd71442013-01-22 17:25:57 +000031 return IUP_DEFAULT;
Adrien Destuguesc7b42182012-01-29 17:20:30 +000032 }
Adrien Destugues6bd71442013-01-22 17:25:57 +000033
34 Ihandle* motoron = (Ihandle*)IupGetAttribute(ih, "target");
35 if (status & 8)
36 IupSetAttribute(motoron, "VALUE", "0"); // motor OFF
37 else
38 IupSetAttribute(motoron, "VALUE", "1"); // motor ON
Adrien Destugues192e2992012-02-03 18:37:06 +000039 return IUP_DEFAULT;
40}
Adrien Destuguesc7b42182012-01-29 17:20:30 +000041
Adrien Destugues6bd71442013-01-22 17:25:57 +000042
Adrien Destuguesc7b42182012-01-29 17:20:30 +000043void startPolling(Ihandle* target) {
44 Ihandle* timer = IupTimer();
45
46 IupSetAttribute(timer, "target", (const char*)target);
47
48 IupSetAttribute(timer, "TIME", "300");
49 IupSetCallback(timer, "ACTION_CB", pollStatus);
50 IupSetAttribute(timer, "RUN", "YES");
51}
52
53/* UI */
54
Adrien Destuguesc7b42182012-01-29 17:20:30 +000055Gui::Gui(int* argc, char*** argv)
56{
57 file = NULL;
58
59 IupOpen(argc, argv);
Adrien Destuguesc2a98542012-02-04 18:43:03 +000060 IupControlsOpen();
Adrien Destugues4a558d72012-03-24 16:10:40 +000061 IupImageLibOpen();
Adrien Destuguesc7b42182012-01-29 17:20:30 +000062
Adrien Destuguesc7b42182012-01-29 17:20:30 +000063 Ihandle* menu_open = IupItem("Open", NULL);
Adrien Destuguesf9263dd2012-01-29 20:06:29 +000064 Ihandle* menu_exit = IupItem("Exit", NULL);
65 Callback<Gui>::create(menu_open, "ACTION", this, &Gui::menu_open);
66 Callback<Gui>::create(menu_exit, "ACTION", this, &Gui::menu_exit);
Adrien Destuguesc7b42182012-01-29 17:20:30 +000067
68 Ihandle* menu = IupMenu(
69 IupSubmenu("File",
70 IupMenu(
71 menu_open,
Adrien Destuguesf9263dd2012-01-29 20:06:29 +000072 menu_exit,
Adrien Destuguesc7b42182012-01-29 17:20:30 +000073 NULL
74 )
75 ),
76 NULL
77 );
78
Adrien Destuguesc7b42182012-01-29 17:20:30 +000079 // CONTROL
80 Ihandle* motoron = IupProgressBar();
81 IupSetAttribute(motoron, "RASTERSIZE", "16x16");
82
Adrien Destugues4a558d72012-03-24 16:10:40 +000083 playToggle = IupToggle("play", NULL);
84 IupSetAttribute(playToggle, "ACTIVE", "NO");
85 IupSetAttribute(playToggle, "IMAGE", "IUP_MediaPlay");
86 Callback<Gui, int, int>::create(playToggle, "ACTION", this,
87 &Gui::setPlaying);
88
89 Ihandle* recToggle = IupToggle("rec", NULL);
90 IupSetAttribute(recToggle, "ACTIVE", "NO");
91 IupSetAttribute(recToggle, "IMAGE", "IUP_MediaRecord");
Adrien Destuguesc2a98542012-02-04 18:43:03 +000092
Adrien Destuguesc7b42182012-01-29 17:20:30 +000093 // EXPLORE
94 Ihandle* platformlist = IupList(NULL);
95 IupSetAttribute(platformlist, "EXPAND", "HORIZONTAL");
96 IupSetAttribute(platformlist, "DROPDOWN", "YES");
97 IupSetAttribute(platformlist, "1", "MO5");
98 IupSetAttribute(platformlist, "VALUE", "1");
99
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000100 blocklist = IupTree();
Adrien Destuguesc7b42182012-01-29 17:20:30 +0000101 IupSetAttribute(blocklist, "EXPAND", "VERTICAL");
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000102 IupSetAttribute(blocklist, "ADDEXPANDED", "NO");
103 IupSetAttribute(blocklist, "ADDROOT", "NO");
104 IupSetAttribute(blocklist, "IMAGELEAF", "IMGBLANK");
105 IupSetAttribute(blocklist, "RASTERSIZE", "140x200");
106 Callback<Gui, int, int, int>::create(blocklist, "SELECTION_CB", this, &Gui::selectBlock);
Adrien Destuguesc7b42182012-01-29 17:20:30 +0000107
Adrien Destugues53c4be32012-02-05 14:25:32 +0000108 hexEd = IupMatrix(NULL);
Adrien Destugues192e2992012-02-03 18:37:06 +0000109
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000110 // Setup title cells
Adrien Destugues53c4be32012-02-05 14:25:32 +0000111 IupSetAttribute(hexEd, "FONT", "Courier, 10");
112 IupSetAttribute(hexEd, "FONT*:17", "Courier, 10");
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000113 IupSetAttribute(hexEd, "NUMCOL", "17");
Adrien Destugues53c4be32012-02-05 14:25:32 +0000114 IupSetAttribute(hexEd, "WIDTHDEF", "10");
115 IupSetAttribute(hexEd, "WIDTH17", "105");
116 IupSetAttribute(hexEd, "WIDTH0", "12");
117 IupSetAttribute(hexEd, "HEIGHT0", "8");
118 IupSetAttribute(hexEd, "SIZE", "400x230");
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000119 IupSetAttribute(hexEd, "EXPAND", "YES");
Adrien Destugues53c4be32012-02-05 14:25:32 +0000120 IupSetAttribute(hexEd, "ALIGNMENT", "ALEFT");
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000121 Callback<Gui, const char*, int, int>::create(hexEd, "VALUE_CB", this, &Gui::matVal);
Adrien Destuguese43c8d02012-03-24 14:26:03 +0000122 Callback<Gui, int, int, int, const char*>::create(hexEd, "VALUE_EDIT_CB", this, &Gui::setMatVal);
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000123
124 // WINDOW LAYOUT
Adrien Destuguesc7b42182012-01-29 17:20:30 +0000125 Ihandle* tabs = IupTabs(
126 IupVbox(
127 IupHbox(
128 IupLabel("Motor"),
129 motoron,
130 NULL
131 ),
132 IupHbox(
Adrien Destugues192e2992012-02-03 18:37:06 +0000133 playToggle,
Adrien Destugues4a558d72012-03-24 16:10:40 +0000134 recToggle,
Adrien Destuguesc7b42182012-01-29 17:20:30 +0000135 NULL
136 ),
137 NULL
138 ),
139 IupVbox(
140 IupHbox(
141 IupLabel("Format:"),
142 platformlist,
143 NULL
144 ),
145 IupHbox(
146 blocklist,
147 IupVbox(
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000148 hexEd,
Adrien Destuguesc7b42182012-01-29 17:20:30 +0000149 IupLabel("Checksum:"),
150 NULL
151 ),
152 NULL
153 )
154 ),
155 NULL
156 );
157
158 IupSetAttribute(tabs,"TABTITLE0", "Control");
159 IupSetAttribute(tabs,"TABTITLE1", "Explore");
160
161 Ihandle* dialog = IupDialog(tabs);
162 IupSetAttribute(dialog, "TITLE", "CrO2 tape emulator");
163 IupSetAttributeHandle(dialog, "MENU", menu);
164 IupShow(dialog);
165
166 // Run the timer
167 startPolling(motoron);
168
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000169 // TODO the IUP main loop is blocking - it may be wise to move it out of
170 // the constructor...
Adrien Destuguesc7b42182012-01-29 17:20:30 +0000171 IupMainLoop();
Adrien Destuguesc7b42182012-01-29 17:20:30 +0000172}
173
174
175Gui::~Gui()
176{
177 delete file;
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000178
179 IupControlsClose();
180 IupClose();
Adrien Destuguesc7b42182012-01-29 17:20:30 +0000181}
182
Adrien Destugues192e2992012-02-03 18:37:06 +0000183
Adrien Destuguesc7b42182012-01-29 17:20:30 +0000184int Gui::menu_open()
185{
186 char name[65536];
187 name[0] = 0;
188 if (IupGetFile(name) == 0)
189 {
190 // Load file
Adrien Destugues53c4be32012-02-05 14:25:32 +0000191 try {
192 file = Tape::load(name);
193 } catch (const char* error) {
Adrien Destugues45825a32013-01-29 20:11:50 +0000194 // FIXME popup an error dialog
Adrien Destugues53c4be32012-02-05 14:25:32 +0000195 puts(error);
196 return IUP_DEFAULT;
197 }
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000198
199 // Fill in EXPLORE tab
200 int count = file->getBlockCount();
201 int lastfile = -1;
202
Adrien Destugues53c4be32012-02-05 14:25:32 +0000203 IupSetAttribute(blocklist, "DELNODE", "ALL");
204
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000205 for (int i = 0; i < count; ++i)
206 {
Adrien Destugues53c4be32012-02-05 14:25:32 +0000207 const Tape::Block& blk = file->getBlock(i);
208 if (blk.isFile())
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000209 {
Adrien Destugues53c4be32012-02-05 14:25:32 +0000210 IupSetAttributeId(blocklist, "INSERTBRANCH", lastfile, blk.getName().c_str());
211 lastfile = i;
212 } else {
213 IupSetAttributeId(blocklist, "ADDLEAF", i-1, blk.getName().c_str());
214 if (blk.isControl())
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000215 IupSetAttributeId(blocklist, "IMAGE", i, "IMGLEAF");
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000216 }
217 }
Adrien Destugues4a558d72012-03-24 16:10:40 +0000218
219 // Enable play button
220 IupSetAttribute(playToggle, "ACTIVE", "YES");
Adrien Destuguesc7b42182012-01-29 17:20:30 +0000221 }
222 return IUP_DEFAULT;
223}
Adrien Destugues192e2992012-02-03 18:37:06 +0000224
225int Gui::menu_exit()
226{
227 return IUP_CLOSE;
228}
229
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000230int Gui::selectBlock(int id, int what)
231{
232 if (what)
233 {
Adrien Destugues53c4be32012-02-05 14:25:32 +0000234 selblock = id;
235 std::ostringstream att;
236 att << (file->getBlock(id).length / 16);
237 IupSetAttribute(hexEd, "NUMLIN", att.str().c_str());
238
239 IupSetAttribute(hexEd, IUP_REDRAW, "ALL");
240 } else if (selblock == id) {
241 selblock = -1;
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000242 }
243
244 return IUP_DEFAULT;
245}
246
Adrien Destuguese43c8d02012-03-24 14:26:03 +0000247
248int Gui::setMatVal(int x, int y, const char* val)
249{
250 int pos = (y-1) * 16 + (x-1);
251
252 if (file == NULL || selblock < 0 || selblock >= file->getBlockCount())
253 return 0;
254
255 const Tape::Block& block = file->getBlock(selblock);
256 block.data[pos] = 0; // TODO parse hex val to int
257
258 return 0;
259}
260
261
Adrien Destugues53c4be32012-02-05 14:25:32 +0000262const char* Gui::matVal(int y, int x)
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000263{
Adrien Destugues53c4be32012-02-05 14:25:32 +0000264 if (y == 0)
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000265 {
Adrien Destugues53c4be32012-02-05 14:25:32 +0000266 switch(x)
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000267 {
268 case 0:
269 return "0x";
270 case 17:
271 return "ASCII";
272 default:
273 {
274 std::ostringstream name;
275 name << std::hex;
Adrien Destugues53c4be32012-02-05 14:25:32 +0000276 name << (x-1);
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000277 return name.str().c_str();
278 }
279 }
280 }
281
Adrien Destugues53c4be32012-02-05 14:25:32 +0000282 if (x == 0)
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000283 {
Adrien Destugues53c4be32012-02-05 14:25:32 +0000284 std::ostringstream name;
285 name << std::hex;
286 name << (y-1)*16;
287 return name.str().c_str();
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000288 }
289
Adrien Destugues53c4be32012-02-05 14:25:32 +0000290 if (file == NULL || selblock < 0 || selblock >= file->getBlockCount())
291 return "";
292 const Tape::Block& block = file->getBlock(selblock);
293
294 if (x == 17)
295 {
296 int off = (y-1)*16;
297 std::ostringstream txt;
298 for(int j = 0; j < 16; j++)
299 {
300 if (off + j >= block.length)
301 break;
302 char c = block.data[off+j];
303 if (isprint(c))
304 txt << c;
305 else
306 txt << '.';
307 }
308 return txt.str().c_str();
309 } else {
310
311 int pos = (y-1) * 16 + (x-1);
312 if (pos >= block.length)
313 return "";
314
315 return toHex(block.data[pos]);
316 }
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000317}
318
Adrien Destugues192e2992012-02-03 18:37:06 +0000319int Gui::setPlaying(int state)
320{
321 if (state == 0)
322 {
323 // pause
Adrien Destugues4a558d72012-03-24 16:10:40 +0000324 } else try {
Adrien Destugues192e2992012-02-03 18:37:06 +0000325 // play
326 Device::getDevice().write(*file);
Adrien Destugues4a558d72012-03-24 16:10:40 +0000327 } catch (const char* ex) {
328 IupMessage("CrO2 error", ex);
Adrien Destugues192e2992012-02-03 18:37:06 +0000329 }
330
331 return IUP_DEFAULT;
332}
Adrien Destugues53c4be32012-02-05 14:25:32 +0000333
334const char* Gui::toHex(int val)
335{
336 std::ostringstream str;
337 str.flags(std::ios_base::hex | std::ios_base::uppercase);
338 str.width(2);
339 str.fill('0');
340 str << val;
341 return str.str().c_str();
342}