blob: 47416ea41fbd2645c8ee2bdc0478de1a47be9321 [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{
22 try {
Adrien Destuguesc7b42182012-01-29 17:20:30 +000023 Ihandle* motoron = (Ihandle*)IupGetAttribute(ih, "target");
24
25 uint8_t status = Device::getDevice().getStatus();
26 if (status & 8)
27 IupSetAttribute(motoron, "VALUE", "0"); // motor OFF
28 else
29 IupSetAttribute(motoron, "VALUE", "1"); // motor ON
Adrien Destugues192e2992012-02-03 18:37:06 +000030 } catch(const char*) {
31 // Silently ignore exception if device is not available - not a good
32 // idea to handle it from a timer...
33 // Keep the timer running so it starts working when the device is
34 // plugged
Adrien Destuguesc7b42182012-01-29 17:20:30 +000035 }
Adrien Destugues192e2992012-02-03 18:37:06 +000036 return IUP_DEFAULT;
37}
Adrien Destuguesc7b42182012-01-29 17:20:30 +000038
39void startPolling(Ihandle* target) {
40 Ihandle* timer = IupTimer();
41
42 IupSetAttribute(timer, "target", (const char*)target);
43
44 IupSetAttribute(timer, "TIME", "300");
45 IupSetCallback(timer, "ACTION_CB", pollStatus);
46 IupSetAttribute(timer, "RUN", "YES");
47}
48
49/* UI */
50
Adrien Destuguesc7b42182012-01-29 17:20:30 +000051Gui::Gui(int* argc, char*** argv)
52{
53 file = NULL;
54
55 IupOpen(argc, argv);
Adrien Destuguesc2a98542012-02-04 18:43:03 +000056 IupControlsOpen();
Adrien Destuguesc7b42182012-01-29 17:20:30 +000057
Adrien Destuguesc7b42182012-01-29 17:20:30 +000058 Ihandle* menu_open = IupItem("Open", NULL);
Adrien Destuguesf9263dd2012-01-29 20:06:29 +000059 Ihandle* menu_exit = IupItem("Exit", NULL);
60 Callback<Gui>::create(menu_open, "ACTION", this, &Gui::menu_open);
61 Callback<Gui>::create(menu_exit, "ACTION", this, &Gui::menu_exit);
Adrien Destuguesc7b42182012-01-29 17:20:30 +000062
63 Ihandle* menu = IupMenu(
64 IupSubmenu("File",
65 IupMenu(
66 menu_open,
Adrien Destuguesf9263dd2012-01-29 20:06:29 +000067 menu_exit,
Adrien Destuguesc7b42182012-01-29 17:20:30 +000068 NULL
69 )
70 ),
71 NULL
72 );
73
Adrien Destuguesc7b42182012-01-29 17:20:30 +000074 // CONTROL
75 Ihandle* motoron = IupProgressBar();
76 IupSetAttribute(motoron, "RASTERSIZE", "16x16");
77
Adrien Destuguesc2a98542012-02-04 18:43:03 +000078 Ihandle* playToggle = IupToggle("play", NULL);
79 Callback<Gui, int, int>::create(playToggle, "ACTION", this, &Gui::setPlaying);
80
Adrien Destuguesc7b42182012-01-29 17:20:30 +000081 // EXPLORE
82 Ihandle* platformlist = IupList(NULL);
83 IupSetAttribute(platformlist, "EXPAND", "HORIZONTAL");
84 IupSetAttribute(platformlist, "DROPDOWN", "YES");
85 IupSetAttribute(platformlist, "1", "MO5");
86 IupSetAttribute(platformlist, "VALUE", "1");
87
Adrien Destuguesc2a98542012-02-04 18:43:03 +000088 blocklist = IupTree();
Adrien Destuguesc7b42182012-01-29 17:20:30 +000089 IupSetAttribute(blocklist, "EXPAND", "VERTICAL");
Adrien Destuguesc2a98542012-02-04 18:43:03 +000090 IupSetAttribute(blocklist, "ADDEXPANDED", "NO");
91 IupSetAttribute(blocklist, "ADDROOT", "NO");
92 IupSetAttribute(blocklist, "IMAGELEAF", "IMGBLANK");
93 IupSetAttribute(blocklist, "RASTERSIZE", "140x200");
94 Callback<Gui, int, int, int>::create(blocklist, "SELECTION_CB", this, &Gui::selectBlock);
Adrien Destuguesc7b42182012-01-29 17:20:30 +000095
Adrien Destugues53c4be32012-02-05 14:25:32 +000096 hexEd = IupMatrix(NULL);
Adrien Destugues192e2992012-02-03 18:37:06 +000097
Adrien Destuguesc2a98542012-02-04 18:43:03 +000098 // Setup title cells
Adrien Destugues53c4be32012-02-05 14:25:32 +000099 IupSetAttribute(hexEd, "FONT", "Courier, 10");
100 IupSetAttribute(hexEd, "FONT*:17", "Courier, 10");
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000101 IupSetAttribute(hexEd, "NUMCOL", "17");
Adrien Destugues53c4be32012-02-05 14:25:32 +0000102 IupSetAttribute(hexEd, "WIDTHDEF", "10");
103 IupSetAttribute(hexEd, "WIDTH17", "105");
104 IupSetAttribute(hexEd, "WIDTH0", "12");
105 IupSetAttribute(hexEd, "HEIGHT0", "8");
106 IupSetAttribute(hexEd, "SIZE", "400x230");
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000107 IupSetAttribute(hexEd, "EXPAND", "YES");
Adrien Destugues53c4be32012-02-05 14:25:32 +0000108 IupSetAttribute(hexEd, "ALIGNMENT", "ALEFT");
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000109 Callback<Gui, const char*, int, int>::create(hexEd, "VALUE_CB", this, &Gui::matVal);
Adrien Destuguese43c8d02012-03-24 14:26:03 +0000110 Callback<Gui, int, int, int, const char*>::create(hexEd, "VALUE_EDIT_CB", this, &Gui::setMatVal);
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000111
112 // WINDOW LAYOUT
Adrien Destuguesc7b42182012-01-29 17:20:30 +0000113 Ihandle* tabs = IupTabs(
114 IupVbox(
115 IupHbox(
116 IupLabel("Motor"),
117 motoron,
118 NULL
119 ),
120 IupHbox(
Adrien Destugues192e2992012-02-03 18:37:06 +0000121 playToggle,
Adrien Destuguesc7b42182012-01-29 17:20:30 +0000122 IupToggle("REC",NULL),
123 NULL
124 ),
125 NULL
126 ),
127 IupVbox(
128 IupHbox(
129 IupLabel("Format:"),
130 platformlist,
131 NULL
132 ),
133 IupHbox(
134 blocklist,
135 IupVbox(
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000136 hexEd,
Adrien Destuguesc7b42182012-01-29 17:20:30 +0000137 IupLabel("Checksum:"),
138 NULL
139 ),
140 NULL
141 )
142 ),
143 NULL
144 );
145
146 IupSetAttribute(tabs,"TABTITLE0", "Control");
147 IupSetAttribute(tabs,"TABTITLE1", "Explore");
148
149 Ihandle* dialog = IupDialog(tabs);
150 IupSetAttribute(dialog, "TITLE", "CrO2 tape emulator");
151 IupSetAttributeHandle(dialog, "MENU", menu);
152 IupShow(dialog);
153
154 // Run the timer
155 startPolling(motoron);
156
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000157 // TODO the IUP main loop is blocking - it may be wise to move it out of
158 // the constructor...
Adrien Destuguesc7b42182012-01-29 17:20:30 +0000159 IupMainLoop();
Adrien Destuguesc7b42182012-01-29 17:20:30 +0000160}
161
162
163Gui::~Gui()
164{
165 delete file;
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000166
167 IupControlsClose();
168 IupClose();
Adrien Destuguesc7b42182012-01-29 17:20:30 +0000169}
170
Adrien Destugues192e2992012-02-03 18:37:06 +0000171
Adrien Destuguesc7b42182012-01-29 17:20:30 +0000172int Gui::menu_open()
173{
174 char name[65536];
175 name[0] = 0;
176 if (IupGetFile(name) == 0)
177 {
178 // Load file
Adrien Destugues53c4be32012-02-05 14:25:32 +0000179 try {
180 file = Tape::load(name);
181 } catch (const char* error) {
182 puts(error);
183 return IUP_DEFAULT;
184 }
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000185
186 // Fill in EXPLORE tab
187 int count = file->getBlockCount();
188 int lastfile = -1;
189
Adrien Destugues53c4be32012-02-05 14:25:32 +0000190 IupSetAttribute(blocklist, "DELNODE", "ALL");
191
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000192 for (int i = 0; i < count; ++i)
193 {
Adrien Destugues53c4be32012-02-05 14:25:32 +0000194 const Tape::Block& blk = file->getBlock(i);
195 if (blk.isFile())
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000196 {
Adrien Destugues53c4be32012-02-05 14:25:32 +0000197 IupSetAttributeId(blocklist, "INSERTBRANCH", lastfile, blk.getName().c_str());
198 lastfile = i;
199 } else {
200 IupSetAttributeId(blocklist, "ADDLEAF", i-1, blk.getName().c_str());
201 if (blk.isControl())
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000202 IupSetAttributeId(blocklist, "IMAGE", i, "IMGLEAF");
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000203 }
204 }
Adrien Destuguesc7b42182012-01-29 17:20:30 +0000205 }
206 return IUP_DEFAULT;
207}
Adrien Destugues192e2992012-02-03 18:37:06 +0000208
209int Gui::menu_exit()
210{
211 return IUP_CLOSE;
212}
213
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000214int Gui::selectBlock(int id, int what)
215{
216 if (what)
217 {
Adrien Destugues53c4be32012-02-05 14:25:32 +0000218 selblock = id;
219 std::ostringstream att;
220 att << (file->getBlock(id).length / 16);
221 IupSetAttribute(hexEd, "NUMLIN", att.str().c_str());
222
223 IupSetAttribute(hexEd, IUP_REDRAW, "ALL");
224 } else if (selblock == id) {
225 selblock = -1;
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000226 }
227
228 return IUP_DEFAULT;
229}
230
Adrien Destuguese43c8d02012-03-24 14:26:03 +0000231
232int Gui::setMatVal(int x, int y, const char* val)
233{
234 int pos = (y-1) * 16 + (x-1);
235
236 if (file == NULL || selblock < 0 || selblock >= file->getBlockCount())
237 return 0;
238
239 const Tape::Block& block = file->getBlock(selblock);
240 block.data[pos] = 0; // TODO parse hex val to int
241
242 return 0;
243}
244
245
Adrien Destugues53c4be32012-02-05 14:25:32 +0000246const char* Gui::matVal(int y, int x)
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000247{
Adrien Destugues53c4be32012-02-05 14:25:32 +0000248 if (y == 0)
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000249 {
Adrien Destugues53c4be32012-02-05 14:25:32 +0000250 switch(x)
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000251 {
252 case 0:
253 return "0x";
254 case 17:
255 return "ASCII";
256 default:
257 {
258 std::ostringstream name;
259 name << std::hex;
Adrien Destugues53c4be32012-02-05 14:25:32 +0000260 name << (x-1);
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000261 return name.str().c_str();
262 }
263 }
264 }
265
Adrien Destugues53c4be32012-02-05 14:25:32 +0000266 if (x == 0)
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000267 {
Adrien Destugues53c4be32012-02-05 14:25:32 +0000268 std::ostringstream name;
269 name << std::hex;
270 name << (y-1)*16;
271 return name.str().c_str();
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000272 }
273
Adrien Destugues53c4be32012-02-05 14:25:32 +0000274 if (file == NULL || selblock < 0 || selblock >= file->getBlockCount())
275 return "";
276 const Tape::Block& block = file->getBlock(selblock);
277
278 if (x == 17)
279 {
280 int off = (y-1)*16;
281 std::ostringstream txt;
282 for(int j = 0; j < 16; j++)
283 {
284 if (off + j >= block.length)
285 break;
286 char c = block.data[off+j];
287 if (isprint(c))
288 txt << c;
289 else
290 txt << '.';
291 }
292 return txt.str().c_str();
293 } else {
294
295 int pos = (y-1) * 16 + (x-1);
296 if (pos >= block.length)
297 return "";
298
299 return toHex(block.data[pos]);
300 }
Adrien Destuguesc2a98542012-02-04 18:43:03 +0000301}
302
Adrien Destugues192e2992012-02-03 18:37:06 +0000303int Gui::setPlaying(int state)
304{
305 if (state == 0)
306 {
307 // pause
308 } else {
309 // play
310 Device::getDevice().write(*file);
311 }
312
313 return IUP_DEFAULT;
314}
Adrien Destugues53c4be32012-02-05 14:25:32 +0000315
316const char* Gui::toHex(int val)
317{
318 std::ostringstream str;
319 str.flags(std::ios_base::hex | std::ios_base::uppercase);
320 str.width(2);
321 str.fill('0');
322 str << val;
323 return str.str().c_str();
324}