Browse code

Prioritize loading image under cursor. Use sleep in background thread. Use nanosleep instead of select for sleeping.

Dario Rodriguez authored on 28/09/2025 11:09:14
Showing 1 changed files
... ...
@@ -176,6 +176,10 @@ rayui_getmousedata(rayui_t *rayui, mousedata_t *mousedata)
176 176
           )?1:0;
177 177
         mousedata->needs_nextredraw=0;
178 178
         /* mouse process scrolling */
179
+#if 1
180
+if(mousedata->lmbdown!=mousedata->oldlmbdown)
181
+ fprintf(stderr,"[%s:+%i+%i] ===\n",(mousedata->lmbdown==1)?"DOWN":".UP.",(int)mousedata->mousepos.x,(int)mousedata->mousepos.y);
182
+#endif
179 183
         if(mousedata->lmbdown==1 && mousedata->oldlmbdown==0 && mousedata->scrollstart==0) {
180 184
                 for(i=0;i<mousedata->usedscrollablerect;i++) {
181 185
                         if(is_global_insidewhxy(mousedata->mousepos,&(mousedata->scrollablerect[i].whxy),0)) {
... ...
@@ -187,7 +191,17 @@ rayui_getmousedata(rayui_t *rayui, mousedata_t *mousedata)
187 191
                                 break;
188 192
                         }
189 193
                 }
194
+#if 1
195
+if(i>=mousedata->usedscrollablerect) {
196
+fprintf(stderr,"Couldn't start scrolling because not in scrollable area\n");
197
+}
198
+#endif
190 199
         }
200
+#if 1
201
+else if(mousedata->lmbdown==1 && mousedata->oldlmbdown==0 && mousedata->scrollstart!=0) {
202
+fprintf(stderr,"Couldn't start scrolling because scrollstart!=0\n");
203
+}
204
+#endif
191 205
         if(mousedata->scrollstart!=0 && mousedata->lmbdown==0) {
192 206
                 mousedata->scrollstart=0;
193 207
                 if(mousedata->is_scrolling)
Browse code

Move video init and mouse logic to rayui.c/rayui.h . Move background loading to bg.c/bg.h

Dario Rodriguez authored on 20/09/2025 18:36:52
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,760 @@
1
+/*
2
+ * rayui.c
3
+ *
4
+ * Collections of functions to aid creating GUIs using raylib
5
+ *
6
+ * History:
7
+ *      20250902 Creation from imgmover prototype.
8
+ *      20250920 Reimplemented all scroll functionality.
9
+ *
10
+ * Author: Dario Rodriguez dario@darionomono.com
11
+ * (c) Dario Rodriguez 2025
12
+ * This program is licensed under the terms of GNU GPL v2.1+
13
+ */
14
+
15
+#include <stdio.h>
16
+#include <stdlib.h>
17
+#include <unistd.h>
18
+#include <string.h>
19
+#include <stdarg.h>
20
+#include <sys/time.h>
21
+#include "raylib.h"
22
+#include "rayui.h"
23
+#include "roboto_regular.c"
24
+
25
+#define DEFAULTTARGETFPS 30
26
+#if defined(ANDROID) || defined(SIMANDROID)
27
+#define FONTSIZE 64
28
+#define FONTBIGSIZE 96
29
+#define FONTHUGESIZE 128
30
+#else
31
+#define FONTSIZE 18
32
+#define FONTBIGSIZE 32
33
+#define FONTHUGESIZE 48
34
+#endif
35
+
36
+#if !defined(__linux__) && !defined(ANDROID) && RAYLIB_VERSION_MAJOR==5 && RAYLIB_VERSION_MINOR==0
37
+/* the old raylib used in the windows build lacks this function */
38
+bool IsImageValid(Image image)
39
+{
40
+    bool result = false;
41
+
42
+    if ((image.data != NULL) &&     // Validate pixel data available
43
+        (image.width > 0) &&        // Validate image width
44
+        (image.height > 0) &&       // Validate image height
45
+        (image.format > 0) &&       // Validate image format
46
+        (image.mipmaps > 0)) result = true; // Validate image mipmaps (at least 1 for basic mipmap level)
47
+
48
+    return result;
49
+}
50
+#endif
51
+
52
+menubar_t *rayuimenubar_init(char *menus, font_t *font);
53
+void rayuimenubar_free(menubar_t *menubar);
54
+
55
+int rayuimenubar_mouse(menubar_t *menubar, Vector2 mousepos, int lmbpressed, int lmbreleased, int lmbdown, int *click_avail, char **sel_menu, char **sel_submenu);
56
+int rayuimenubar_draw(menubar_t *menubar, int windowwidth, int windowheight, int *needs_nextredraw);
57
+
58
+static int *intglobal_getcodepoints(int *sizecodepoints);
59
+
60
+int intglobal_menu_count(char *menus);
61
+char *intglobal_menu_get(char *menus, int targetn, int *len);
62
+int intglobal_submenu_count(char *menus);
63
+char *intglobal_submenu_get(char *menus, int targetsubn, int *len);
64
+
65
+char *intglobal_strduplen(char *str, int len);
66
+
67
+int intglobal_menudata_pos2option(menudata_t *menudata, Vector2 pos);
68
+
69
+static char *intglobal_messageboxcaption(char *newcaption);
70
+
71
+rayui_t *
72
+rayui_init(int w, int h, char *title, char *menus)
73
+{
74
+        rayui_t *rayui;
75
+        char *errstr;
76
+        if((rayui=calloc(1,sizeof(rayui_t)))==NULL) {
77
+                rayui_free(rayui),rayui=NULL;
78
+                return(NULL); /* insuf. mem. */
79
+        }
80
+        rayui->defaultfontdata=(const unsigned char *)roboto_regular;
81
+        rayui->sizedefaultfontdata=sizeof(roboto_regular)-1;
82
+        /* init window */
83
+        SetTraceLogLevel(LOG_ERROR);
84
+        InitWindow((rayui->w=w),(rayui->h=h),(title==NULL)?"rayui":title);
85
+        rayui->windowinit=1;
86
+        SetTargetFPS(DEFAULTTARGETFPS);
87
+        rayui->targetfps=DEFAULTTARGETFPS;
88
+        /* init fonts and contents */
89
+        if(title!=NULL)
90
+                intglobal_messageboxcaption(title);
91
+        if((errstr="Couldn't init font")==NULL
92
+          || (rayui->font=rayuifont_init(FONTSIZE))==NULL
93
+          || (rayui->fontbig=rayuifont_init(FONTBIGSIZE))==NULL
94
+          || (rayui->fonthuge=rayuifont_init(FONTHUGESIZE))==NULL
95
+          || (errstr="Couldn't init menus")==NULL
96
+          || (menus!=NULL && (rayui->menubar=rayuimenubar_init(menus,rayui->font))==NULL)
97
+        ) {
98
+                global_messagebox("%s",errstr);
99
+                rayui_free(rayui),rayui=NULL;
100
+                return(NULL); /* insuf. mem. */
101
+        }
102
+        return(rayui);
103
+}
104
+
105
+void
106
+rayui_free(rayui_t *rayui)
107
+{
108
+        if(rayui==NULL)
109
+                return;
110
+        if(rayui->menubar!=NULL)
111
+                rayuimenubar_free(rayui->menubar),rayui->menubar=NULL;
112
+        if(rayui->font!=NULL)
113
+                rayuifont_free(rayui->font),rayui->font=NULL;
114
+        if(rayui->fontbig!=NULL)
115
+                rayuifont_free(rayui->fontbig),rayui->fontbig=NULL;
116
+        if(rayui->fonthuge!=NULL)
117
+                rayuifont_free(rayui->fonthuge),rayui->fonthuge=NULL;
118
+        if(rayui->windowinit)
119
+                CloseWindow(),rayui->windowinit=0;
120
+        free(rayui),rayui=NULL;
121
+        return;
122
+}
123
+
124
+
125
+int
126
+rayui_scrollablerectreset(rayui_t *rayui, mousedata_t *mousedata)
127
+{
128
+        if(rayui==NULL || mousedata==NULL)
129
+                return(-1);
130
+        mousedata->usedscrollablerect=0;
131
+        return(0);
132
+}
133
+
134
+int
135
+rayui_scrollablerectadd(rayui_t *rayui, mousedata_t *mousedata,whxy_t *rect, char *id, int scrollpos, int (*tryselect)(void *userptr, Vector2 mousepos),void *userptr, int scrollthreshold)
136
+{
137
+        scrollrect_t *scrollrect;
138
+        if(rayui==NULL || mousedata==NULL || rect==NULL || id==NULL)
139
+                return(-1);
140
+        if(mousedata->usedscrollablerect==MAXSCROLLABLERECT)
141
+                return(-1);
142
+        scrollrect=mousedata->scrollablerect+mousedata->usedscrollablerect;
143
+        memcpy(&(scrollrect->whxy),rect,sizeof(whxy_t));
144
+        strncpy(scrollrect->id,id,SIZESCROLLABLERECTID);
145
+        scrollrect->id[sizeof(scrollrect->id)-1]='\0';
146
+        scrollrect->scrollpos=scrollpos;
147
+        scrollrect->tryselect=tryselect;
148
+        scrollrect->userptr=userptr;
149
+        scrollrect->scrollthreshold=scrollthreshold;
150
+        mousedata->usedscrollablerect++;
151
+        return(0);
152
+}
153
+
154
+int
155
+rayui_getmousedata(rayui_t *rayui, mousedata_t *mousedata)
156
+{
157
+        int i;
158
+        if(rayui==NULL || mousedata==NULL)
159
+                return(-1);
160
+        mousedata->oldmousepos=mousedata->mousepos;
161
+        mousedata->mousepos=GetMousePosition();
162
+        mousedata->wheel=GetMouseWheelMoveV();
163
+        mousedata->lmbpressed=IsMouseButtonPressed(0);
164
+        mousedata->lmbreleased=IsMouseButtonReleased(0);
165
+        mousedata->oldlmbdown=mousedata->lmbdown;
166
+        mousedata->lmbdown=IsMouseButtonDown(0);
167
+        mousedata->oldrmbdown=mousedata->rmbdown;
168
+        mousedata->rmbdown=IsMouseButtonDown(1);
169
+        mousedata->click_avail=1;
170
+        mousedata->has_mousechanges=(mousedata->lmbdown!=mousedata->oldlmbdown
171
+            || mousedata->rmbdown!=mousedata->oldrmbdown
172
+            || mousedata->mousepos.x!=mousedata->oldmousepos.x
173
+            || mousedata->mousepos.y!=mousedata->oldmousepos.y
174
+            || mousedata->wheel.x!=0
175
+            || mousedata->wheel.y!=0
176
+          )?1:0;
177
+        mousedata->needs_nextredraw=0;
178
+        /* mouse process scrolling */
179
+        if(mousedata->lmbdown==1 && mousedata->oldlmbdown==0 && mousedata->scrollstart==0) {
180
+                for(i=0;i<mousedata->usedscrollablerect;i++) {
181
+                        if(is_global_insidewhxy(mousedata->mousepos,&(mousedata->scrollablerect[i].whxy),0)) {
182
+                                strncpy(mousedata->scrollingid,mousedata->scrollablerect[i].id,sizeof(mousedata->scrollingid));
183
+                                mousedata->scrollingid[sizeof(mousedata->scrollingid)-1]='\0';
184
+                                mousedata->scrollstart=global_currentmilliseconds();
185
+                                mousedata->scrollstartpos=mousedata->mousepos;
186
+                                mousedata->scrollposstart=mousedata->scrollablerect[i].scrollpos;
187
+                                break;
188
+                        }
189
+                }
190
+        }
191
+        if(mousedata->scrollstart!=0 && mousedata->lmbdown==0) {
192
+                mousedata->scrollstart=0;
193
+                if(mousedata->is_scrolling)
194
+                        mousedata->click_avail=0; /* this click is the mouseup of the scroll */
195
+        }
196
+        mousedata->is_scrolling=(mousedata->scrollstart==0)?0:mousedata->is_scrolling;
197
+        if(mousedata->is_scrolling==0 && mousedata->scrollstart!=0) {
198
+                for(i=0;i<mousedata->usedscrollablerect;i++) {
199
+                        if(is_global_insidewhxy(mousedata->mousepos,&(mousedata->scrollablerect[i].whxy),0) && mousedata->scrollablerect[i].tryselect!=NULL) {
200
+                                float t;
201
+                                t=mousedata->scrollstartpos.y-mousedata->mousepos.y;
202
+                                t=(t<0)?-t:t;
203
+                                if(t>mousedata->scrollablerect[i].scrollthreshold) {
204
+                                        mousedata->is_scrolling=1;
205
+                                        mousedata->scrolllast=0;
206
+                                }
207
+                                t=mousedata->scrollstartpos.x-mousedata->mousepos.x;
208
+                                t=(t<0)?-t:t;
209
+                                if(t>mousedata->scrollablerect[i].scrollthreshold) {
210
+                                        mousedata->is_scrolling=0,mousedata->scrollstart=0;
211
+                                        if(mousedata->scrollablerect[i].tryselect(mousedata->scrollablerect[i].userptr,mousedata->mousepos)!=-1) {
212
+                                                mousedata->click_avail=0;
213
+                                                break;
214
+                                        }
215
+                                }
216
+                        }
217
+                }
218
+        }
219
+        if(mousedata->is_scrolling) {
220
+                long long tcur,tdif;
221
+                long long ycur;
222
+                tcur=global_currentmilliseconds();
223
+                tdif=tcur-mousedata->scrolllast;
224
+                ycur=mousedata->scrollstartpos.y-mousedata->mousepos.y;
225
+                mousedata->scrollspeed=(tdif>0)?(mousedata->oldmousepos.y-mousedata->mousepos.y)*100000/tdif:0;
226
+                mousedata->scrollpos=mousedata->scrollposstart+ycur;
227
+                mousedata->scrolllast=tcur;
228
+        }
229
+        if(mousedata->is_scrolling==0 && mousedata->scrollspeed!=0) {
230
+                mousedata->scrollspeed=mousedata->scrollspeed*4/5;
231
+                mousedata->scrollpos+=mousedata->scrollspeed;
232
+        }
233
+        return(0);
234
+}
235
+
236
+int
237
+is_rayui_scrolling(rayui_t *rayui, mousedata_t *mousedata,char *id, int *newscrollpos)
238
+{
239
+        if(rayui==NULL || mousedata==NULL || id==NULL || newscrollpos==NULL)
240
+                return(0);
241
+        if((mousedata->is_scrolling || mousedata->scrollspeed!=0) && strcmp(mousedata->scrollingid,id)==0) {
242
+                *newscrollpos=mousedata->scrollpos;
243
+                return(1);
244
+        }
245
+        return(0);
246
+}
247
+
248
+void
249
+rayui_settargetfps(rayui_t *rayui, int newfps)
250
+{
251
+        if(rayui==NULL)
252
+                return;
253
+        newfps=(newfps<=0)?DEFAULTTARGETFPS:newfps;
254
+        rayui->targetfps=newfps;
255
+        SetTargetFPS(newfps);
256
+        return;
257
+}
258
+
259
+int
260
+has_rayui_timeleft(rayui_t *rayui)
261
+{
262
+        struct timeval *deadline,now;
263
+        if(rayui==NULL)
264
+                return(1);
265
+        deadline=&(rayui->deadline);
266
+        gettimeofday(&now,NULL);
267
+        if(deadline->tv_sec<now.tv_sec || (deadline->tv_sec==now.tv_sec && deadline->tv_usec<now.tv_usec))
268
+                return(0);
269
+        return(1);
270
+}
271
+
272
+void
273
+rayui_timereset(rayui_t *rayui)
274
+{
275
+        struct timeval *deadline;
276
+        long deadlineincr;
277
+        if(rayui==NULL)
278
+                return;
279
+        deadline=&(rayui->deadline);
280
+        gettimeofday(deadline,NULL);
281
+        deadlineincr=1000000L/((rayui->targetfps!=0)?rayui->targetfps:DEFAULTTARGETFPS);
282
+        deadline->tv_usec+=deadlineincr;
283
+        deadline->tv_sec+=(deadline->tv_usec)/1000000L;
284
+        deadline->tv_usec%=1000000L;
285
+}
286
+
287
+
288
+long long
289
+global_currentmilliseconds(void)
290
+{
291
+        long long res;
292
+        struct timeval now;
293
+        gettimeofday(&now,NULL);
294
+        res=((long long) (now.tv_sec))*1000000L+((long long) (now.tv_usec));
295
+        return(res);
296
+}
297
+
298
+font_t *
299
+rayuifont_init(int size)
300
+{
301
+        font_t *font;
302
+        int sizecodepoints;
303
+        int *codepoints;
304
+        if((font=calloc(1,sizeof(font_t)))==NULL)
305
+                return(NULL); /* insuf. mem. */
306
+        font->height=size;
307
+        codepoints=intglobal_getcodepoints(&sizecodepoints);
308
+        font->font=LoadFontFromMemory(".ttf",(const unsigned char *)roboto_regular,sizeof(roboto_regular)-1,font->height,codepoints,sizecodepoints);
309
+        return(font);
310
+}
311
+
312
+void
313
+rayuifont_free(font_t *font)
314
+{
315
+        if(font==NULL)
316
+                return;
317
+        /* NOTE: Cannot call UnloadFont(font->font) as the data was not malloc'd; see https://github.com/raysan5/raylib/blob/master/examples/others/embedded_files_loading.c */
318
+        free(font),font=NULL;
319
+        return;
320
+}
321
+
322
+menubar_t *
323
+rayuimenubar_init(char *menus, font_t *font)
324
+{
325
+        int i,j;
326
+        char *str,*substr;
327
+        int len,sublen;
328
+        menubar_t *menubar;
329
+        menudata_t *menudata;
330
+        if(menus==NULL || font==NULL)
331
+                return(NULL); /* sanity check failed */
332
+        if((menubar=calloc(1,sizeof(menubar_t)))==NULL
333
+          || (menubar->sizemenudata=intglobal_menu_count(menus))<=0
334
+          || (menubar->menudata=calloc(menubar->sizemenudata,sizeof(menudata_t)))==NULL
335
+        ) {
336
+                rayuimenubar_free(menubar),menubar=NULL;
337
+                return(NULL); /* insuf. mem. */
338
+        }
339
+        menubar->ptrfont=font;
340
+        menubar->height=font->height+font->height/2;
341
+        /* init menus */
342
+        for(i=0;i<menubar->sizemenudata;i++) {
343
+                if((menudata=menubar->menudata[i]=calloc(1,sizeof(menudata_t)))==NULL
344
+                  || (str=intglobal_menu_get(menus,i,&len))==NULL
345
+                  || (menudata->title=intglobal_strduplen(str,len))==NULL
346
+                  || (menudata->sizeoptions=intglobal_submenu_count(str))<=0
347
+                  || (menudata->options=calloc(menudata->sizeoptions,sizeof(char *)))==NULL
348
+                ) {
349
+                        rayuimenubar_free(menubar),menubar=NULL;
350
+                        return(NULL); /* insuf. mem. */
351
+                }
352
+                for(j=0;j<menudata->sizeoptions;j++) {
353
+                        if((substr=intglobal_submenu_get(str,j,&sublen))==NULL
354
+                          || (menudata->options[j]=intglobal_strduplen(substr,sublen))==NULL
355
+                        ) {
356
+                                rayuimenubar_free(menubar),menubar=NULL;
357
+                                return(NULL); /* insuf. mem. */
358
+                        }
359
+                }
360
+        }
361
+        return(menubar);
362
+}
363
+
364
+void
365
+rayuimenubar_free(menubar_t *menubar)
366
+{
367
+        int i,j;
368
+        menudata_t *menudata;
369
+        if(menubar==NULL)
370
+                return;
371
+        if(menubar->menudata!=NULL) {
372
+                for(i=0;i<menubar->sizemenudata;i++) {
373
+                        if((menudata=menubar->menudata[i])==NULL)
374
+                                continue;
375
+                        if(menudata->title!=NULL)
376
+                                free(menudata->title),menudata->title=NULL;
377
+                        if(menudata->options!=NULL) {
378
+                                for(j=0;j<menudata->sizeoptions;j++) {
379
+                                        if(menudata->options[j]!=NULL)
380
+                                                free(menudata->options[j]),menudata->options[j]=NULL;
381
+                                }
382
+                                free(menudata->options),menudata->options=NULL,menudata->sizeoptions=0;
383
+                        }
384
+                        free(menubar->menudata[i]),menubar->menudata[i]=NULL,menudata=NULL;
385
+                }
386
+                free(menubar->menudata),menubar->menudata=NULL,menubar->sizemenudata=0;
387
+        }
388
+        free(menubar),menubar=NULL;
389
+        return;
390
+}
391
+
392
+int
393
+rayuimenubar_mouse(menubar_t *menubar, Vector2 mousepos, int lmbpressed, int lmbreleased, int lmbdown, int *click_avail, char **sel_menu, char **sel_submenu)
394
+{
395
+        int flag_outsideall;
396
+        int i,j;
397
+        if(menubar==NULL || click_avail==NULL || sel_menu==NULL || sel_submenu==NULL)
398
+                return(-1);
399
+        *click_avail=1;
400
+        *sel_menu=NULL;
401
+        *sel_submenu=NULL;
402
+        flag_outsideall=1;
403
+#if 0
404
+if(lmbpressed || lmbdown)
405
+ fprintf(stderr,"in_menubar_mouse: lmbpressed:%i lmbdown:%i\n",lmbpressed,lmbdown);
406
+#endif
407
+        for(i=0;i<menubar->sizemenudata;i++) {
408
+                int insidetitle,currentoption;
409
+                insidetitle=is_global_insidewhxy(mousepos,&(menubar->menudata[i]->whxy),0);
410
+                currentoption=intglobal_menudata_pos2option(menubar->menudata[i],mousepos);
411
+                flag_outsideall=(currentoption!=-1 || insidetitle)?0:flag_outsideall;
412
+                if(lmbreleased && insidetitle) {
413
+                        for(j=0;j<menubar->sizemenudata;j++) {
414
+                                menubar->menudata[j]->flag_stickyopen=(j==i)?1:0;
415
+                                menubar->menudata[j]->flag_open=0;
416
+                                menubar->menudata[j]->currentoption=-1;
417
+                        }
418
+                } else if((lmbpressed || lmbdown) && insidetitle) {
419
+                        for(j=0;j<menubar->sizemenudata;j++) {
420
+                                menubar->menudata[j]->flag_open=(j==i)?1:0;
421
+                                menubar->menudata[j]->flag_stickyopen=0;
422
+                                menubar->menudata[j]->currentoption=-1;
423
+                        }
424
+                } else if((lmbdown || menubar->menudata[i]->flag_stickyopen) && currentoption!=-1) {
425
+                        for(j=0;j<menubar->sizemenudata;j++) {
426
+                                if(lmbreleased==0 || j!=i || menubar->menudata[i]->flag_stickyopen==0) {
427
+                                        menubar->menudata[j]->flag_open=(j==i)?menubar->menudata[i]->flag_open:0;
428
+                                        menubar->menudata[j]->flag_stickyopen=(j==i)?menubar->menudata[i]->flag_stickyopen:0;
429
+                                        menubar->menudata[j]->currentoption=(j==i)?currentoption:-1;
430
+                                } else {
431
+                                        menubar->menudata[j]->flag_open=0;
432
+                                        menubar->menudata[j]->flag_stickyopen=0;
433
+                                        menubar->menudata[j]->currentoption=-1;
434
+                                        /* has selected this submenu */
435
+                                        *click_avail=0;
436
+                                        *sel_menu=menubar->menudata[j]->title;
437
+                                        *sel_submenu=menubar->menudata[j]->options[currentoption];
438
+                                }
439
+                        }
440
+                } else if(menubar->menudata[i]->flag_stickyopen && currentoption==-1) {
441
+                        if(lmbreleased) {
442
+                                menubar->menudata[i]->flag_open=0;
443
+                                menubar->menudata[i]->flag_stickyopen=0;
444
+                        }
445
+                        menubar->menudata[i]->currentoption=-1;
446
+                } else if(lmbreleased && currentoption!=-1) {
447
+                        for(j=0;j<menubar->sizemenudata;j++) {
448
+                                menubar->menudata[j]->flag_open=0;
449
+                                menubar->menudata[j]->flag_stickyopen=0;
450
+                                menubar->menudata[j]->currentoption=-1;
451
+                        }
452
+                        /* has selected this submenu */
453
+                        *click_avail=0;
454
+                        *sel_menu=menubar->menudata[i]->title;
455
+                        *sel_submenu=menubar->menudata[i]->options[currentoption];
456
+                } else if(lmbdown==0) {
457
+                        menubar->menudata[i]->flag_open=0;
458
+                        menubar->menudata[i]->flag_stickyopen=0;
459
+                        menubar->menudata[i]->currentoption=-1;
460
+                }
461
+        }
462
+        if(flag_outsideall) {
463
+                for(j=0;j<menubar->sizemenudata;j++) {
464
+                        menubar->menudata[j]->currentoption=-1;
465
+                }
466
+        }
467
+        /* update click_avail */
468
+        for(j=0;j<menubar->sizemenudata;j++) {
469
+                if(menubar->menudata[j]->flag_open || menubar->menudata[j]->flag_stickyopen) {
470
+                        *click_avail=0;
471
+                        break;
472
+                }
473
+        }
474
+        return(0);
475
+}
476
+
477
+int
478
+rayuimenubar_draw(menubar_t *menubar, int windowwidth, int windowheight, int *needs_nextredraw)
479
+{
480
+        int i,j,k,x;
481
+        menudata_t *menudata;
482
+        font_t *font;
483
+        if(menubar==NULL)
484
+                return(-1); /* sanity check failed */
485
+        font=menubar->ptrfont;
486
+        DrawRectangle(0,0,windowwidth, font->height+font->height/2, (Color){ 235, 235, 235, 235 } );
487
+        for(i=0,x=0;i<menubar->sizemenudata;i++) {
488
+                Vector2 v2;
489
+                menudata=menubar->menudata[i];
490
+                v2=MeasureTextEx(font->font,menudata->title,font->height,0);
491
+                FILLWHXY(menudata->whxy,((int)v2.x)+font->height,font->height+font->height/2,x,0);
492
+                v2.x=(float) (menudata->whxy.x+font->height/2);
493
+                v2.y=(float) (menudata->whxy.y+font->height/4);
494
+                DrawTextEx(font->font
495
+                  ,menudata->title
496
+                  ,v2
497
+                  ,font->height
498
+                  ,0
499
+                  ,(Color){ 45, 45, 45, 255 }
500
+                );
501
+                if(menudata->flag_open || menudata->flag_stickyopen) {
502
+                        int underline_height=3;
503
+                        int maxw;
504
+                        DrawRectangle(menudata->whxy.x,menudata->whxy.y+menudata->whxy.h-underline_height,menudata->whxy.w,underline_height, (Color){ 53,132,228,255 } );
505
+                        for(j=0,maxw=0;j<menudata->sizeoptions;j++) {
506
+                                v2=MeasureTextEx(font->font,menudata->options[j],font->height,0);
507
+                                maxw=(((int)(v2.x))>maxw)?((int)(v2.x)):maxw;
508
+                        }
509
+                        maxw=(maxw<(menudata->whxy.w+font->height))?(menudata->whxy.w+font->height):maxw;
510
+                        maxw+=font->height;
511
+                        FILLWHXY(menudata->optionswhxy,maxw,(font->height+font->height/2)*menudata->sizeoptions,menudata->whxy.x+1,menudata->whxy.y+menudata->whxy.h+2);
512
+                        DrawLine(menudata->optionswhxy.x-1,menudata->optionswhxy.y-2,menudata->optionswhxy.x+menudata->optionswhxy.w+2,menudata->optionswhxy.y-2,(Color){ 255,255,255,255 } );
513
+                        DrawLine(menudata->optionswhxy.x-1,menudata->optionswhxy.y,menudata->optionswhxy.x-1,menudata->optionswhxy.y+menudata->optionswhxy.h+1,(Color){ 255,255,255,255 } );
514
+                        DrawLine(menudata->optionswhxy.x+menudata->optionswhxy.w+2,menudata->optionswhxy.y,menudata->optionswhxy.x+menudata->optionswhxy.w+2,menudata->optionswhxy.y+menudata->optionswhxy.h+1,(Color){ 192,192,192,255 } );
515
+                        DrawLine(menudata->optionswhxy.x-1,menudata->optionswhxy.y+menudata->optionswhxy.h+1,menudata->optionswhxy.x+menudata->optionswhxy.w+2,menudata->optionswhxy.y+menudata->optionswhxy.h+1,(Color){ 192,192,192,255 } );
516
+                        DrawRectangle(menudata->optionswhxy.x,menudata->optionswhxy.y,menudata->optionswhxy.w,menudata->optionswhxy.h,(Color){ 235, 235, 235, 235 });
517
+                        for(k=0;k<menudata->sizeoptions;k++) {
518
+                                Color c;
519
+                                c=(k==menudata->currentoption)?((Color){ 255,255,255,255 }):((Color){ 45, 45, 45, 255 });
520
+                                if(k==menudata->currentoption)
521
+                                        DrawRectangle(menudata->optionswhxy.x+1,menudata->optionswhxy.y+(font->height+(font->height/2))*k,menudata->optionswhxy.w-2,font->height+font->height/2,(Color){ 53,132,228,255 });
522
+                                v2.x=(float) (menudata->optionswhxy.x+font->height/2);
523
+                                v2.y=(float) (menudata->optionswhxy.y+(font->height/4)+(font->height+(font->height/2))*k);
524
+                                DrawTextEx(font->font
525
+                                  ,menudata->options[k]
526
+                                  ,v2
527
+                                  ,font->height
528
+                                  ,0
529
+                                  ,c
530
+                                );
531
+                        }
532
+                } else {
533
+                        FILLWHXY(menudata->optionswhxy,0,0,0,0);
534
+                }
535
+                x=menudata->whxy.x+menudata->whxy.w;
536
+        }
537
+        return(0);
538
+}
539
+
540
+static int *
541
+intglobal_getcodepoints(int *sizecodepoints)
542
+{
543
+        static int codepoints[]={
544
+/* Basic Latin */
545
+32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
546
+/* Latin-1 Supplement */
547
+160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,
548
+/* down arrow (U+2186 -- this font doesn't have arrows, this is old roman numeral 50 */
549
+(0x21<<8)|0x86,
550
+};
551
+        if(sizecodepoints!=NULL)
552
+                *sizecodepoints=(int) (sizeof(codepoints)/sizeof(codepoints[0]));
553
+        return(codepoints);
554
+}
555
+
556
+int
557
+intglobal_menu_count(char *menus)
558
+{
559
+        int n;
560
+        char *ptr,*next;
561
+        if(menus==NULL)
562
+                return(0); /* sanity check error */
563
+        for(ptr=menus,n=0;(next=strchr(ptr,'\n'))!=NULL;ptr=next+1) {
564
+                if(next[1]=='\n') {
565
+                        next++;
566
+                        n++;
567
+                }
568
+        }
569
+        return(n);
570
+}
571
+
572
+char *
573
+intglobal_menu_get(char *menus, int targetn, int *len)
574
+{
575
+        int n;
576
+        char *ptr,*next,*end;
577
+        int is_title;
578
+        if(len!=NULL)
579
+                *len=0;
580
+        if(menus==NULL || targetn<0)
581
+                return(NULL); /* sanity check error */
582
+        end=menus+strlen(menus);
583
+        for(ptr=menus,is_title=1,n=0;(next=strchr(ptr,'\n'))!=NULL;ptr=next+1) {
584
+                if(targetn==n && is_title==1) {
585
+                        if(len!=NULL)
586
+                                *len=next-ptr;
587
+                        return(ptr); /* found, return start of menu string */
588
+                }
589
+                is_title=0;
590
+                if(next[1]=='\n') {
591
+                        n++;
592
+                        next++;
593
+                        is_title=1;
594
+                }
595
+        }
596
+        return(end); /* not found, return end of menus */
597
+}
598
+
599
+int
600
+intglobal_submenu_count(char *menus)
601
+{
602
+        int subn;
603
+        char *ptr,*next;
604
+        if(menus==NULL)
605
+                return(0); /* sanity check error */
606
+        next=strchr(menus,'\n');
607
+        if(next==NULL)
608
+                return(0); /* no title */
609
+        for(subn=0,ptr=next+1;(next=strchr(ptr,'\n'))!=NULL;ptr=next+1) {
610
+                subn++;
611
+                if(next[1]=='\n')
612
+                        break;
613
+        }
614
+        return(subn);
615
+}
616
+
617
+char *
618
+intglobal_submenu_get(char *menus, int targetsubn, int *len)
619
+{
620
+        char *ptr,*next,*end;
621
+        int subn;
622
+        if(len!=NULL)
623
+                *len=0;
624
+        if(menus==NULL || targetsubn<0)
625
+                return(NULL); /* sanity check error */
626
+        end=menus+strlen(menus);
627
+        next=strchr(menus,'\n');
628
+        if(next==NULL)
629
+                return(end); /* no title */
630
+        for(ptr=next+1,subn=0;(next=strchr(ptr,'\n'))!=NULL;ptr=next+1,subn++) {
631
+                if(targetsubn==subn) {
632
+                        if(len!=NULL)
633
+                                *len=next-ptr;
634
+                        return(ptr);
635
+                }
636
+                if(next[1]=='\n')
637
+                        break; /* "\n\n" marks the end of submenus */
638
+        }
639
+        return(end);
640
+}
641
+
642
+char *
643
+intglobal_strduplen(char *str, int len)
644
+{
645
+        char *res;
646
+        if(len<0 || (str==NULL && len!=0))
647
+                return(NULL);
648
+        if((res=malloc(len+1))==NULL)
649
+                return(NULL);
650
+        memcpy(res,str,len);
651
+        res[len]='\0';
652
+        return(res);
653
+}
654
+
655
+int
656
+intglobal_menudata_pos2option(menudata_t *menudata, Vector2 pos)
657
+{
658
+        int n,h;
659
+        if(menudata==NULL
660
+          || (menudata->flag_open==0 && menudata->flag_stickyopen==0)
661
+          || is_global_insidewhxy(pos, &(menudata->optionswhxy),0)==0
662
+        ) {
663
+                return(-1);
664
+        }
665
+        h=(menudata->sizeoptions==0)?0:menudata->optionswhxy.h/menudata->sizeoptions;
666
+        n=(((int)pos.y)-menudata->optionswhxy.y)/h;
667
+        return(n);
668
+}
669
+
670
+
671
+static char *
672
+intglobal_messageboxcaption(char *newcaption)
673
+{
674
+        static char caption[1024]={"Message"};
675
+        if(newcaption!=NULL) {
676
+                strncpy(caption,newcaption,sizeof(caption));
677
+                caption[sizeof(caption)-1]='\0';
678
+        }
679
+        return(caption);
680
+}
681
+
682
+
683
+#if !defined(__linux__) && !defined(ANDROID)
684
+int MessageBoxA(void *hWnd,void *lpText,void *lpCaption,unsigned int uType);
685
+void
686
+global_messagebox(char *format, ...)
687
+{
688
+        char msgbuf[4096];
689
+        va_list va;
690
+        va_start(va,format);
691
+        vsnprintf(msgbuf,sizeof(msgbuf),format,va);
692
+        msgbuf[sizeof(msgbuf)-1]='\0';
693
+        va_end(va);
694
+        MessageBoxA(NULL,msgbuf,intglobal_messageboxcaption(NULL),0);
695
+        return;
696
+}
697
+#else
698
+void
699
+global_messagebox(char *format, ...)
700
+{
701
+        va_list va;
702
+        va_start(va,format);
703
+        vfprintf(stderr,format,va);
704
+        fprintf(stderr,"\n");
705
+        va_end(va);
706
+        return;
707
+}
708
+#endif
709
+
710
+#ifdef ANDROID
711
+Image
712
+global_loadimage(const char *filename)
713
+{
714
+        unsigned char *filedata=NULL;
715
+        FILE *f=NULL;
716
+        struct stat st;
717
+        Image img;
718
+        char *ext;
719
+        if((f=fopen(filename,"r"))==NULL
720
+          || fstat(fileno(f),&st)!=0
721
+          || st.st_size<=0
722
+          || (filedata=(unsigned char *)malloc(st.st_size))==NULL
723
+          || fread(filedata,1,st.st_size,f)!=st.st_size
724
+        ) {
725
+                if(f!=NULL)
726
+                        fclose(f),f=NULL;
727
+                if(filedata!=NULL)
728
+                        free(filedata),filedata=NULL;
729
+                return((Image){0});
730
+        }
731
+        fclose(f);
732
+        ext=strchr(filename,'.');
733
+        ext=(ext==NULL)?filename+strlen(filename):ext;
734
+        img=LoadImageFromMemory(ext,filedata,st.st_size);
735
+        free(filedata),filedata=NULL;
736
+        return(img);
737
+}
738
+#else
739
+Image
740
+global_loadimage(const char *filename)
741
+{
742
+        return(LoadImage(filename));
743
+}
744
+#endif
745
+
746
+int
747
+is_global_insidewhxy(Vector2 pos, whxy_t *whxy, int margin)
748
+{
749
+        if(whxy==NULL)
750
+                return(0); /* sanity check error */
751
+        if(pos.x>=(float)(whxy->x-margin)
752
+          && pos.x<=(float)(whxy->x+whxy->w+margin)
753
+          && pos.y>=(float)(whxy->y-margin)
754
+          && pos.y<=(float)(whxy->y+whxy->h+margin)
755
+        ) {
756
+                return(1);
757
+        }
758
+        return(0);
759
+}
760
+