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
Line 
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>
13#include <string.h>
14#include <sstream>
15#include <ios>
16
17#include <iupcontrols.h>
18
19 // Start status poller "thread"
20int pollStatus(Ihandle* ih)
21{
22 try {
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
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
35 }
36 return IUP_DEFAULT;
37}
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);
56 IupControlsOpen();
57 IupImageLibOpen();
58
59 Ihandle* menu_open = IupItem("Open", NULL);
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);
63
64 Ihandle* menu = IupMenu(
65 IupSubmenu("File",
66 IupMenu(
67 menu_open,
68 menu_exit,
69 NULL
70 )
71 ),
72 NULL
73 );
74
75 // CONTROL
76 Ihandle* motoron = IupProgressBar();
77 IupSetAttribute(motoron, "RASTERSIZE", "16x16");
78
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");
88
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
96 blocklist = IupTree();
97 IupSetAttribute(blocklist, "EXPAND", "VERTICAL");
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);
103
104 hexEd = IupMatrix(NULL);
105
106 // Setup title cells
107 IupSetAttribute(hexEd, "FONT", "Courier, 10");
108 IupSetAttribute(hexEd, "FONT*:17", "Courier, 10");
109 IupSetAttribute(hexEd, "NUMCOL", "17");
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");
115 IupSetAttribute(hexEd, "EXPAND", "YES");
116 IupSetAttribute(hexEd, "ALIGNMENT", "ALEFT");
117 Callback<Gui, const char*, int, int>::create(hexEd, "VALUE_CB", this, &Gui::matVal);
118 Callback<Gui, int, int, int, const char*>::create(hexEd, "VALUE_EDIT_CB", this, &Gui::setMatVal);
119
120 // WINDOW LAYOUT
121 Ihandle* tabs = IupTabs(
122 IupVbox(
123 IupHbox(
124 IupLabel("Motor"),
125 motoron,
126 NULL
127 ),
128 IupHbox(
129 playToggle,
130 recToggle,
131 NULL
132 ),
133 NULL
134 ),
135 IupVbox(
136 IupHbox(
137 IupLabel("Format:"),
138 platformlist,
139 NULL
140 ),
141 IupHbox(
142 blocklist,
143 IupVbox(
144 hexEd,
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
165 // TODO the IUP main loop is blocking - it may be wise to move it out of
166 // the constructor...
167 IupMainLoop();
168}
169
170
171Gui::~Gui()
172{
173 delete file;
174
175 IupControlsClose();
176 IupClose();
177}
178
179
180int Gui::menu_open()
181{
182 char name[65536];
183 name[0] = 0;
184 if (IupGetFile(name) == 0)
185 {
186 // Load file
187 try {
188 file = Tape::load(name);
189 } catch (const char* error) {
190 puts(error);
191 return IUP_DEFAULT;
192 }
193
194 // Fill in EXPLORE tab
195 int count = file->getBlockCount();
196 int lastfile = -1;
197
198 IupSetAttribute(blocklist, "DELNODE", "ALL");
199
200 for (int i = 0; i < count; ++i)
201 {
202 const Tape::Block& blk = file->getBlock(i);
203 if (blk.isFile())
204 {
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())
210 IupSetAttributeId(blocklist, "IMAGE", i, "IMGLEAF");
211 }
212 }
213
214 // Enable play button
215 IupSetAttribute(playToggle, "ACTIVE", "YES");
216 }
217 return IUP_DEFAULT;
218}
219
220int Gui::menu_exit()
221{
222 return IUP_CLOSE;
223}
224
225int Gui::selectBlock(int id, int what)
226{
227 if (what)
228 {
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;
237 }
238
239 return IUP_DEFAULT;
240}
241
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
257const char* Gui::matVal(int y, int x)
258{
259 if (y == 0)
260 {
261 switch(x)
262 {
263 case 0:
264 return "0x";
265 case 17:
266 return "ASCII";
267 default:
268 {
269 std::ostringstream name;
270 name << std::hex;
271 name << (x-1);
272 return name.str().c_str();
273 }
274 }
275 }
276
277 if (x == 0)
278 {
279 std::ostringstream name;
280 name << std::hex;
281 name << (y-1)*16;
282 return name.str().c_str();
283 }
284
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 }
312}
313
314int Gui::setPlaying(int state)
315{
316 if (state == 0)
317 {
318 // pause
319 } else try {
320 // play
321 Device::getDevice().write(*file);
322 } catch (const char* ex) {
323 IupMessage("CrO2 error", ex);
324 }
325
326 return IUP_DEFAULT;
327}
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.