FreeWRL / FreeX3D 4.3.0
common.c
1/*
2
3 FreeWRL support library.
4
5 See common.h.
6
7*/
8
9/*
10 MVC - the setter functions here in common.c are called from the Model (libfreewrl),
11 - the getter functions you call from your View (UI), typically once per frame, in a style called 'polling'
12 - once per Controller loop (_DisplayThread) you can have the controller notify your View that
13 it's time to poll the Model for updates
14 - benefit of MVC: the Model never calls back into the View, so isn't dependent on it, so
15 a) the View is easier to change (for different platforms and windowing technology), and
16 b) the Model is UI-technology-agnostic, so it's easier to maintain across platforms.
17 - Polling vs callbacks: the reason we poll the model, instead of registering callbacks:
18 the Controller is usually in the same language/technology as the UI, which often isn't C,
19 and calling into C is usually much easier then calling back from C into python, ObjectiveC, Java, C#
20 or whatever other technology/language your View/UI and Controller is in
21*/
22
23#include <config.h>
24#include <system.h>
25#include <internal.h>
26#include <libFreeWRL.h>
27#include <iglobal.h>
28#include "../ui/common.h"
29#include <scenegraph/Vector.h>
30
31// OLD_IPHONE_AQUA #if defined (_MSC_VER) || defined (AQUA) || defined(QNX) || defined(_ANDROID) || defined(ANDROIDNDK)
32#if defined (_MSC_VER) || defined (AQUA) || defined(QNX) || defined(_ANDROID) || defined(ANDROIDNDK)
33#include "../../buildversion.h"
34#endif
35
36
37// Linux builds, thanks to our very own Ian, creates this function for us.
38// on other platforms, we have to have this defined, as we don't have Ian's
39// talents to help us out.
40
41// OLD_IPHONE_AQUA #if defined (AQUA) || defined (_MSC_VER) || defined(QNX) || defined(_ANDROID) || defined(ANDROIDNDK)
42#if defined (_MSC_VER) || defined (AQUA) || defined(QNX) || defined(_ANDROID) || defined(ANDROIDNDK)
43const char *libFreeWRL_get_version(void) {return FW_BUILD_VERSION_STR;}
44//#else desktop linux which has a more complex versioning system
45#endif
46
47
48#define MAXSTAT 200
49
50#define MAXTITLE 200
51
52typedef struct keyval {
53 char *key;
54 char *val;
55} keyval;
56
57/* textual status messages */
58typedef struct pcommon{
59 int itrap;
60 float myFps; // = (float) 0.0;
61 int target_frames_per_second;
62 char myMenuStatus[MAXSTAT];
63 char messagebar[MAXSTAT];
64 char SensorStatus[MAXSTAT];
65 char fpsbar[16];
66 char distbar[16];
67 char window_title[MAXTITLE];
68 int cursorStyle;
69 int promptForURL;
70 int promptForFile;
71 int sb_hasString;// = FALSE;
72 char buffer[200];
73 int showConsoleText;
74 void *colorScheme;
75 int colorSchemeChanged;
76 int pin_statusbar;
77 int pin_menubar;
78 int want_menubar;
79 int want_statusbar;
80 struct Vector *keyvals;
81 float density_factor;
82 int pedal;
83 int hover;
84 int jsengine;
85 int jsengine_variant;
86 int draw_bounding_boxes;
87 int show_viewpoints;
88 int draw_rig;
89 int record_inputs;
90 int playback_inputs;
91 double start_time;
92}*ppcommon;
93void *common_constructor(){
94 void *v = MALLOCV(sizeof(struct pcommon));
95 memset(v,0,sizeof(struct pcommon));
96 return v;
97}
98void common_init(struct tcommon *t){
99 //public
100 //private
101 t->prv = common_constructor();
102 {
103 ppcommon p = (ppcommon)t->prv;
104 p->itrap = 0; //handy for debugging, see fwl_setTrap, fwl_getTrap
105 p->myFps = (float) 0.0;
106 p->cursorStyle = ACURSE;
107 p->sb_hasString = FALSE;
108 p->colorScheme = NULL;
109 p->colorSchemeChanged = 0;
110 p->pin_statusbar = 1;
111 p->pin_menubar = 1;
112 p->want_menubar = 1;
113 p->want_statusbar = 1;
114 p->keyvals = NULL;
115 p->showConsoleText = 0; //in the UI, if a callback is registered with ConsoleMessage. Won't affect old fashioned console,
116 p->target_frames_per_second = 120; //is 120 FPS a good target FPS?
117 p->density_factor = 1.0f; //how much to scale up UI elements for small high res screens ie mobile, see fwl_setDensityFactor
118 p->pedal = 0; //pedal mode moves in-scene cursor by drag amount ie indirect/offset drag
119 p->hover = 0; //hover mode means your drags only do isOver -no navigation or sensor click
120 p->jsengine = JSENGINE_STUB;
121 p->draw_bounding_boxes = FALSE;
122 p->show_viewpoints = FALSE;
123 p->draw_rig = FALSE;
124#ifdef JAVASCRIPT_DUK
125 p->jsengine = JSENGINE_DUK;
126#endif
127#ifdef JAVASCRIPT_SM
128 p->jsengine = JSENGINE_SM;
129 p->jsengine_variant = 2; //1= pre-2018 SM1 2= 2018+ SM2
130#ifdef JAVASCRIPT_ENGINE_VARIANT
131 p->jsengine_variant = JAVASCRIPT_ENGINE_VARIANT; //1= pre-2018 SM1 2= 2018+ SM2
132#endif
133#endif
134 p->record_inputs = FALSE;
135 p->playback_inputs = FALSE;
136 }
137}
138void common_clear(struct tcommon *t){
139 //public
140 //private
141 {
142 ppcommon p = (ppcommon)t->prv;
143 if(p->keyvals){
144 int i;
145 for(i=0;i<vectorSize(p->keyvals);i++){
146 keyval k_v = vector_get(keyval,p->keyvals,i);
147 FREE_IF_NZ(k_v.key);
148 FREE_IF_NZ(k_v.val);
149 }
150 deleteVector(keyval,p->keyvals);
151 }
152 }
153}
154void splitpath3(const char* url, char** folder, char** local_name, char** suff);
155//ppcommon p = (ppcommon)gglobal()->common.prv;
156static FILE* frecord = NULL;
157FILE* getRecordFile() {
158 if (!frecord) {
159 char name[300];
160 char* folder, * local_name, * suff;
161 splitpath3(gglobal()->Mainloop.url, &folder, &local_name, &suff);
162 strcpy(name, folder);
163 strcat(name, "/");
164 strcat(name, local_name);
165 strcat(name, ".fwplay");
166 frecord = fopen(name, "w+");
167 }
168 return frecord;
169}
170static double last_time, run_time;
171void record_touch(int mev, unsigned int ID, int mouseX, int mouseY, int windex) {
172 ttglobal tg = gglobal();
173 FILE* f = getRecordFile();
174 double this_time = Time1970sec();
175 double delta_time = this_time -last_time;
176 last_time = this_time;
177 //normalize touch coords to -1 to +1 range in y,
178 // so if screen size, dimensions change between record and playback it will still work
179 float fmouseX, fmouseY, scale;
180 scale = 2.0f / (float)tg->display.screenHeight;
181 fmouseY = (float)mouseY * scale - 1.0f;
182 fmouseX = (float)mouseX * scale - 1.0f;
183 fprintf(f, "T,%d,%u,%f,%f,%d,%lf\n",mev,ID,fmouseX,fmouseY,windex,delta_time);
184}
185void record_mouse(int mev, int butnum, int mouseX, int mouseY, int windex) {
186 ttglobal tg = gglobal();
187 FILE* f = getRecordFile();
188 double this_time = Time1970sec();
189 double delta_time = this_time - last_time;
190 last_time = this_time;
191 //normalize mouse coords to -1 to +1 range in y,
192 // so if screen size, dimensions change between record and playback it will still work
193 float fmouseX, fmouseY, scale;
194 scale = 2.0f / (float)tg->display.screenHeight;
195 fmouseY = (float)mouseY * scale - 1.0f;
196 fmouseX = (float)mouseX * scale - 1.0f;
197
198 fprintf(f, "M,%d,%d,%f,%f,%d,%lf\n",mev,butnum,fmouseX,fmouseY,windex,delta_time);
199}
200void record_rawkeypress(int key, int type) {
201 FILE* f = getRecordFile();
202 double this_time = Time1970sec();
203 double delta_time = this_time - last_time;
204 last_time = this_time;
205 fprintf(f, "K,%d,%d,%lf\n", key, type, delta_time);
206}
207
208void fwl_set_modeRecord() {
209 ppcommon p = (ppcommon)gglobal()->common.prv;
210 p->record_inputs = TRUE;
211 last_time = Time1970sec();
212}
213
214void fwl_do_keyPress0(int key, int type);
215int fwl_handle_mouse0(const int mev, const unsigned int button, int x, int y, int windex);
216int fwl_handle_touch0(int mev, unsigned int ID, int mouseX, int mouseY, int windex);
217#if defined(_MSC_VER)
218void updateCursorStyle0(int cstyle);
219#endif
220
221static pthread_t playback_thread;
222void _playbackthread(ttglobal tglobal) {
223 ttglobal tg = tglobal;
224 char name[300], line[300], cc;
225 int mev, butnum, mouseX, mouseY, windex, cstyle, ID, key, type, iret;
226 float fmouseX, fmouseY, scale;
227 double delta_time, this_time, dtime;
228 char* folder, * local_name, * suff;
229 fwl_setCurrentHandle(tg, __FILE__, __LINE__);
230 while (tg->Mainloop.url == NULL) sleep(50);
231 splitpath3(tg->Mainloop.url, &folder, &local_name, &suff);
232 strcpy(name, folder);
233 strcat(name, "/");
234 strcat(name, local_name);
235 strcat(name, ".fwplay");
236 FILE *fplay = fopen(name, "r+");
237 run_time = Time1970sec();
238 while (fscanf(fplay, "%s", &line)>0) {
239 //printf("%s\n",line);
240 switch (line[0]) {
241 case 'M':
242 sscanf(line, "%c,%d,%d,%f,%f,%d,%lf\n", &cc, &mev, &butnum, &fmouseX, &fmouseY, &windex, &dtime);
243 //printf("%c %d %d %d %d %d %lf\n", cc, mev, butnum, mouseX, mouseY, windex, rtime);
244 run_time += dtime;
245 this_time = Time1970sec();
246 delta_time = run_time - this_time;
247 if (delta_time > 0.0) sleep((int)(1000.0 * (delta_time)));
248 //de-normalize mouse coords
249 scale = 2.0f / (float)tg->display.screenHeight;
250 mouseY = (int)((fmouseY + 1.0)/scale + .5f);
251 mouseX = (int)((fmouseX + 1.0)/scale + .5f);
252 cstyle = fwl_handle_mouse0(mev, butnum, mouseX, mouseY, windex);
253#if defined(_MSC_VER)
254 updateCursorStyle0(cstyle);
255#endif
256 break;
257 case 'T':
258 sscanf(line, "%c,%d,%u,%f,%f,%d,%lf\n", &cc, &mev, &ID, &fmouseX, &fmouseY, &windex, &dtime);
259 run_time += dtime;
260 this_time = Time1970sec();
261 delta_time = run_time - this_time;
262 if (delta_time > 0.0) sleep((int)(1000.0 * (delta_time)));
263 //de-normalize touch coords
264 scale = 2.0f / (float)tg->display.screenHeight;
265 mouseY = (int)(fmouseY + 1.0) / scale;
266 mouseX = (int)(fmouseX + 1.0) / scale;
267 cstyle = fwl_handle_touch0(mev, ID, mouseX, mouseY, windex);
268#if defined(_MSC_VER)
269 updateCursorStyle0(cstyle);
270#endif
271 break;
272 case 'K':
273 sscanf(line, "%c,%d,%d,%lf\n", &cc, &key, &type, &dtime);
274 run_time += dtime;
275 this_time = Time1970sec();
276 delta_time = run_time - this_time;
277 if (delta_time > 0.0) sleep((int)(1000.0 * (delta_time)));
278 fwl_do_keyPress0(key, type);
279 break;
280 }
281 }
282}
283void fwl_set_modePlayback() {
284 ppcommon p = (ppcommon)gglobal()->common.prv;
285 p->playback_inputs = TRUE;
286 last_time = Time1970sec();
287 int ret = pthread_create(&playback_thread, NULL, (void*)_playbackthread, gglobal());
288}
289int fwl_get_modePlayback() {
290 ppcommon p = (ppcommon)gglobal()->common.prv;
291 return p->playback_inputs;
292}
293int fwl_get_modeRecord() {
294 ppcommon p = (ppcommon)gglobal()->common.prv;
295 return p->record_inputs;
296}
297void fwl_setTrap(int k){
298 ppcommon p = (ppcommon)gglobal()->common.prv;
299 p->itrap = k;
300}
301int fwl_getTrap(){
302 ppcommon p = (ppcommon)gglobal()->common.prv;
303 return p->itrap;
304}
305
306void fwl_setJsEngine(char *optarg){
307 //this has to be set during startup, can't reset during the run.
308 int engine, engine_variant, ivalid;
309 ppcommon p = (ppcommon)gglobal()->common.prv;
310 engine = -1;
311 engine_variant = -1;
312 ivalid = FALSE;
313
314 if(strlen(optarg) >= 2 && (!strncmp(optarg,"SM",2) || !strncmp(optarg,"sm",2))){
315 ivalid = TRUE;
316 #ifdef JAVASCRIPT_SM
317 engine = JSENGINE_SM;
318 if(strlen(optarg) >= 3){
319 if(optarg[2] == '2') engine_variant = 2;
320 if(optarg[2] == '1') engine_variant = 1;
321 }
322 #else
323 ConsoleMessage("not built with spidermonkey js engine\n");
324 #endif
325 }
326 if(!strcmp(optarg,"DUK") || !strcmp(optarg,"duk")){
327 ivalid = TRUE;
328 #ifdef JAVASCRIPT_DUK
329 engine = JSENGINE_DUK;
330 #else
331 ConsoleMessage("not built with duktape js engine\n");
332 #endif
333 }
334 if(!strcmp(optarg,"NONE") || !strcmp(optarg,"none")){
335 ivalid = TRUE;
336 engine = JSENGINE_STUB;
337 }
338 if(engine == -1){
339 static char *engine_names [] = {"NONE","DUK","SM"};
340 ConsoleMessage("could not do js preference %s, trying %s\n",optarg,engine_names[p->jsengine]);
341 }
342 if(!ivalid){
343 ConsoleMessage("invalid --javascript / -J otpion, should be SM, DUK or NONE\n");
344 }
345 if(ivalid && engine > -1){
346 p->jsengine = engine; //should be JSENGINE_SM 1 or JSENGINE_DUK 2 or 0 for stubs)
347 if(engine_variant > -1) p->jsengine_variant = engine_variant;
348 }
349}
350int getJsEngine(){
351 ppcommon p = (ppcommon)gglobal()->common.prv;
352 return p->jsengine;
353}
354int getJsEngineVariant(){
355 ppcommon p = (ppcommon)gglobal()->common.prv;
356 return p->jsengine_variant;
357}
358/* Status update functions (generic = all platform) */
359void setFpsBar();
360void setMenuFps(float fps)
361{
362 ppcommon p = (ppcommon)gglobal()->common.prv;
363
364 p->myFps = fps;
365 setFpsBar();
366}
367/* make sure that on a re-load that we re-init */
368void kill_status(void) {
369 /* hopefully, by this time, rendering has been stopped */
370 ppcommon p = (ppcommon)gglobal()->common.prv;
371
372 p->sb_hasString = FALSE;
373 p->buffer[0] = '\0';
374}
375
376void showConsoleText(int on){
377 ppcommon p = (ppcommon)gglobal()->common.prv;
378 p->showConsoleText = on;
379}
380int getShowConsoleText(){
381 ppcommon p = (ppcommon)gglobal()->common.prv;
382 return p->showConsoleText;
383}
384/* trigger a update */
385void update_status(char* msg) {
386 ppcommon p = (ppcommon)gglobal()->common.prv;
387
388 if (msg == NULL){
389 p->sb_hasString = FALSE;
390 p->buffer[0] = '\0';
391 }
392 else {
393 p->sb_hasString = TRUE;
394 strcpy(p->buffer, msg);
395 }
396}
397char *get_status(){
398 ppcommon p = (ppcommon)gglobal()->common.prv;
399 return p->buffer;
400}
401void setSensorStatus(char* status) {
402 char* pp;
403 ppcommon p = (ppcommon)gglobal()->common.prv;
404
405 pp = status;
406 if (!pp) pp = "";
407 snprintf(p->SensorStatus, MAXSTAT - 1, "%s", pp);
408
409}
410char* getSensorStatus() {
411 return ((ppcommon)gglobal()->common.prv)->SensorStatus;
412}
413void setMenuStatus3(char* status3)
414{
415 char *pp;
416 ppcommon p = (ppcommon)gglobal()->common.prv;
417
418 pp = status3;
419 if (!pp) pp = "";
420 snprintf(p->myMenuStatus, MAXSTAT-1, "%s", pp);
421}
422void setMenuStatusVP(char *stattext)
423{
424 setMenuStatus3(stattext);
425}
426char *getMenuStatus()
427{
428 return ((ppcommon)gglobal()->common.prv)->myMenuStatus;
429}
430//#if !defined (_ANDROID)
431
432
433void setWindowTitle0()
434{
435 ppcommon p = (ppcommon)gglobal()->common.prv;
436
437 snprintf(p->window_title, sizeof(p->window_title), "FreeWRL");
438 //setWindowTitle(); //dug9 Mar2014: it will be up to your UI/View to poll for getWindowTitle and set any windowing title in your UI.
439}
440char *getWindowTitle()
441{
442 ppcommon p = (ppcommon)gglobal()->common.prv;
443 return p->window_title;
444}
445//#endif //ANDROID
446
447void setMessageBar()
448{
449 ppcommon p = (ppcommon)gglobal()->common.prv;
450
451 snprintf(p->messagebar, MAXSTAT-1, "%s", p->myMenuStatus);
452}
453char *getMessageBar()
454{
455 ppcommon p = (ppcommon)gglobal()->common.prv;
456 return p->messagebar;
457}
458double get_viewer_dist();
459char *getDistBar(){
460 ppcommon p = (ppcommon)gglobal()->common.prv;
461 snprintf(p->distbar, 10, "D%8f", (float)get_viewer_dist()); //DIST %4f
462
463 return p->distbar;
464}
465
466char *getFpsBar(){
467 ppcommon p = (ppcommon)gglobal()->common.prv;
468 return p->fpsbar;
469}
470void setFpsBar(){
471 ppcommon p = (ppcommon)gglobal()->common.prv;
472 //snprintf(p->fpsbar, 10, "%7.2f", p->myFps);
473 snprintf(p->fpsbar, 10, "%4d", (int)(p->myFps + .49999f));
474}
475static int frontend_using_cursor = 0;
476void fwl_set_frontend_using_cursor(int on)
477{
478 //used by statusbarHud to shut off cursor settings coming from sensitive nodes
479 //while the mouse is over the statusbar or menu buttons.
480 frontend_using_cursor = on;
481}
482
483void setArrowCursor()
484{
485 ppcommon p = (ppcommon)gglobal()->common.prv;
486 p->cursorStyle = ACURSE;
487}
488void setLookatCursor()
489{
490 ppcommon p = (ppcommon)gglobal()->common.prv;
491 p->cursorStyle = SCURSE; // need a special cursor just for lookat
492}
493
494void setSensorCursor()
495{
496 ppcommon p = (ppcommon)gglobal()->common.prv;
497 p->cursorStyle = SCURSE;
498}
499
500int getCursorStyle()
501{
502 ppcommon p = (ppcommon)gglobal()->common.prv;
503 if (!frontend_using_cursor)
504 return p->cursorStyle;
505 else
506 return ACURSE;
507}
508
509int fwl_set_sbh_pin_option(char *optarg){
510 if(optarg && strlen(optarg) > 1){
511 ppcommon p = (ppcommon)gglobal()->common.prv;
512 p->pin_statusbar = (optarg[0] == 'T' || optarg[0] == 't') ? 1 : 0;
513 p->pin_menubar = (optarg[1] == 'T' || optarg[1] == 't') ? 1 : 0;
514 }
515 return 1;
516}
517int fwl_set_sbh_want_option(char *optarg){
518 if(optarg && strlen(optarg) > 1){
519 ppcommon p = (ppcommon)gglobal()->common.prv;
520 p->want_statusbar = (optarg[0] == 'T' || optarg[0] == 't') ? 1 : 0;
521 p->want_menubar = (optarg[1] == 'T' || optarg[1] == 't') ? 1 : 0;
522 }
523 return 1;
524}
525
526void fwl_set_sbh_pin(int sb, int mb){
527 ppcommon p = (ppcommon)gglobal()->common.prv;
528 p->pin_statusbar = sb;
529 p->pin_menubar = mb;
530}
531void fwl_get_sbh_pin(int *sb, int *mb){
532 ppcommon p = (ppcommon)gglobal()->common.prv;
533 *sb = p->pin_statusbar;
534 *mb = p->pin_menubar;
535}
536void fwl_set_sbh_wantMenubar(int want){
537 ppcommon p = (ppcommon)gglobal()->common.prv;
538 p->want_menubar = want ? 1 : 0;
539}
540int fwl_get_sbh_wantMenubar(){
541 ppcommon p = (ppcommon)gglobal()->common.prv;
542 return p->want_menubar;
543}
544void fwl_set_sbh_wantStatusbar(int want){
545 ppcommon p = (ppcommon)gglobal()->common.prv;
546 p->want_statusbar = want ? 1 : 0;
547}
548int fwl_get_sbh_wantStatusbar(){
549 ppcommon p = (ppcommon)gglobal()->common.prv;
550 return p->want_statusbar;
551}
552
553void fwl_set_target_fps(int target_fps){
554 ppcommon p = (ppcommon)gglobal()->common.prv;
555 p->target_frames_per_second = max(1,target_fps);
556}
557int fwl_get_target_fps(){
558 ppcommon p = (ppcommon)gglobal()->common.prv;
559 return p->target_frames_per_second;
560}
561// start ui color scheme >>>>>>>>>>>
562
563// StatusbarHud color schemes:
564
565typedef struct colorScheme {
566 char *name;
567 char *panel;
568 char *menuIcon;
569 char *statusText;
570 char *messageText;
572static colorScheme colorSchemes [] = {
573{
574"original",
575"#EBE8D7", //{.922f,.91f,.844f,1.0f}; 235 232 215 //offwhite
576"#5E5EE6", //{0.37f,0.37f,0.9f,1.0f}; 94 94 230//medium blue
577"#333333", //{.2f, .2f, .2f, 1.0f}; 51 //very dark grey
578"#FFFFFF", //{1.0f, 1.0f, 1.0f, 1.0f}; 255 //white
579},
580{
581"midnight",
582"#000000",
583"#FFFFFF",
584"#FFFFFF",
585"#FFFFFF",
586},
587{
588"angry",
589"#003333", //= {0.0f,0.2f,0.2f,1.0f}; //slightly blue-green black
590"#FF0000", // {1.0f, 0.0f, 0.0f, 1.0f}; //red
591"#FF0000", //{1.0f, 0.0f, 0.0f, 1.0f}; //red
592"#FF0000", // {1.0f, 0.0f, 0.0f, 1.0f}; //red
593},
594{
595"favicon",
596"#004073", // {0.0f,0.25f,0.45f,1.0f}; 0 64 115//indigo
597"#91CCF0", // {.57f, 0.8f, 0.94f, 1.0f}; 145 204 240//light aqua
598"#FF7800", // {1.0f, 0.47f, 0.0f, 1.0f}; 255 120 0//orange
599"#FF7800", // {1.0f, 0.47f, 0.0f, 1.0f}; 255 120 0//orange
600},
601{
602"aqua",
603"#BFD4BD", // {0.75f,0.83f,0.74f,1.0f}; 191 212 189//clamshell
604"#007085", //{.0f, 0.44f, 0.52f, 1.0f}; 0 112 133//dark aqua/indigo
605"#52736E", // {.32f, 0.45f, 0.43f, 1.0f}; 82 115 110//dark clamshell
606"#0FB0CC", // {.06f, 0.69f, 0.8f, 1.0f}; 15 176 204//aqua
607},
608{
609"neon:lime",
610"#3D4557", //= {0.24f,0.27f,0.34f,1.0f}; 61 69 87//steely grey
611"#CCFF00", //LIME {.8f,1.0f,0.0f,1.0f} 204 255 0
612"#CCFF00", //LIME {.8f,1.0f,0.0f,1.0f} 204 255 0
613"#CCFF00", //LIME {.8f,1.0f,0.0f,1.0f} 204 255 0
614},
615{
616"neon:yellow",
617"#3D4557", //= {0.24f,0.27f,0.34f,1.0f}; 61 69 87//steely grey
618"#FFFF33", //YELLOW {1.0f,1.0f,.2f,1.0f} 255 255 51
619"#FFFF33", //YELLOW {1.0f,1.0f,.2f,1.0f} 255 255 51
620"#FFFF33", //YELLOW {1.0f,1.0f,.2f,1.0f} 255 255 51
621},
622{
623"neon:cyan",
624"#3D4557", //= {0.24f,0.27f,0.34f,1.0f}; 61 69 87//steely grey
625"#00FFFF", //CYAN {0.0f,1.0f,1.0f,1.0f} 0 255 255
626"#00FFFF", //CYAN {0.0f,1.0f,1.0f,1.0f} 0 255 255
627"#00FFFF", //CYAN {0.0f,1.0f,1.0f,1.0f} 0 255 255
628},
629{
630"neon:pink",
631"#3D4557", //= {0.24f,0.27f,0.34f,1.0f}; 61 69 87//steely grey
632"#FF78FF", //PINK {1.0f,.47f,1.0f,1.0f} 255 120 255
633"#FF78FF", //PINK {1.0f,.47f,1.0f,1.0f} 255 120 255
634"#FF78FF", //PINK {1.0f,.47f,1.0f,1.0f} 255 120 255
635},
636{
637"custom",
638NULL,
639NULL,
640NULL,
641NULL,
642},
643{NULL,NULL,NULL,NULL},
644};
645
646void color_html2rgb(char *html, float *rgb){
647 //converts one html color in "#FFFFFF" or "FFFFFF" format
648 //int float[3] rgb colors in range 0.0f-1.0f suitable for use in opengl
649 int ir, ig, ib;
650 long ic;
651 char *shex;
652 shex = html;
653 if(shex[0] == '#') shex = &shex[1];
654 ic = strtol(shex,NULL,16);
655 ib = (ic & 0xFF);
656 ig = (ic & 0xFF00) >> 8;
657 ir = (ic & 0xFF0000) >> 16;
658 rgb[0] = (float)ir/255.0f;
659 rgb[1] = (float)ig/255.0f;
660 rgb[2] = (float)ib/255.0f;
661}
662char *hexpermitted = " #0123456789ABCDEFabcdef";
663
664// OLD_IPHONE_AQUA #ifdef AQUA
665// OLD_IPHONE_AQUA #include <malloc/malloc.h>
666// OLD_IPHONE_AQUA #else
667
668#include <malloc.h>
669
670// OLD_IPHONE_AQUA #endif
671
672#include <string.h>
673int colorsoption2colorscheme(const char *optionstring, colorScheme *cs){
674 //converts html colors given for freewrl command line option:
675 // --ui_colors "#FFFFFF,#FFFFFF,#FFFFFF,#FFFFFF" (for panel, menuicon, statusText, messageText)
676 //into 4 float [0-1] rgb colors suitable for use in opengl calls
677 //returns number of successfully parsed numbers
678 int len,i,count;
679 char *str, *html, *stok; //4 colors per color scheme
680 len = strlen(optionstring);
681 str = alloca(len+1); //alloca on stack so its freed automatically at end of function, _msc can't do str[len]
682 strcpy(str,optionstring);
683 //clean string
684 for(i=0;i<len;i++){
685 if(!strchr(hexpermitted,str[i])){
686 str[i] = ' ';
687 }
688 }
689 //find color substrings ie strtok
690 count = 0;
691 stok = str;
692 for(i=0;i<4;i++){
693 html = strtok(stok," ");
694 if(!html) {
695 if(cs->menuIcon) html = cs->menuIcon;
696 else html = "#FFFFFF";
697 }
698 switch(i){
699 case 0: cs->panel = strdup(html); break;
700 case 1: cs->menuIcon = strdup(html); break;
701 case 2: cs->statusText = strdup(html); break;
702 case 3: cs->messageText = strdup(html); break;
703 default:
704 break;
705 }
706 count++;
707 stok = NULL;
708 }
709 return count;
710}
711
712colorScheme *search_ui_colorscheme(char *colorschemename){
713 int i;
714 colorScheme *cs = NULL;
715 i = 0;
716 do{
717 if(!strcmp(colorSchemes[i].name,colorschemename)){
718 cs = &colorSchemes[i];
719 break;
720 }
721 i++;
722 }while(colorSchemes[i].name);
723 return cs;
724}
725int fwl_set_ui_colorscheme(char *colorschemename){
726 colorScheme *cs;
727 ppcommon p = (ppcommon)gglobal()->common.prv;
728 cs = search_ui_colorscheme(colorschemename);
729 if(cs) {
730 p->colorScheme = cs;
731 p->colorSchemeChanged++;
732 }
733 return 1;
734}
735// set here from commandline options
736// --ui_colorscheme "angry"
737// --ui_colors "#FFFFFF,#FFFFFF,#FFFFFF,#FFFFFF" panel, menuIcon, statusText, messsageText
738
739void fwl_set_ui_colors(char *fourhtmlcolors){
740 colorScheme *cs;
741 ppcommon p = (ppcommon)gglobal()->common.prv;
742 cs = search_ui_colorscheme("custom");
743 colorsoption2colorscheme(fourhtmlcolors, cs);
744 p->colorScheme = (void *)cs;
745 p->colorSchemeChanged++;
746}
747char *fwl_get_ui_colorschemename(){
748 colorScheme *cs;
749 ppcommon p = (ppcommon)gglobal()->common.prv;
750 cs = (colorScheme*)p->colorScheme;
751 return cs->name;
752}
753void fwl_next_ui_colorscheme(){
754 int i;
755 colorScheme *cs;
756 char *colorschemename;
757 //ppcommon p = (ppcommon)gglobal()->common.prv;
758
759 colorschemename = fwl_get_ui_colorschemename();
760 i = 0;
761 do{
762 if(!strcmp(colorSchemes[i].name,colorschemename)){
763 cs = &colorSchemes[i+1];
764 if(!cs->name){
765 cs = &colorSchemes[0]; //start over
766 }
767 if(!strcmp(cs->name,"custom")){
768 cs = &colorSchemes[0]; //skip custom and start over
769 }
770 fwl_set_ui_colorscheme(cs->name);
771 break;
772 }
773 i++;
774 }while(colorSchemes[i].name);
775
776}
777
778//want to compile-in the default color scheme? just define UI_COLORSCHEME_DEFAULT in your config.h
779#ifndef UI_COLORSCHEME_DEFAULT
780#define UI_COLORSCHEME_DEFAULT "neon:lime" //"original" "favicon" "midnight" "aqua" "angry" "neon:cyan" "neon:yellow" "neon:lime" "neon:pink"
781#endif
782void fwl_get_ui_color(char *use, float *rgb){
783 colorScheme *cs;
784 ppcommon p = (ppcommon)gglobal()->common.prv;
785 if(!p->colorScheme){
786 p->colorScheme = search_ui_colorscheme(UI_COLORSCHEME_DEFAULT); //"original");
787 p->colorSchemeChanged++;
788 }
789 cs = p->colorScheme;
790 if(!strcmp(use,"panel")){
791 color_html2rgb(cs->panel, rgb);
792 }else if(!strcmp(use,"menuIcon")){
793 color_html2rgb(cs->menuIcon, rgb);
794 }else if(!strcmp(use,"statusText")){
795 color_html2rgb(cs->statusText, rgb);
796 }else if(!strcmp(use,"messageText")){
797 color_html2rgb(cs->messageText, rgb);
798 }
799}
800int fwl_get_ui_color_changed(){
801 ppcommon p = (ppcommon)gglobal()->common.prv;
802 return p->colorSchemeChanged;
803}
804// end ui colors <<<<<<<<<<<<<<<
805
806
807// fwl_command() >>>>>>>>>>
808/* generally: I'm tired of writing fwl_setThisOrThat() functions. I want a simpler interface.
809 one idea is to have a set_keyval(key,val) function and a set_command(key) function.
810 another idea is to have a set_commandline(commandline) function and it would split
811 on a separator like ',' or ' ' and decide if it has a parameter or not.
812 Now from your front end you can call:
813 fwl_commandline("pin,FF");
814 Or if calling through the dllfreewrl api wrapper:
815 dllFreeWRL_commandline(fwctx,"pin,FF");
816
817 PS. A benefit of storing View settings/preferences like colorscheme and pinning
818 in the Model part of MVC is that several Views can be setting and/or polling
819 the settings on each frame.
820 For example commandline options can set, then javascript Browser.keyvalue can set or get,
821 then statusbarHud can poll or set, then Motif or .net Gui can poll or set. And they have
822 a common place to set, and poll. If there's no statusbarHud, and no GUI, nothing
823 breaks: commandline options still has a place to put the values. Even though they
824 aren't used in the backend/Model.
825*/
826#include <scenegraph/Viewer.h>
827
828int fwl_setDragChord(char *chordname);
829int fwl_setKeyChord(char *chordname);
830int print_help();
831int fwl_keyval(char *key, char *val);
832
833int searchkeyvals(char *key){
834 int i, iret;
835 ppcommon p = (ppcommon)gglobal()->common.prv;
836 if(!p->keyvals)
837 p->keyvals = newVector(keyval,4);
838 iret = -1;
839 for(i=0;i<vectorSize(p->keyvals);i++){
840 keyval k_v = vector_get(keyval,p->keyvals,i);
841 if(!strcmp(k_v.key,key)){
842 iret = i;
843 break;
844 }
845 }
846 return iret;
847}
848int set_key_val(char *key, char *val){
849 int index;
850 keyval k_v;
851 ppcommon p = (ppcommon)gglobal()->common.prv;
852
853 index = searchkeyvals(key);
854 if(index < 0){
855 if(!p->keyvals)
856 p->keyvals = newVector(keyval,4);
857 k_v.key = STRDUP(key);
858 k_v.val = STRDUP(val);
859 vector_pushBack(keyval,p->keyvals,k_v);
860 }else{
861 k_v = vector_get(keyval,p->keyvals,index);
862 FREE_IF_NZ(k_v.val);
863 k_v.val = STRDUP(val);
864 vector_set(keyval,p->keyvals,index,k_v);
865 }
866 return 1;
867}
868int set_keyval(char *keyval){
869 //save arbitrary char* keyval = "key,val" pairs,
870 // for later retrieval with print_keyval or get_key_val
871 int i, iret;
872 char kv[100];
873 ppcommon p = (ppcommon)gglobal()->common.prv;
874 if(!p->keyvals)
875 p->keyvals = newVector(keyval,4);
876 i = strlen(keyval);
877 iret = 0;
878 if(i > 100)
879 iret = -1;
880 else
881 {
882 char *sep;
883 strcpy(kv,keyval);
884 sep = strchr(kv,' ');
885 if(!sep) sep = strchr(kv,',');
886 if(sep){
887 char *key, *val;
888 val = &sep[1];
889 (*sep) = '\0';
890 key = kv;
891 set_key_val(key,val);
892 iret = 1;
893 }
894 }
895 return iret;
896}
897char *get_key_val(char *key){
898 int index;
899 keyval k_v;
900 ppcommon p = (ppcommon)gglobal()->common.prv;
901
902 index = searchkeyvals(key);
903 if(index < 0) return NULL;
904 k_v = vector_get(keyval,p->keyvals,index);
905 return k_v.val; //warning not strduped here, caller doesn't own, just looking
906}
907int print_keyval(char *key){
908 int index;
909 ppcommon p = (ppcommon)gglobal()->common.prv;
910 index = searchkeyvals(key);
911 if(index < 0)
912 ConsoleMessage("\n key %s not found\n",key);
913 else{
914 keyval k_v;
915 k_v = vector_get(keyval,p->keyvals,index);
916 ConsoleMessage("\n key=%s val=%s\n",key,k_v.val);
917 }
918 return 1;
919}
920//int fwl_hyper_option(char *val);
921int ssr_test(char *keyval);
922struct command {
923 char *key;
924 int (*cmdfunc)();
925 int (*valfunc)(char *val);
926 char *helpstring;
927} commands [] = {
928 {"dragchord",NULL,fwl_setDragChord,"[yawz,yawpitch,roll,xy]"},
929 {"keychord", NULL,fwl_setKeyChord,"[yawz,yawpitch,roll,xy]"},
930 {"navmode",NULL,fwl_setNavMode,"[walk,fly,examine,explore,spherical,turntable,lookat]"},
931 {"help",print_help,NULL,NULL},
932 {"pin",NULL,fwl_set_sbh_pin_option,"[tf,tt,ft,ff]"},
933 {"colorscheme",NULL,fwl_set_ui_colorscheme,"[original,midnight,angry,favicon,aqua,neon:lime,neon:yellow,neon:cyan,neon:pink]"},
934 {"set_keyval",NULL,set_keyval,"key,val"},
935 {"print_keyval",NULL,print_keyval,"key"},
936 //{"hyper_option",NULL,fwl_hyper_option,"[0 - 10]"},
937#ifdef SSR_SERVER
938 {"ssrtest",NULL,ssr_test,"nav,val"},
939#endif
940 {"",print_help,NULL,NULL}, //bootstrap user knowhow by spacebarEnter lucky sequence
941 {NULL,NULL,NULL},
942};
943int print_help(){
944 int i, ret = 1;
945 ConsoleMessage("\n%s\n","spacebar commands: spacebar:key[,val]Enter");
946 i = 0;
947 while(commands[i].key){
948 if(commands[i].helpstring)
949 ConsoleMessage(" %s,%s\n",commands[i].key,commands[i].helpstring);
950 else
951 ConsoleMessage(" %s\n",commands[i].key);
952 i++;
953 }
954 return ret;
955}
956struct command *getCommand(char *key){
957 struct command *ret;
958 int i;
959 i = 0;
960 ret = NULL;
961 while(commands[i].key){
962 if(!strcmp(key,commands[i].key)){
963 ret = &commands[i];
964 break;
965 }
966 i++;
967 }
968 return ret;
969}
970int fwl_keyval(char *key, char *val){
971 struct command *cmd;
972 int ok = 0;
973 cmd = getCommand(key);
974 if(cmd){
975 if(cmd->valfunc)
976 ok = cmd->valfunc(val);
977 }
978 return ok;
979}
980int fwl_command(char *key){
981 struct command *cmd;
982 int ok = 0;
983 cmd = getCommand(key);
984 if(cmd){
985 if(cmd->cmdfunc)
986 ok = cmd->cmdfunc();
987 }
988 return ok;
989}
990int fwl_commandline(char *cmdline){
991 char *sep = strchr(cmdline,' ');
992 if(!sep) sep = strchr(cmdline,',');
993 if(sep){
994 int keylen;
995 char *key, *val;
996 val = strdup(&sep[1]);
997 keylen = (int)(sep - cmdline);
998 //(*sep) = '\0';
999 key = strndup(cmdline,keylen +1);
1000 key[keylen] = '\0';
1001 //printf("key=[%s] val=[%s]\n",key,val);
1002 fwl_keyval(key,val);
1003 free(key);
1004 free(val);
1005 }else{
1006 //not key,val, just command
1007 fwl_command(cmdline);
1008 }
1009 return 1;
1010}
1011
1012// fwl_command() <<<<<<<<<<
1013
1014void fwl_setDensityFactor(float density_factor){
1015 // mobile device APIs sometimes can give you a hint
1016 // you can use to scale UI elements to human size
1017 // for example a human finger tip has a constant size in the physical world
1018 // as screen resolutions DPI have increased, its been necessary to scale buttons
1019 // up by some factor depending on the DPI relative to good old fashioned 160 DPI
1020 ppcommon p = (ppcommon)gglobal()->common.prv;
1021 p->density_factor = density_factor;
1022}
1023float fwl_getDensityFactor(){
1024 ppcommon p = (ppcommon)gglobal()->common.prv;
1025 return p->density_factor;
1026}
1027int fwl_getPedal(){
1028 ppcommon p = (ppcommon)gglobal()->common.prv;
1029 return p->pedal;
1030}
1031void fwl_setPedal(int pedal){
1032 ppcommon p = (ppcommon)gglobal()->common.prv;
1033 p->pedal = pedal; //0 means off, 1 means on
1034}
1035int fwl_getHover(){
1036 ppcommon p = (ppcommon)gglobal()->common.prv;
1037 return p->hover;
1038}
1039void fwl_setHover(int hover){
1040 ppcommon p = (ppcommon)gglobal()->common.prv;
1041 p->hover = hover; //0 means off, 1 means on
1042}
1043void fwl_setDrawBoundingBoxes(int drawbb){
1044 ppcommon p = (ppcommon)gglobal()->common.prv;
1045 p->draw_bounding_boxes = drawbb; //0 means off, 1 means on
1046}
1047int fwl_getDrawBoundingBoxes(){
1048 ppcommon p = (ppcommon)gglobal()->common.prv;
1049 return p->draw_bounding_boxes; //0 means off, 1 means on
1050}
1051void fwl_setShowViewpoints(int show){
1052 ppcommon p = (ppcommon)gglobal()->common.prv;
1053 p->show_viewpoints = show; //0 means off, 1 means on
1054}
1055int fwl_getShowViewpoints(){
1056 ppcommon p = (ppcommon)gglobal()->common.prv;
1057 return p->show_viewpoints; //0 means off, 1 means on
1058}
1059void fwl_setDrawRig(int draw) {
1060 ppcommon p = (ppcommon)gglobal()->common.prv;
1061 p->draw_rig = draw; //0 means off, 1 means on
1062}
1063int fwl_getDrawRig() {
1064 ppcommon p = (ppcommon)gglobal()->common.prv;
1065 return p->draw_rig; //0 means off, 1 means on
1066}
Definition Viewer.h:141