/* * boxui.c * * Multiplatform UI library for SDL2. * * (c) 2023 Dario Rodriguez <antartica@box-ui.org> * Licensed under the terms of the GNU GPL v3. */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <time.h> #include <signal.h> #include "SDL.h" #include "SDL_rwops.h" #include "SDL_ttf.h" #include "boxui.h" #include "notosans_regular_ttf.c" #define DEFAULT_FPS 30 typedef struct intboxui_t { char *title; int defaultw,defaulth; int fps; long long fpsepoch; long long fpsepochframeno; void *userptr; int isdirty; long long lastrenderedframeno; int sizeafterbuf; int usedafterbuf; char *afterbuf; /* temporary members (to be able to test) */ int screenw; int screenh; SDL_Window *win; SDL_Renderer *renderer; SDL_RWops *defaultfontdata; TTF_Font *defaultfont; int defaultfontheight; int defaultfontwidth; char currenttext[1024]; char currentclickevent[1024]; char currentactionstr[1024]; int flag_currentactionstrused; /* end of temporary members (to be able to test) */ } intboxui_t; int intboxui_waittick(intboxui_t *boxui); int intboxui_render(intboxui_t *boxui); int intboxui_after_doexpired(intboxui_t *boxui); boxui_t * boxui_init(char *title,int defaultw, int defaulth, int flags) { intboxui_t *boxui; title=(title==NULL)?"":title; if((boxui=malloc(sizeof(intboxui_t)))==NULL) return(NULL); /* insuf. mem for boxui struct */ memset(boxui,0,sizeof(intboxui_t)); if((boxui->title=strdup(title))==NULL) { boxui_free((boxui_t *)boxui),boxui=NULL; return(NULL); /* insuf. mem for title string */ } /* temporary initialization (to be able to test) */ if(SDL_Init(SDL_INIT_VIDEO)<0 || (boxui->win=SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED, defaultw,defaulth,0))==NULL || (boxui->renderer=SDL_CreateRenderer(boxui->win,-1, SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC))==NULL) { boxui_free(boxui),boxui=NULL; return(NULL); /* ERROR: Couldn't init video */ } boxui->screenw=defaultw,boxui->screenh=defaulth; if((boxui->defaultfontdata=SDL_RWFromConstMem(notosans_regular_ttf,notosans_regular_ttf_len))==NULL || TTF_Init()==-1) { boxui_free(boxui),boxui=NULL; return(NULL); /* ERROR: Couldn't init font data */ } SDL_RWseek(boxui->defaultfontdata,0,RW_SEEK_SET); if(boxui->defaultfontdata==NULL || (boxui->defaultfont=TTF_OpenFontRW(boxui->defaultfontdata,0,/* default font size */ 16))==NULL) { boxui_free(boxui),boxui=NULL; return(NULL); /* ERROR: Couldn't init defaultfont */ } /* font width and height */ { SDL_Surface *s; SDL_Color c={0,0,0,0}; /* width */ if((s=TTF_RenderUTF8_Blended(boxui->defaultfont,"m",c))==NULL) { boxui_free(boxui),boxui=NULL; return(NULL); /* ERROR: Couldn't query font width */ } boxui->defaultfontwidth=s->w; SDL_FreeSurface(s),s=NULL; /* height */ if((s=TTF_RenderUTF8_Blended(boxui->defaultfont,"gjpqy_",c))==NULL) { boxui_free(boxui),boxui=NULL; return(NULL); /* ERROR: Couldn't query font height */ } boxui->defaultfontheight=s->h; SDL_FreeSurface(s),s=NULL; } /* end of temporary initialization (to be able to test) */ /* return the struct */ return((boxui_t *)boxui); } void boxui_free(boxui_t *paramboxui) { intboxui_t *boxui=(intboxui_t *)paramboxui; if(boxui==NULL) return; /* sanity check failed */ if(boxui->title!=NULL) free(boxui->title),boxui->title=NULL; /* temporary initialization freeing */ if(boxui->defaultfont!=NULL) TTF_CloseFont(boxui->defaultfont),boxui->defaultfont=NULL; if(TTF_WasInit()) TTF_Quit(); if(boxui->defaultfontdata!=NULL) SDL_FreeRW(boxui->defaultfontdata),boxui->defaultfontdata=NULL; if(boxui->renderer!=NULL) SDL_DestroyRenderer(boxui->renderer),boxui->renderer=NULL; if(boxui->win!=NULL) SDL_DestroyWindow(boxui->win),boxui->win=NULL; SDL_Quit(); /* end of temporary initialization freeing */ free(boxui),boxui=NULL,paramboxui=NULL; return; } int boxui_setuserptr(boxui_t *paramboxui, void *userptr) { intboxui_t *boxui=(intboxui_t *)paramboxui; if(boxui==NULL) return(-1); /* sanity check failed */ boxui->userptr=userptr; return(0); } void * boxui_getuserptr(boxui_t *paramboxui) { intboxui_t *boxui=(intboxui_t *)paramboxui; if(boxui==NULL) return(NULL); /* sanity check failed */ return(boxui->userptr); } int boxui_setfps(boxui_t *paramboxui, int fps) { intboxui_t *boxui=(intboxui_t *)paramboxui; if(boxui==NULL || fps<1) return(-1); /* sanity check failed */ boxui->fps=fps; #if SDL_VERSION_ATLEAST(2,0,18) boxui->fpsepoch=(long long)SDL_GetTicks64(); #else boxui->fpsepoch=(long long)SDL_GetTicks(); #endif boxui->fpsepochframeno=boxui->lastrenderedframeno; return(0); } int boxui_getfps(boxui_t *paramboxui) { intboxui_t *boxui=(intboxui_t *)paramboxui; if(boxui==NULL) return(DEFAULT_FPS); /* sanity check failed */ return((boxui->fps==0)?DEFAULT_FPS:boxui->fps); } int boxui_tick(boxui_t *paramboxui) { intboxui_t *boxui=(intboxui_t *)paramboxui; if(boxui==NULL) { SDL_Delay(1); return(-1); /* sanity check failed */ } if(boxui->isdirty) { boxui_ghostrender((boxui_t *)boxui); intboxui_waittick(boxui); intboxui_render(boxui); } else { intboxui_waittick(boxui); } boxui->lastrenderedframeno++; return(0); } int boxui_ghostrender(boxui_t *boxui) { #warning TODO return(-1); } int boxui_refreshevents(boxui_t *paramboxui) { intboxui_t *boxui=(intboxui_t *)paramboxui; if(boxui==NULL) return(-1); /* sanity check failed */ SDL_PumpEvents(); return(0); } int boxui_getevent(boxui_t *paramboxui, SDL_Event *event) { intboxui_t *boxui=(intboxui_t *)paramboxui; if(boxui==NULL || event==NULL) return(-1); /* sanity check failed */ while(SDL_PeepEvents(event,1,SDL_GETEVENT,SDL_FIRSTEVENT,SDL_LASTEVENT)>0) { #if 1 /* test */ if(event->type==SDL_MOUSEBUTTONUP) { if(boxui->currentclickevent[0]!='\0') { boxui_putactionstr((boxui_t *)boxui,boxui->currentclickevent,NULL); continue; /* we have used the event */ } } /* end of test */ #endif return(0); /* event found and put into "event" */ } return(-1); /* no events remaining */ } int boxui_event_isquit(boxui_t *paramboxui, SDL_Event *event) { intboxui_t *boxui=(intboxui_t *)paramboxui; if(boxui==NULL || event==NULL) return(0); /* sanity check failed */ if(event->type==SDL_WINDOWEVENT && event->window.event==SDL_WINDOWEVENT_CLOSE) return(1); return(0); } int boxui_event_iskeydown(boxui_t *paramboxui, SDL_Event *event, int *keycode) { intboxui_t *boxui=(intboxui_t *)paramboxui; if(boxui==NULL || event==NULL) return(0); /* sanity check failed */ if(event->type==SDL_KEYDOWN) { if(keycode!=NULL) *keycode=event->key.keysym.sym; return(1); } return(0); } int boxui_event_isresize(boxui_t *paramboxui, SDL_Event *event, int *neww, int *newh) { intboxui_t *boxui=(intboxui_t *)paramboxui; if(boxui==NULL || event==NULL) return(0); /* sanity check failed */ if(event->type==SDL_WINDOWEVENT_RESIZED || event->type==SDL_WINDOWEVENT_SIZE_CHANGED) { if(neww!=NULL) *neww=event->window.data1; if(newh!=NULL) *newh=event->window.data2; return(1); } return(0); } void boxui_resize(boxui_t *boxui, int neww, int newh) { #warning TODO return; } int boxui_putactionstr(boxui_t *paramboxui, char *firstelem, ...) { intboxui_t *boxui=(intboxui_t *)paramboxui; if(boxui==NULL || firstelem==NULL) return(-1); /* sanity check failed */ /* test */ strncpy(boxui->currentactionstr,firstelem,sizeof(boxui->currentactionstr)); boxui->currentactionstr[sizeof(boxui->currentactionstr)-1]='\0'; boxui->flag_currentactionstrused=0; /* end of test */ #warning TODO return(-1); } char * boxui_getactionstr(boxui_t *paramboxui, char **secondelemtoend) { intboxui_t *boxui=(intboxui_t *)paramboxui; if(boxui==NULL) return(NULL); /* sanity check failed */ /* test */ if(boxui->flag_currentactionstrused || boxui->currentactionstr[0]=='\0') return(NULL); boxui->flag_currentactionstrused=1; return(boxui->currentactionstr); /* end of test */ #warning TODO return(NULL); } char * boxui_add(boxui_t *paramboxui, char *filedebug, int linedebug, char *classwiname, char *format, ...) { intboxui_t *boxui=(intboxui_t *)paramboxui; if(boxui==NULL || classwiname==NULL || format==NULL) return(NULL); /* sanity check failed */ /* test */ if(strchr(format,'%')==NULL) boxui_config(boxui,NULL,-1,classwiname,format); /* end of test */ #warning TODO return(NULL); } char * boxui_config(boxui_t *paramboxui, char *filedebug, int linedebug, char *winame, char *format, ...) { intboxui_t *boxui=(intboxui_t *)paramboxui; if(boxui==NULL || winame==NULL || format==NULL) return(NULL); /* sanity check failed */ /* test */ { char *start,*end; int l; /* text */ if((start=strchr(format,'\"'))!=NULL && (start=start+1)!=NULL && (end=strchr(start,'\"'))!=NULL) { l=end-start; l=(l>sizeof(boxui->currenttext))?sizeof(boxui->currenttext)-1:l; memcpy(boxui->currenttext,start,l); boxui->currenttext[l]='\0'; boxui->isdirty=1; } /* click */ for(start=strchr(format,'-');start!=NULL;start=strchr(start+1,'-')) { if(memcmp(start,"-click ",7)==0) { start+=7; end=strchr(start,' '); end=(end==NULL)?start+strlen(start):end; l=end-start; l=(l>sizeof(boxui->currentclickevent))?sizeof(boxui->currentclickevent)-1:l; memcpy(boxui->currentclickevent,start,l); boxui->currentclickevent[l]='\0'; } } } /* end of test */ #warning TODO return(NULL); } char * boxui_pack(boxui_t *boxui, char *filedebug, int linedebug, char *winame, char *format, ...) { #warning TODO return(NULL); } int intboxui_waittick(intboxui_t *paramboxui) { long long delay; intboxui_t *boxui=(intboxui_t *)paramboxui; if(boxui==NULL) { SDL_Delay(1); return(-1); /* sanity check failed */ } #if SDL_VERSION_ATLEAST(2,0,18) delay=((long long)SDL_GetTicks64()); #else delay=((long long)SDL_GetTicks()); #endif delay-=boxui->fpsepoch; delay=(((boxui->lastrenderedframeno-boxui->fpsepochframeno)+1)*1000/((boxui->fps==0)?DEFAULT_FPS:boxui->fps))-delay; if(delay>0) { SDL_Delay(delay); } else if(delay<-1000) { /* reset epoch if unsyncronized by more than 1s */ #if SDL_VERSION_ATLEAST(2,0,18) boxui->fpsepoch=((long long)SDL_GetTicks64()); #else boxui->fpsepoch=((long long)SDL_GetTicks()); #endif boxui->fpsepochframeno=boxui->lastrenderedframeno; } intboxui_after_doexpired(boxui); return(0); } int intboxui_render(intboxui_t *paramboxui) { intboxui_t *boxui=(intboxui_t *)paramboxui; if(boxui==NULL) return(-1); /* sanity check failed */ /* test */ SDL_SetRenderDrawColor(boxui->renderer,0xff,0xff,0xff,0xff); SDL_RenderClear(boxui->renderer); if(boxui->currenttext[0]!='\0') { SDL_Surface *fgsurface=NULL; SDL_Texture *fg=NULL; SDL_Rect dstrect; SDL_Color c={0x00,0x00,0x00,0xff}; if((fgsurface=TTF_RenderUTF8_Blended(boxui->defaultfont,boxui->currenttext,c))!=NULL && (fg=SDL_CreateTextureFromSurface(boxui->renderer,fgsurface))!=NULL) { dstrect.x=(boxui->screenw-fgsurface->w)/2; dstrect.y=(boxui->screenh-fgsurface->h)/2; dstrect.w=fgsurface->w,dstrect.h=fgsurface->h; SDL_RenderCopy(boxui->renderer,fg,NULL,&dstrect); } if(fgsurface!=NULL) SDL_FreeSurface(fgsurface),fgsurface=NULL; if(fg!=NULL) SDL_DestroyTexture(fg),fg=NULL; } SDL_RenderPresent(boxui->renderer); boxui->isdirty=0; /* end of test */ #warning TODO return(0); } int intboxui_after_doexpired(intboxui_t *boxui) { #warning TODO return(0); }