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.
22 #include "FXExpression.h"
23 #include "FXPNGImage.h"
29 #include "targets/simu/simulcd.h"
31 #if defined(SIMU_AUDIO)
42 #define W2 LCD_W*LCD_ZOOM
43 #define H2 LCD_H*LCD_ZOOM
45 class OpenTxSim
: public FXMainWindow
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
);
59 void refreshDisplay();
60 void setPixel(int x
, int y
, FXColor color
);
67 FXSlider
* sliders
[NUM_STICKS
];
68 FXKnob
* knobs
[NUM_POTS
+NUM_SLIDERS
];
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
);
90 FXHorizontalFrame
* hf11
= new FXHorizontalFrame(this, LAYOUT_CENTER_X
);
91 FXHorizontalFrame
* hf1
= new FXHorizontalFrame(this, LAYOUT_FILL_X
);
94 for (int i
=0; i
<4; i
++) {
96 #define L LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FIX_X|LAYOUT_FIX_Y
98 sliders
[i
]=new FXSlider(hf1
, nullptr, 0, L
|SLIDER_HORIZONTAL
, 10, 110, 100, 20);
101 sliders
[i
]=new FXSlider(hf1
, nullptr, 0, L
|SLIDER_VERTICAL
, 110, 10, 20, 100);
104 sliders
[i
]=new FXSlider(hf1
, nullptr, 0, L
|SLIDER_VERTICAL
, 130, 10, 20, 100);
107 sliders
[i
]=new FXSlider(hf1
, nullptr, 0, L
|SLIDER_HORIZONTAL
, 150, 110, 100, 20);
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);
129 knobs
[i
]->setRange(-1024, 1024);
132 bmf
= new FXImageFrame(this, bmp
);
134 bmf
->setTarget(this);
136 updateKeysAndSwitches(true);
138 getApp()->addTimeout(this, 2, 100);
141 OpenTxSim::~OpenTxSim()
156 for (int i
= 0; i
< NUM_POTS
+ NUM_SLIDERS
; i
++) {
162 #if defined(SIMU_AUDIO)
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
);
181 sprintf(buf
, "%02d.png", index
);
182 if (stream
.open(buf
, FXStreamSave
)) {
183 snapshot
.savePixels(stream
);
185 TRACE("Bitmap %d (w=%d, h=%d) created", index
, w
, h
);
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());
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);
207 // Grab pixels from server side back to client side
210 // Save recovered pixels to a file
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
);
222 printf("Snapshot written: %s\n", buf
);
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') {
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
,
256 KEY_Right
, KEY_RIGHT
,
258 #elif defined(PCBXLITE) || defined(RADIO_T12)
259 #if defined(KEYS_GPIO_REG_SHIFT)
260 KEY_Shift_L
, KEY_SHIFT
,
262 KEY_Return
, KEY_ENTER
,
263 KEY_BackSpace
, KEY_EXIT
,
264 KEY_Right
, KEY_RIGHT
,
268 #elif defined(PCBTARANIS)
269 KEY_Page_Up
, KEY_MENU
,
270 #if defined(KEYS_GPIO_REG_PAGE)
271 KEY_Page_Down
, KEY_PAGE
,
273 KEY_Return
, KEY_ENTER
,
274 KEY_BackSpace
, KEY_EXIT
,
278 KEY_Return
, KEY_MENU
,
279 KEY_BackSpace
, KEY_EXIT
,
280 KEY_Right
, KEY_RIGHT
,
287 for (unsigned int i
=0; i
<DIM(keys
); i
+=2) {
288 simuSetKey(keys
[i
+1], start
? false : getApp()->getKeyState(keys
[i
]));
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
};
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
};
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)) { \
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; \
317 state##key = false; \
319 simuSetSwitch(swtch, state_##swtch) \
321 #if defined(PCBSKY9X)
335 #if defined(HARDWARE_SWITCH_G) && defined(HARDWARE_SWITCH_H)
340 #elif defined(HARDWARE_SWITCH_F) && defined(HARDWARE_SWITCH_H)
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);
360 long OpenTxSim::onTimeout(FXObject
*, FXSelector
, void*)
363 #if defined(COPROCESSOR)
364 coprocData
.temp
= 23;
365 coprocData
.maxtemp
= 28;
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
;
376 else if (getApp()->getKeyState(KEY_W
)) {
377 if (!rotencAction
) ROTARY_ENCODER_NAVIGATION_VALUE
-= ROTARY_ENCODER_GRANULARITY
;
381 rotencAction
= false;
387 static int timeToRefresh
;
388 if (++timeToRefresh
>= 5) {
392 getApp()->addTimeout(this, 2, 10);
397 #define BL_COLOR FXRGB(47, 123, 227)
399 #define BL_COLOR FXRGB(150, 200, 152)
402 void OpenTxSim::setPixel(int x
, int y
, FXColor color
)
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
);
411 bmp
->setPixel(x
, y
, color
);
415 void OpenTxSim::refreshDisplay()
417 if (simuLcdRefresh
) {
418 simuLcdRefresh
= false;
419 FXColor offColor
= isBacklightEnabled() ? BL_COLOR
: FXRGB(200, 200, 200);
421 FXColor onColor
= FXRGB(0, 0, 0);
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
];
429 setPixel(x
, y
, FXRGB(0, 0, 0));
431 else if (z
== 0xFFFF) {
432 setPixel(x
, y
, FXRGB(255, 255, 255));
435 FXColor color
= FXRGB(255*((z
&0xF800)>>11)/0x1f, 255*((z
&0x07E0)>>5)/0x3F, 255*(z
&0x001F)/0x01F);
436 setPixel(x
, y
, color
);
440 display_t
* p
= &simuLcdBuf
[y
/ 2 * LCD_W
+ x
];
441 uint8_t z
= (y
& 1) ? (*p
>> 4) : (*p
& 0x0F);
444 if (isBacklightEnabled())
445 color
= FXRGB(47-(z
*47)/15, 123-(z
*123)/15, 227-(z
*227)/15);
447 color
= FXRGB(200-(z
*200)/15, 200-(z
*200)/15, 200-(z
*200)/15);
448 setPixel(x
, y
, color
);
451 if (simuLcdBuf
[x
+(y
/8)*LCD_W
] & (1<<(y
%8))) {
452 setPixel(x
, y
, onColor
);
456 setPixel(x
, y
, offColor
);
466 OpenTxSim
* opentxSim
;
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
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.
504 opentxSim
->show(PLACEMENT_SCREEN
);
506 opentxSim
->show(); // Otherwise the main window gets centred across my two monitors, split down the middle.
510 printf("Model size = %d\n", (int)sizeof(g_model
));
515 StartEepromThread(argc
>= 2 ? argv
[1] : "eeprom.bin");
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
)
537 uint16_t getAnalogValue(uint8_t 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
);