Cosmetics
[opentx.git] / radio / src / simu.cpp
blob80ad85e80fd8f747f82b650a22d8cee2063de996
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include "fx.h"
22 #include "FXExpression.h"
23 #include "FXPNGImage.h"
24 #include <unistd.h>
25 #include "fxkeys.h"
26 #include "opentx.h"
27 #include <time.h>
28 #include <ctype.h>
29 #include "targets/simu/simulcd.h"
31 #if defined(SIMU_AUDIO)
32 #include <SDL.h>
33 #undef main
34 #endif
36 #if LCD_W > 212
37 #define LCD_ZOOM 1
38 #else
39 #define LCD_ZOOM 2
40 #endif
42 #define W2 LCD_W*LCD_ZOOM
43 #define H2 LCD_H*LCD_ZOOM
45 class OpenTxSim: public FXMainWindow
47 FXDECLARE(OpenTxSim)
49 public:
50 OpenTxSim(){};
51 OpenTxSim(FXApp* a);
52 ~OpenTxSim();
53 void updateKeysAndSwitches(bool start=false);
54 long onKeypress(FXObject*, FXSelector, void*);
55 long onTimeout(FXObject*, FXSelector, void*);
56 void createBitmap(int index, uint16_t *data, int x, int y, int w, int h);
57 void makeSnapshot(const FXDrawable* drawable);
58 void doEvents();
59 void refreshDisplay();
60 void setPixel(int x, int y, FXColor color);
62 private:
63 FXImage * bmp;
64 FXImageFrame * bmf;
66 public:
67 FXSlider * sliders[NUM_STICKS];
68 FXKnob * knobs[NUM_POTS+NUM_SLIDERS];
71 // Message Map
72 FXDEFMAP(OpenTxSim) OpenTxSimMap[] = {
73 // Message_Type _______ID____Message_Handler_______
74 FXMAPFUNC(SEL_TIMEOUT, 2, OpenTxSim::onTimeout),
75 FXMAPFUNC(SEL_KEYPRESS, 0, OpenTxSim::onKeypress),
78 FXIMPLEMENT(OpenTxSim, FXMainWindow, OpenTxSimMap, ARRAYNUMBER(OpenTxSimMap))
80 OpenTxSim::OpenTxSim(FXApp* a):
81 FXMainWindow(a, "OpenTX Simu", nullptr, nullptr, DECOR_ALL, 20, 90, 0, 0)
83 memset(displayBuf, 0, DISPLAY_BUFFER_SIZE * sizeof(display_t));
84 bmp = new FXPPMImage(getApp(), nullptr, IMAGE_OWNED|IMAGE_KEEP|IMAGE_SHMI|IMAGE_SHMP, W2, H2);
86 #if defined(SIMU_AUDIO)
87 SDL_Init(SDL_INIT_AUDIO);
88 #endif
90 FXHorizontalFrame * hf11 = new FXHorizontalFrame(this, LAYOUT_CENTER_X);
91 FXHorizontalFrame * hf1 = new FXHorizontalFrame(this, LAYOUT_FILL_X);
93 //rh lv rv lh
94 for (int i=0; i<4; i++) {
95 switch (i) {
96 #define L LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FIX_X|LAYOUT_FIX_Y
97 case 0:
98 sliders[i]=new FXSlider(hf1, nullptr, 0, L|SLIDER_HORIZONTAL, 10, 110, 100, 20);
99 break;
100 case 1:
101 sliders[i]=new FXSlider(hf1, nullptr, 0, L|SLIDER_VERTICAL, 110, 10, 20, 100);
102 break;
103 case 2:
104 sliders[i]=new FXSlider(hf1, nullptr, 0, L|SLIDER_VERTICAL, 130, 10, 20, 100);
105 break;
106 case 3:
107 sliders[i]=new FXSlider(hf1, nullptr, 0, L|SLIDER_HORIZONTAL, 150, 110, 100, 20);
108 break;
109 default:;
111 sliders[i]->setRange(-1024, 1024);
112 sliders[i]->setTickDelta(7);
113 sliders[i]->setValue(0);
116 for (int i = 0; i < NUM_POTS + NUM_SLIDERS; i++) {
117 knobs[i]= new FXKnob(hf11, nullptr, 0, KNOB_TICKS|LAYOUT_LEFT);
118 knobs[i]->setValue(0);
120 #if defined(PCBHORUS)
121 if (i == 1) { // 6-pos switch
122 knobs[i]->setRange(0, 2048);
123 knobs[i]->setIncrement(2048 / 5);
124 knobs[i]->setTickDelta(2048 / 5);
125 continue;
127 #endif
129 knobs[i]->setRange(-1024, 1024);
132 bmf = new FXImageFrame(this, bmp);
133 bmf->enable();
134 bmf->setTarget(this);
136 updateKeysAndSwitches(true);
138 getApp()->addTimeout(this, 2, 100);
141 OpenTxSim::~OpenTxSim()
143 StopSimu();
144 StopAudioThread();
146 #if defined(EEPROM)
147 StopEepromThread();
148 #endif
150 delete bmp;
151 delete sliders[0];
152 delete sliders[1];
153 delete sliders[2];
154 delete sliders[3];
156 for (int i = 0; i < NUM_POTS + NUM_SLIDERS; i++) {
157 delete knobs[i];
160 delete bmf;
162 #if defined(SIMU_AUDIO)
163 SDL_Quit();
164 #endif
167 void OpenTxSim::createBitmap(int index, uint16_t *data, int x, int y, int w, int h)
169 FXPNGImage snapshot(getApp(), nullptr, IMAGE_OWNED, w, h);
171 for (int i=0; i<w; i++) {
172 for (int j=0; j<h; j++) {
173 display_t z = data[(y+j) * LCD_W + (x+i)];
174 FXColor color = FXRGB(255*((z&0xF00)>>8)/0x0f, 255*((z&0x0F0)>>4)/0x0f, 255*(z&0x00F)/0x0f);
175 snapshot.setPixel(i, j, color);
179 FXFileStream stream;
180 char buf[32];
181 sprintf(buf, "%02d.png", index);
182 if (stream.open(buf, FXStreamSave)) {
183 snapshot.savePixels(stream);
184 stream.close();
185 TRACE("Bitmap %d (w=%d, h=%d) created", index, w, h);
187 else {
188 TRACE("Bitmap %d (w=%d, h=%d) error", index, w, h);
192 void OpenTxSim::makeSnapshot(const FXDrawable* drawable)
194 // Construct and create an FXImage object
195 FXPNGImage snapshot(getApp(), nullptr, 0, drawable->getWidth(), drawable->getHeight());
196 snapshot.create();
198 // Create a window device context and lock it onto the image
199 FXDCWindow dc(&snapshot);
201 // Draw from the widget to this
202 dc.drawArea(drawable, 0, 0, drawable->getWidth(), drawable->getHeight(), 0, 0);
204 // Release lock
205 dc.end();
207 // Grab pixels from server side back to client side
208 snapshot.restore();
210 // Save recovered pixels to a file
211 FXFileStream stream;
212 char buf[100];
214 do {
215 stream.close();
216 sprintf(buf, "snapshot_%02d.png", ++g_snapshot_idx);
217 } while (stream.open(buf, FXStreamLoad));
219 if (stream.open(buf, FXStreamSave)) {
220 snapshot.savePixels(stream);
221 stream.close();
222 printf("Snapshot written: %s\n", buf);
224 else {
225 printf("Cannot create snapshot %s\n", buf);
229 void OpenTxSim::doEvents()
231 getApp()->runOneEvent(false);
234 long OpenTxSim::onKeypress(FXObject *, FXSelector, void * v)
236 auto * evt = (FXEvent *)v;
238 // TRACE("keypress %x", evt->code);
240 if (evt->code == 's') {
241 makeSnapshot(bmf);
244 return 0;
247 void OpenTxSim::updateKeysAndSwitches(bool start)
249 static int keys[] = {
250 #if defined(PCBHORUS)
251 KEY_Page_Up, KEY_PGUP,
252 KEY_Page_Down, KEY_PGDN,
253 KEY_Return, KEY_ENTER,
254 KEY_Up, KEY_UP,
255 KEY_Down, KEY_DOWN,
256 KEY_Right, KEY_RIGHT,
257 KEY_Left, KEY_LEFT,
258 #elif defined(PCBXLITE) || defined(RADIO_T12)
259 #if defined(KEYS_GPIO_REG_SHIFT)
260 KEY_Shift_L, KEY_SHIFT,
261 #endif
262 KEY_Return, KEY_ENTER,
263 KEY_BackSpace, KEY_EXIT,
264 KEY_Right, KEY_RIGHT,
265 KEY_Left, KEY_LEFT,
266 KEY_Up, KEY_UP,
267 KEY_Down, KEY_DOWN,
268 #elif defined(PCBTARANIS)
269 KEY_Page_Up, KEY_MENU,
270 #if defined(KEYS_GPIO_REG_PAGE)
271 KEY_Page_Down, KEY_PAGE,
272 #endif
273 KEY_Return, KEY_ENTER,
274 KEY_BackSpace, KEY_EXIT,
275 KEY_Up, KEY_PLUS,
276 KEY_Down, KEY_MINUS,
277 #else
278 KEY_Return, KEY_MENU,
279 KEY_BackSpace, KEY_EXIT,
280 KEY_Right, KEY_RIGHT,
281 KEY_Left, KEY_LEFT,
282 KEY_Up, KEY_UP,
283 KEY_Down, KEY_DOWN,
284 #endif
287 for (unsigned int i=0; i<DIM(keys); i+=2) {
288 simuSetKey(keys[i+1], start ? false : getApp()->getKeyState(keys[i]));
291 #ifdef __APPLE__
292 // gruvin: Can't use Function keys on the Mac -- too many other app conflicts.
293 // The ordering of these keys, Q/W,E/R,T/Y,U/I matches the on screen
294 // order of trim sliders
295 static FXuint trimKeys[] = { KEY_E, KEY_R, KEY_U, KEY_I, KEY_R, KEY_E, KEY_Y, KEY_T, KEY_Q, KEY_W };
296 #else
297 static FXuint trimKeys[] = { KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12 };
298 #endif
300 for (unsigned i=0; i<2*NUM_TRIMS; i++) {
301 simuSetTrim(i, getApp()->getKeyState(trimKeys[i]));
304 #define SWITCH_KEY(key, swtch, states) \
305 static bool state##key = 0; \
306 static int8_t state_##swtch = -1; \
307 static int8_t inc_##swtch = 4-states; \
308 if (getApp()->getKeyState(KEY_##key)) { \
309 if (!state##key) { \
310 state_##swtch = (state_##swtch+inc_##swtch); \
311 if (state_##swtch == -1+((states-1)*inc_##swtch)) inc_##swtch = -4+states; \
312 else if (state_##swtch == -1) inc_##swtch = 4-states; \
313 state##key = true; \
316 else { \
317 state##key = false; \
319 simuSetSwitch(swtch, state_##swtch) \
321 #if defined(PCBSKY9X)
322 SWITCH_KEY(1, 0, 3);
323 SWITCH_KEY(2, 1, 2);
324 SWITCH_KEY(3, 2, 2);
325 SWITCH_KEY(4, 3, 2);
326 SWITCH_KEY(5, 4, 2);
327 SWITCH_KEY(6, 5, 2);
328 SWITCH_KEY(7, 6, 2);
329 #else
330 SWITCH_KEY(A, 0, 3);
331 SWITCH_KEY(B, 1, 3);
332 SWITCH_KEY(C, 2, 3);
333 SWITCH_KEY(D, 3, 3);
335 #if defined(HARDWARE_SWITCH_G) && defined(HARDWARE_SWITCH_H)
336 SWITCH_KEY(E, 4, 3);
337 SWITCH_KEY(F, 5, 2);
338 SWITCH_KEY(G, 6, 3);
339 SWITCH_KEY(H, 7, 2);
340 #elif defined(HARDWARE_SWITCH_F) && defined(HARDWARE_SWITCH_H)
341 SWITCH_KEY(F, 4, 2);
342 SWITCH_KEY(H, 5, 2);
343 #endif
345 #if defined(PCBX9E)
346 SWITCH_KEY(I, 8, 3);
347 SWITCH_KEY(J, 9, 3);
348 SWITCH_KEY(K, 10, 3);
349 SWITCH_KEY(L, 11, 3);
350 SWITCH_KEY(M, 12, 3);
351 SWITCH_KEY(N, 13, 3);
352 SWITCH_KEY(O, 14, 3);
353 SWITCH_KEY(P, 15, 3);
354 SWITCH_KEY(Q, 16, 3);
355 SWITCH_KEY(R, 17, 3);
356 #endif
357 #endif
360 long OpenTxSim::onTimeout(FXObject*, FXSelector, void*)
362 if (hasFocus()) {
363 #if defined(COPROCESSOR)
364 coprocData.temp = 23;
365 coprocData.maxtemp = 28;
366 #endif
368 updateKeysAndSwitches();
370 #if defined(ROTARY_ENCODER_NAVIGATION)
371 static bool rotencAction = false;
372 if (getApp()->getKeyState(KEY_X)) {
373 if (!rotencAction) ROTARY_ENCODER_NAVIGATION_VALUE += ROTARY_ENCODER_GRANULARITY;
374 rotencAction = true;
376 else if (getApp()->getKeyState(KEY_W)) {
377 if (!rotencAction) ROTARY_ENCODER_NAVIGATION_VALUE -= ROTARY_ENCODER_GRANULARITY;
378 rotencAction = true;
380 else {
381 rotencAction = false;
383 #endif
386 per10ms();
387 static int timeToRefresh;
388 if (++timeToRefresh >= 5) {
389 timeToRefresh = 0;
390 refreshDisplay();
392 getApp()->addTimeout(this, 2, 10);
393 return 0;
396 #if LCD_W >= 212
397 #define BL_COLOR FXRGB(47, 123, 227)
398 #else
399 #define BL_COLOR FXRGB(150, 200, 152)
400 #endif
402 void OpenTxSim::setPixel(int x, int y, FXColor color)
404 #if LCD_ZOOM > 1
405 for (int i=0; i<LCD_ZOOM; ++i) {
406 for (int j=0; j<LCD_ZOOM; ++j) {
407 bmp->setPixel(LCD_ZOOM*x+i, LCD_ZOOM*y+j, color);
410 #else
411 bmp->setPixel(x, y, color);
412 #endif
415 void OpenTxSim::refreshDisplay()
417 if (simuLcdRefresh) {
418 simuLcdRefresh = false;
419 FXColor offColor = isBacklightEnabled() ? BL_COLOR : FXRGB(200, 200, 200);
420 #if LCD_DEPTH == 1
421 FXColor onColor = FXRGB(0, 0, 0);
422 #endif
423 for (int x=0; x<LCD_W; x++) {
424 for (int y=0; y<LCD_H; y++) {
425 #if defined(COLORLCD)
426 display_t z = simuLcdBuf[y * LCD_W + x];
427 if (1) {
428 if (z == 0) {
429 setPixel(x, y, FXRGB(0, 0, 0));
431 else if (z == 0xFFFF) {
432 setPixel(x, y, FXRGB(255, 255, 255));
434 else {
435 FXColor color = FXRGB(255*((z&0xF800)>>11)/0x1f, 255*((z&0x07E0)>>5)/0x3F, 255*(z&0x001F)/0x01F);
436 setPixel(x, y, color);
439 #elif LCD_DEPTH == 4
440 display_t * p = &simuLcdBuf[y / 2 * LCD_W + x];
441 uint8_t z = (y & 1) ? (*p >> 4) : (*p & 0x0F);
442 if (z) {
443 FXColor color;
444 if (isBacklightEnabled())
445 color = FXRGB(47-(z*47)/15, 123-(z*123)/15, 227-(z*227)/15);
446 else
447 color = FXRGB(200-(z*200)/15, 200-(z*200)/15, 200-(z*200)/15);
448 setPixel(x, y, color);
450 #else
451 if (simuLcdBuf[x+(y/8)*LCD_W] & (1<<(y%8))) {
452 setPixel(x, y, onColor);
454 #endif
455 else {
456 setPixel(x, y, offColor);
461 bmp->render();
462 bmf->setImage(bmp);
466 OpenTxSim * opentxSim;
468 void doFxEvents()
470 //puts("doFxEvents");
471 opentxSim->getApp()->runOneEvent(false);
472 opentxSim->refreshDisplay();
475 int main(int argc, char ** argv)
477 // Each FOX GUI program needs one, and only one, application object.
478 // The application objects coordinates some common stuff shared between
479 // all the widgets; for example, it dispatches events, keeps track of
480 // all the windows, and so on.
481 // We pass the "name" of the application, and its "vendor", the name
482 // and vendor are used to search the registry database (which stores
483 // persistent information e.g. fonts and colors).
484 FXApp application("OpenTX Simu", "OpenTX");
486 // Here we initialize the application. We pass the command line arguments
487 // because FOX may sometimes need to filter out some of the arguments.
488 // This opens up the display as well, and reads the registry database
489 // so that persistent settings are now available.
490 application.init(argc, argv);
492 // This creates the main window. We pass in the title to be displayed
493 // above the window, and possibly some icons for when its iconified.
494 // The decorations determine stuff like the borders, close buttons,
495 // drag handles, and so on the Window Manager is supposed to give this
496 // window.
497 //FXMainWindow *main=new FXMainWindow(&application, "Hello", nullptr, nullptr, DECOR_ALL);
498 opentxSim = new OpenTxSim(&application);
499 application.create();
501 // Pretty self-explanatory:- this shows the window, and places it in the
502 // middle of the screen.
503 #ifndef __APPLE__
504 opentxSim->show(PLACEMENT_SCREEN);
505 #else
506 opentxSim->show(); // Otherwise the main window gets centred across my two monitors, split down the middle.
507 #endif
510 printf("Model size = %d\n", (int)sizeof(g_model));
512 simuInit();
514 #if defined(EEPROM)
515 StartEepromThread(argc >= 2 ? argv[1] : "eeprom.bin");
516 #endif
517 StartAudioThread();
518 StartSimu(false, argc >= 3 ? argv[2] : 0, argc >= 4 ? argv[3] : 0);
520 return application.run();
523 uint16_t anaIn(uint8_t chan)
525 if (chan < NUM_STICKS)
526 return opentxSim->sliders[chan]->getValue();
527 else if (chan < NUM_STICKS + NUM_POTS + NUM_SLIDERS)
528 return opentxSim->knobs[chan - NUM_STICKS]->getValue();
529 #if defined(PCBTARANIS)
530 else if (chan == TX_RTC_VOLTAGE)
531 return 800; // 2.34V
532 #endif
533 else
534 return 0;
537 uint16_t getAnalogValue(uint8_t index)
539 return anaIn(index);
542 void createBitmap(int index, uint16_t *data, int x, int y, int w, int h)
544 opentxSim->createBitmap(index, data, x, y, w, h);