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 Open9xSim
: 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
);
68 FXSlider
*sliders
[NUM_STICKS
];
69 FXKnob
*knobs
[NUM_POTS
+NUM_SLIDERS
];
73 FXDEFMAP(Open9xSim
) Open9xSimMap
[] = {
74 //Message_Type _________ ID____Message_Handler_______
75 FXMAPFUNC(SEL_TIMEOUT
, 2, Open9xSim::onTimeout
),
76 FXMAPFUNC(SEL_KEYPRESS
, 0, Open9xSim::onKeypress
),
79 FXIMPLEMENT(Open9xSim
,FXMainWindow
,Open9xSimMap
,ARRAYNUMBER(Open9xSimMap
))
81 Open9xSim::Open9xSim(FXApp
* a
):
82 FXMainWindow(a
, "OpenTX Simu", NULL
, NULL
, DECOR_ALL
, 20, 90, 0, 0)
85 memset(displayBuf
, 0, DISPLAY_BUFFER_SIZE
);
86 bmp
= new FXPPMImage(getApp(),NULL
,IMAGE_OWNED
|IMAGE_KEEP
|IMAGE_SHMI
|IMAGE_SHMP
, W2
, H2
);
88 #if defined(SIMU_AUDIO)
89 SDL_Init(SDL_INIT_AUDIO
);
92 FXHorizontalFrame
*hf11
=new FXHorizontalFrame(this,LAYOUT_CENTER_X
);
93 FXHorizontalFrame
*hf1
=new FXHorizontalFrame(this,LAYOUT_FILL_X
);
96 for (int i
=0; i
<4; i
++) {
98 #define L LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FIX_X|LAYOUT_FIX_Y
100 sliders
[i
]=new FXSlider(hf1
,NULL
,0,L
|SLIDER_HORIZONTAL
,10,110,100,20);
103 sliders
[i
]=new FXSlider(hf1
,NULL
,0,L
|SLIDER_VERTICAL
,110,10,20,100);
106 sliders
[i
]=new FXSlider(hf1
,NULL
,0,L
|SLIDER_VERTICAL
,130,10,20,100);
109 sliders
[i
]=new FXSlider(hf1
,NULL
,0,L
|SLIDER_HORIZONTAL
,150,110,100,20);
113 sliders
[i
]->setRange(-1024, 1024);
114 sliders
[i
]->setTickDelta(7);
115 sliders
[i
]->setValue(0);
118 for(int i
=0; i
<NUM_POTS
+NUM_SLIDERS
; i
++){
119 knobs
[i
]= new FXKnob(hf11
,NULL
,0,KNOB_TICKS
|LAYOUT_LEFT
);
120 knobs
[i
]->setValue(0);
122 #if defined(PCBHORUS)
123 if (i
== 1) { // 6-pos switch
124 knobs
[i
]->setRange(0, 2048);
125 knobs
[i
]->setIncrement(2048 / 5);
126 knobs
[i
]->setTickDelta(2048 / 5);
130 knobs
[i
]->setRange(-1024, 1024);
133 bmf
= new FXImageFrame(this,bmp
);
135 updateKeysAndSwitches(true);
137 getApp()->addTimeout(this, 2, 100);
140 Open9xSim::~Open9xSim()
154 for(int i
=0; i
<NUM_POTS
+NUM_SLIDERS
; i
++){
160 #if defined(SIMU_AUDIO)
165 void Open9xSim::createBitmap(int index
, uint16_t *data
, int x
, int y
, int w
, int h
)
167 FXPNGImage
snapshot(getApp(), NULL
, IMAGE_OWNED
, w
, h
);
169 for (int i
=0; i
<w
; i
++) {
170 for (int j
=0; j
<h
; j
++) {
171 display_t z
= data
[(y
+j
) * LCD_W
+ (x
+i
)];
172 FXColor color
= FXRGB(255*((z
&0xF00)>>8)/0x0f, 255*((z
&0x0F0)>>4)/0x0f, 255*(z
&0x00F)/0x0f);
173 snapshot
.setPixel(i
, j
, color
);
179 sprintf(buf
,"%02d.png", index
);
180 if (stream
.open(buf
, FXStreamSave
)) {
181 snapshot
.savePixels(stream
);
183 TRACE("Bitmap %d (w=%d, h=%d) created", index
, w
, h
);
186 TRACE("Bitmap %d (w=%d, h=%d) error", index
, w
, h
);
190 void Open9xSim::makeSnapshot(const FXDrawable
* drawable
)
192 // Construct and create an FXImage object
193 FXPNGImage
snapshot(getApp(), NULL
, 0, drawable
->getWidth(), drawable
->getHeight());
196 // Create a window device context and lock it onto the image
197 FXDCWindow
dc(&snapshot
);
199 // Draw from the widget to this
200 dc
.drawArea(drawable
, 0, 0, drawable
->getWidth(), drawable
->getHeight(), 0, 0);
205 // Grab pixels from server side back to client side
208 // Save recovered pixels to a file
214 sprintf(buf
,"snapshot_%02d.png", ++g_snapshot_idx
);
215 } while (stream
.open(buf
, FXStreamLoad
));
217 if (stream
.open(buf
, FXStreamSave
)) {
218 snapshot
.savePixels(stream
);
220 printf("Snapshot written: %s\n", buf
);
223 printf("Cannot create snapshot %s\n", buf
);
227 void Open9xSim::doEvents()
229 getApp()->runOneEvent(false);
232 long Open9xSim::onKeypress(FXObject
*,FXSelector
,void*v
)
234 FXEvent
*evt
=(FXEvent
*)v
;
235 // printf("keypress %x\n", evt->code);
236 if (evt
->code
=='s') {
242 void Open9xSim::updateKeysAndSwitches(bool start
)
244 static int keys1
[] = {
245 #if defined(PCBHORUS)
246 KEY_Page_Up
, KEY_PGUP
,
247 KEY_Page_Down
, KEY_PGDN
,
248 KEY_Return
, KEY_ENTER
,
251 KEY_Right
, KEY_RIGHT
,
253 #elif defined(PCBTARANIS)
254 KEY_Page_Up
, KEY_MENU
,
255 KEY_Page_Down
, KEY_PAGE
,
256 KEY_Return
, KEY_ENTER
,
257 KEY_BackSpace
, KEY_EXIT
,
261 KEY_Return
, KEY_MENU
,
262 KEY_BackSpace
, KEY_EXIT
,
263 KEY_Right
, KEY_RIGHT
,
267 #if defined(ROTARY_ENCODER_NAVIGATION)
273 for (unsigned int i
=0; i
<DIM(keys1
); i
+=2) {
274 simuSetKey(keys1
[i
+1], start
? false : getApp()->getKeyState(keys1
[i
]));
278 // gruvin: Can't use Function keys on the Mac -- too many other app conflicts.
279 // The ordering of these keys, Q/W,E/R,T/Y,U/I matches the on screen
280 // order of trim sliders
281 static FXuint trimKeys
[] = { KEY_E
, KEY_R
, KEY_U
, KEY_I
, KEY_R
, KEY_E
, KEY_Y
, KEY_T
, KEY_Q
, KEY_W
};
283 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
};
286 for (unsigned i
=0; i
<DIM(trimKeys
); i
++) {
287 simuSetTrim(i
, getApp()->getKeyState(trimKeys
[i
]));
290 #define SWITCH_KEY(key, swtch, states) \
291 static bool state##key = 0; \
292 static int8_t state_##swtch = 2; \
293 static int8_t inc_##swtch = 1; \
294 if (getApp()->getKeyState(KEY_##key)) { \
296 state_##swtch = (state_##swtch+inc_##swtch); \
297 if (state_##swtch == 1+states) inc_##swtch = -1; \
298 else if (state_##swtch == 2) inc_##swtch = 1; \
303 state##key = false; \
305 simuSetSwitch(swtch, state_##swtch-states);
318 SWITCH_KEY(K
, 10, 3);
319 SWITCH_KEY(L
, 11, 3);
320 SWITCH_KEY(M
, 12, 3);
321 SWITCH_KEY(N
, 13, 3);
322 SWITCH_KEY(O
, 14, 3);
323 SWITCH_KEY(P
, 15, 3);
324 SWITCH_KEY(Q
, 16, 3);
325 SWITCH_KEY(R
, 17, 3);
326 #elif defined(PCBTARANIS) || defined(PCBHORUS)
346 long Open9xSim::onTimeout(FXObject
*, FXSelector
, void*)
349 #if defined(COPROCESSOR)
354 #if defined(PCBSKY9X)
359 updateKeysAndSwitches();
361 #if defined(ROTARY_ENCODER_NAVIGATION)
362 static bool rotencAction
= false;
363 if (getApp()->getKeyState(KEY_X
)) {
364 if (!rotencAction
) ROTARY_ENCODER_NAVIGATION_VALUE
+= ROTARY_ENCODER_GRANULARITY
;
367 else if (getApp()->getKeyState(KEY_W
)) {
368 if (!rotencAction
) ROTARY_ENCODER_NAVIGATION_VALUE
-= ROTARY_ENCODER_GRANULARITY
;
372 rotencAction
= false;
387 SWITCH_KEY(K
, 10, 3);
388 SWITCH_KEY(L
, 11, 3);
389 SWITCH_KEY(M
, 12, 3);
390 SWITCH_KEY(N
, 13, 3);
391 SWITCH_KEY(O
, 14, 3);
392 SWITCH_KEY(P
, 15, 3);
393 SWITCH_KEY(Q
, 16, 3);
394 SWITCH_KEY(R
, 17, 3);
395 #elif defined(PCBTARANIS) || defined(PCBHORUS)
421 static int timeToRefresh
;
422 if (++timeToRefresh
>= 5) {
426 getApp()->addTimeout(this, 2, 10);
431 #define BL_COLOR FXRGB(47, 123, 227)
433 #define BL_COLOR FXRGB(150, 200, 152)
436 void Open9xSim::setPixel(int x
, int y
, FXColor color
)
439 for (int i
=0; i
<LCD_ZOOM
; ++i
) {
440 for (int j
=0; j
<LCD_ZOOM
; ++j
) {
441 bmp
->setPixel(LCD_ZOOM
*x
+i
, LCD_ZOOM
*y
+j
, color
);
445 bmp
->setPixel(x
, y
, color
);
449 void Open9xSim::refreshDisplay()
451 if (simuLcdRefresh
) {
452 simuLcdRefresh
= false;
453 FXColor offColor
= isBacklightEnabled() ? BL_COLOR
: FXRGB(200, 200, 200);
455 FXColor onColor
= FXRGB(0, 0, 0);
457 for (int x
=0; x
<LCD_W
; x
++) {
458 for (int y
=0; y
<LCD_H
; y
++) {
459 #if defined(PCBHORUS)
460 display_t z
= simuLcdBuf
[y
* LCD_W
+ x
];
463 setPixel(x
, y
, FXRGB(0,0,0));
465 else if (z
== 0xFFFF) {
466 setPixel(x
, y
, FXRGB(255,255,255));
469 FXColor color
= FXRGB(255*((z
&0xF800)>>11)/0x1f, 255*((z
&0x07E0)>>5)/0x3F, 255*(z
&0x001F)/0x01F);
470 setPixel(x
, y
, color
);
474 display_t
* p
= &simuLcdBuf
[y
/ 2 * LCD_W
+ x
];
475 uint8_t z
= (y
& 1) ? (*p
>> 4) : (*p
& 0x0F);
478 if (isBacklightEnabled())
479 color
= FXRGB(47-(z
*47)/15, 123-(z
*123)/15, 227-(z
*227)/15);
481 color
= FXRGB(200-(z
*200)/15, 200-(z
*200)/15, 200-(z
*200)/15);
482 setPixel(x
, y
, color
);
485 if (simuLcdBuf
[x
+(y
/8)*LCD_W
] & (1<<(y
%8))) {
486 setPixel(x
, y
, onColor
);
490 setPixel(x
, y
, offColor
);
503 //puts("doFxEvents");
504 th9xSim
->getApp()->runOneEvent(false);
505 th9xSim
->refreshDisplay();
508 int main(int argc
,char **argv
)
510 // Each FOX GUI program needs one, and only one, application object.
511 // The application objects coordinates some common stuff shared between
512 // all the widgets; for example, it dispatches events, keeps track of
513 // all the windows, and so on.
514 // We pass the "name" of the application, and its "vendor", the name
515 // and vendor are used to search the registry database (which stores
516 // persistent information e.g. fonts and colors).
517 FXApp
application("OpenTX Simu", "OpenTX");
519 // Here we initialize the application. We pass the command line arguments
520 // because FOX may sometimes need to filter out some of the arguments.
521 // This opens up the display as well, and reads the registry database
522 // so that persistent settings are now available.
523 application
.init(argc
,argv
);
525 // This creates the main window. We pass in the title to be displayed
526 // above the window, and possibly some icons for when its iconified.
527 // The decorations determine stuff like the borders, close buttons,
528 // drag handles, and so on the Window Manager is supposed to give this
530 //FXMainWindow *main=new FXMainWindow(&application,"Hello",NULL,NULL,DECOR_ALL);
531 th9xSim
= new Open9xSim(&application
);
532 application
.create();
534 // Pretty self-explanatory:- this shows the window, and places it in the
535 // middle of the screen.
537 th9xSim
->show(PLACEMENT_SCREEN
);
539 th9xSim
->show(); // Otherwise the main window gets centred across my two monitors, split down the middle.
542 #if defined(TELEMETRY_FRSKY) && !defined(TELEMETRY_FRSKY_SPORT)
543 telemetryStreaming
= 1;
546 printf("Model size = %d\n", (int)sizeof(g_model
));
551 StartEepromThread(argc
>= 2 ? argv
[1] : "eeprom.bin");
554 StartSimu(false, argc
>= 3 ? argv
[2] : 0, argc
>= 4 ? argv
[3] : 0);
556 return application
.run();
559 uint16_t anaIn(uint8_t chan
)
562 return th9xSim
->sliders
[chan
]->getValue();
563 else if (chan
<NUM_STICKS
+NUM_POTS
+NUM_SLIDERS
)
564 return th9xSim
->knobs
[chan
-NUM_STICKS
]->getValue();
565 #if defined(PCBHORUS)
566 else if (chan
== TX_VOLTAGE
)
567 return 1737; //~10.6V
568 #elif defined(PCBX9E)
569 else if (chan
== TX_VOLTAGE
)
570 return 1420; //~10.6V
571 #elif defined(PCBTARANIS)
572 else if (chan
== TX_VOLTAGE
)
574 #elif defined(PCBSKY9X)
575 else if (chan
== TX_VOLTAGE
)
576 return 5.1*1500/11.3;
577 else if (chan
== TX_CURRENT
)
579 #elif defined(PCBGRUVIN9X)
580 else if (chan
== TX_VOLTAGE
)
583 else if (chan
== TX_VOLTAGE
)
590 uint16_t getAnalogValue(uint8_t index
)
595 void createBitmap(int index
, uint16_t *data
, int x
, int y
, int w
, int h
)
597 th9xSim
->createBitmap(index
, data
, x
, y
, w
, h
);