source: thomson/elec/CrO2/software/gui.cpp@ 4a558d7

main
Last change on this file since 4a558d7 was 4a558d7, checked in by Adrien Destugues <pulkomandy@…>, 12 years ago
  • Use graphical buttons for play/rec
  • Show error message when play fails.

git-svn-id: svn://localhost/thomson@27 85ae3b6b-dc8f-4344-a89d-598714f2e4e5

  • Property mode set to 100644
File size: 7.2 KB
RevLine 
[c7b4218]1/* 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>
[c2a9854]13#include <string.h>
14#include <sstream>
[53c4be3]15#include <ios>
[c2a9854]16
[c7b4218]17#include <iupcontrols.h>
18
19 // Start status poller "thread"
[192e299]20int pollStatus(Ihandle* ih)
21{
22 try {
[c7b4218]23 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
[192e299]30 } 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
[c7b4218]35 }
[192e299]36 return IUP_DEFAULT;
37}
[c7b4218]38
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
51Gui::Gui(int* argc, char*** argv)
52{
53 file = NULL;
54
55 IupOpen(argc, argv);
[c2a9854]56 IupControlsOpen();
[4a558d7]57 IupImageLibOpen();
[c7b4218]58
59 Ihandle* menu_open = IupItem("Open", NULL);
[f9263dd]60 Ihandle* menu_exit = IupItem("Exit", NULL);
61 Callback<Gui>::create(menu_open, "ACTION", this, &Gui::menu_open);
62 Callback<Gui>::create(menu_exit, "ACTION", this, &Gui::menu_exit);
[c7b4218]63
64 Ihandle* menu = IupMenu(
65 IupSubmenu("File",
66 IupMenu(
67 menu_open,
[f9263dd]68 menu_exit,
[c7b4218]69 NULL
70 )
71 ),
72 NULL
73 );
74
75 // CONTROL
76 Ihandle* motoron = IupProgressBar();
77 IupSetAttribute(motoron, "RASTERSIZE", "16x16");
78
[4a558d7]79 playToggle = IupToggle("play", NULL);
80 IupSetAttribute(playToggle, "ACTIVE", "NO");
81 IupSetAttribute(playToggle, "IMAGE", "IUP_MediaPlay");
82 Callback<Gui, int, int>::create(playToggle, "ACTION", this,
83 &Gui::setPlaying);
84
85 Ihandle* recToggle = IupToggle("rec", NULL);
86 IupSetAttribute(recToggle, "ACTIVE", "NO");
87 IupSetAttribute(recToggle, "IMAGE", "IUP_MediaRecord");
[c2a9854]88
[c7b4218]89 // EXPLORE
90 Ihandle* platformlist = IupList(NULL);
91 IupSetAttribute(platformlist, "EXPAND", "HORIZONTAL");
92 IupSetAttribute(platformlist, "DROPDOWN", "YES");
93 IupSetAttribute(platformlist, "1", "MO5");
94 IupSetAttribute(platformlist, "VALUE", "1");
95
[c2a9854]96 blocklist = IupTree();
[c7b4218]97 IupSetAttribute(blocklist, "EXPAND", "VERTICAL");
[c2a9854]98 IupSetAttribute(blocklist, "ADDEXPANDED", "NO");
99 IupSetAttribute(blocklist, "ADDROOT", "NO");
100 IupSetAttribute(blocklist, "IMAGELEAF", "IMGBLANK");
101 IupSetAttribute(blocklist, "RASTERSIZE", "140x200");
102 Callback<Gui, int, int, int>::create(blocklist, "SELECTION_CB", this, &Gui::selectBlock);
[c7b4218]103
[53c4be3]104 hexEd = IupMatrix(NULL);
[192e299]105
[c2a9854]106 // Setup title cells
[53c4be3]107 IupSetAttribute(hexEd, "FONT", "Courier, 10");
108 IupSetAttribute(hexEd, "FONT*:17", "Courier, 10");
[c2a9854]109 IupSetAttribute(hexEd, "NUMCOL", "17");
[53c4be3]110 IupSetAttribute(hexEd, "WIDTHDEF", "10");
111 IupSetAttribute(hexEd, "WIDTH17", "105");
112 IupSetAttribute(hexEd, "WIDTH0", "12");
113 IupSetAttribute(hexEd, "HEIGHT0", "8");
114 IupSetAttribute(hexEd, "SIZE", "400x230");
[c2a9854]115 IupSetAttribute(hexEd, "EXPAND", "YES");
[53c4be3]116 IupSetAttribute(hexEd, "ALIGNMENT", "ALEFT");
[c2a9854]117 Callback<Gui, const char*, int, int>::create(hexEd, "VALUE_CB", this, &Gui::matVal);
[e43c8d0]118 Callback<Gui, int, int, int, const char*>::create(hexEd, "VALUE_EDIT_CB", this, &Gui::setMatVal);
[c2a9854]119
120 // WINDOW LAYOUT
[c7b4218]121 Ihandle* tabs = IupTabs(
122 IupVbox(
123 IupHbox(
124 IupLabel("Motor"),
125 motoron,
126 NULL
127 ),
128 IupHbox(
[192e299]129 playToggle,
[4a558d7]130 recToggle,
[c7b4218]131 NULL
132 ),
133 NULL
134 ),
135 IupVbox(
136 IupHbox(
137 IupLabel("Format:"),
138 platformlist,
139 NULL
140 ),
141 IupHbox(
142 blocklist,
143 IupVbox(
[c2a9854]144 hexEd,
[c7b4218]145 IupLabel("Checksum:"),
146 NULL
147 ),
148 NULL
149 )
150 ),
151 NULL
152 );
153
154 IupSetAttribute(tabs,"TABTITLE0", "Control");
155 IupSetAttribute(tabs,"TABTITLE1", "Explore");
156
157 Ihandle* dialog = IupDialog(tabs);
158 IupSetAttribute(dialog, "TITLE", "CrO2 tape emulator");
159 IupSetAttributeHandle(dialog, "MENU", menu);
160 IupShow(dialog);
161
162 // Run the timer
163 startPolling(motoron);
164
[c2a9854]165 // TODO the IUP main loop is blocking - it may be wise to move it out of
166 // the constructor...
[c7b4218]167 IupMainLoop();
168}
169
170
171Gui::~Gui()
172{
173 delete file;
[c2a9854]174
175 IupControlsClose();
176 IupClose();
[c7b4218]177}
178
[192e299]179
[c7b4218]180int Gui::menu_open()
181{
182 char name[65536];
183 name[0] = 0;
184 if (IupGetFile(name) == 0)
185 {
186 // Load file
[53c4be3]187 try {
188 file = Tape::load(name);
189 } catch (const char* error) {
190 puts(error);
191 return IUP_DEFAULT;
192 }
[c2a9854]193
194 // Fill in EXPLORE tab
195 int count = file->getBlockCount();
196 int lastfile = -1;
197
[53c4be3]198 IupSetAttribute(blocklist, "DELNODE", "ALL");
199
[c2a9854]200 for (int i = 0; i < count; ++i)
201 {
[53c4be3]202 const Tape::Block& blk = file->getBlock(i);
203 if (blk.isFile())
[c2a9854]204 {
[53c4be3]205 IupSetAttributeId(blocklist, "INSERTBRANCH", lastfile, blk.getName().c_str());
206 lastfile = i;
207 } else {
208 IupSetAttributeId(blocklist, "ADDLEAF", i-1, blk.getName().c_str());
209 if (blk.isControl())
[c2a9854]210 IupSetAttributeId(blocklist, "IMAGE", i, "IMGLEAF");
211 }
212 }
[4a558d7]213
214 // Enable play button
215 IupSetAttribute(playToggle, "ACTIVE", "YES");
[c7b4218]216 }
217 return IUP_DEFAULT;
218}
[192e299]219
220int Gui::menu_exit()
221{
222 return IUP_CLOSE;
223}
224
[c2a9854]225int Gui::selectBlock(int id, int what)
226{
227 if (what)
228 {
[53c4be3]229 selblock = id;
230 std::ostringstream att;
231 att << (file->getBlock(id).length / 16);
232 IupSetAttribute(hexEd, "NUMLIN", att.str().c_str());
233
234 IupSetAttribute(hexEd, IUP_REDRAW, "ALL");
235 } else if (selblock == id) {
236 selblock = -1;
[c2a9854]237 }
238
239 return IUP_DEFAULT;
240}
241
[e43c8d0]242
243int Gui::setMatVal(int x, int y, const char* val)
244{
245 int pos = (y-1) * 16 + (x-1);
246
247 if (file == NULL || selblock < 0 || selblock >= file->getBlockCount())
248 return 0;
249
250 const Tape::Block& block = file->getBlock(selblock);
251 block.data[pos] = 0; // TODO parse hex val to int
252
253 return 0;
254}
255
256
[53c4be3]257const char* Gui::matVal(int y, int x)
[c2a9854]258{
[53c4be3]259 if (y == 0)
[c2a9854]260 {
[53c4be3]261 switch(x)
[c2a9854]262 {
263 case 0:
264 return "0x";
265 case 17:
266 return "ASCII";
267 default:
268 {
269 std::ostringstream name;
270 name << std::hex;
[53c4be3]271 name << (x-1);
[c2a9854]272 return name.str().c_str();
273 }
274 }
275 }
276
[53c4be3]277 if (x == 0)
[c2a9854]278 {
[53c4be3]279 std::ostringstream name;
280 name << std::hex;
281 name << (y-1)*16;
282 return name.str().c_str();
[c2a9854]283 }
284
[53c4be3]285 if (file == NULL || selblock < 0 || selblock >= file->getBlockCount())
286 return "";
287 const Tape::Block& block = file->getBlock(selblock);
288
289 if (x == 17)
290 {
291 int off = (y-1)*16;
292 std::ostringstream txt;
293 for(int j = 0; j < 16; j++)
294 {
295 if (off + j >= block.length)
296 break;
297 char c = block.data[off+j];
298 if (isprint(c))
299 txt << c;
300 else
301 txt << '.';
302 }
303 return txt.str().c_str();
304 } else {
305
306 int pos = (y-1) * 16 + (x-1);
307 if (pos >= block.length)
308 return "";
309
310 return toHex(block.data[pos]);
311 }
[c2a9854]312}
313
[192e299]314int Gui::setPlaying(int state)
315{
316 if (state == 0)
317 {
318 // pause
[4a558d7]319 } else try {
[192e299]320 // play
321 Device::getDevice().write(*file);
[4a558d7]322 } catch (const char* ex) {
323 IupMessage("CrO2 error", ex);
[192e299]324 }
325
326 return IUP_DEFAULT;
327}
[53c4be3]328
329const char* Gui::toHex(int val)
330{
331 std::ostringstream str;
332 str.flags(std::ios_base::hex | std::ios_base::uppercase);
333 str.width(2);
334 str.fill('0');
335 str << val;
336 return str.str().c_str();
337}
Note: See TracBrowser for help on using the repository browser.