/* * re_ui.c * * A programmers editor * * Simple UI in SDL2 * * Author: Dario Rodriguez antartica@whereismybit.com * This program is licensed under the terms of GNU GPL v2.1+ */ #include #include #include #include #include #include "re_ui.h" #include "hack_regular.h" #include "iosevka_fixed_regular.h" #define DEFAULTTITLEPREFIX "re - " #define DEFAULTWINDOWWIDTH 80 #define DEFAULTWINDOWHEIGHT 66 #define RECTFILL(r,rx,ry,rw,rh) (r).x=(rx),(r).y=(ry),(r).w=(rw),(r).h=(rh) #if SDL_BYTEORDER==SDL_BIG_ENDIAN #define CHARRGBA2UINT(rgba) ((((Uint32)(((unsigned char *)rgba)[0]))<<24) \ |(((Uint32)(((unsigned char *)rgba)[1]))<<16) \ |(((Uint32)(((unsigned char *)rgba)[2]))<<8) \ |((Uint32)(((unsigned char *)rgba)[3]))) #define CHARRGBA2TEXARGB8888(rgba) ((((Uint32)(((unsigned char *)rgba)[2]))<<24) \ |(((Uint32)(((unsigned char *)rgba)[1]))<<16) \ |(((Uint32)(((unsigned char *)rgba)[0]))<<8) \ |((Uint32)(((unsigned char *)rgba)[3]))) #else #define CHARRGBA2UINT(rgba) ((((Uint32)(((unsigned char *)rgba)[3]))<<24) \ |(((Uint32)(((unsigned char *)rgba)[2]))<<16) \ |(((Uint32)(((unsigned char *)rgba)[1]))<<8) \ |((Uint32)(((unsigned char *)rgba)[0]))) #define CHARRGBA2TEXARGB8888(rgba) ((((Uint32)(((unsigned char *)rgba)[3]))<<24) \ |(((Uint32)(((unsigned char *)rgba)[0]))<<16) \ |(((Uint32)(((unsigned char *)rgba)[1]))<<8) \ |((Uint32)(((unsigned char *)rgba)[2]))) #endif static int intreui_fillroundedr(SDL_Surface *dst, int xo, int yo, int w, int h, int r, const char *rgba); static int intreui_triangle(SDL_Surface *dst, int x, int y, int w, int h, char direction, const char *rgba); reui_t * reui_init(int fontheight, reui_t *parent) { reui_t *ui; SDL_Rect screenrect; if(fontheight<=0) return(NULL); /* sanity check failed */ if((ui=malloc(sizeof(reui_t)))==NULL) return(NULL); memset(ui,0,sizeof(reui_t)); /* masks */ #if SDL_BYTEORDER==SDL_BIG_ENDIAN ui->rmask=0xff000000; ui->gmask=0x00ff0000; ui->bmask=0x0000ff00; ui->amask=0x000000ff; #else ui->rmask=0x000000ff; ui->gmask=0x0000ff00; ui->bmask=0x00ff0000; ui->amask=0xff000000; #endif /* video */ if(parent==NULL) { if(SDL_Init(SDL_INIT_VIDEO)<0 || SDL_GetDisplayBounds(0,&screenrect)!=0) { reui_free(ui),ui=NULL; return(NULL); } } else { ui->parent=(void *)parent; screenrect.w=parent->screenw; screenrect.h=parent->screenh; } ui->screenw=screenrect.w; ui->screenh=screenrect.h; /* font */ ui->fontheight=fontheight; /* placeholder, will fill later with real value */ if(parent==NULL) ui->fontdata=SDL_RWFromConstMem(hack_regular,SIZE_HACK_REGULAR); else ui->fontdata=SDL_RWFromConstMem(iosevka_fixed_regular,SIZE_IOSEVKA_FIXED_REGULAR); SDL_RWseek(ui->fontdata,0,RW_SEEK_SET); if(ui->fontdata==NULL || TTF_Init()==-1 || (ui->font=TTF_OpenFontRW(ui->fontdata,0,ui->fontheight))==NULL) { reui_free(ui),ui=NULL; return(NULL); } /* font width */ { SDL_Surface *s; SDL_Color c={0,0,0,0}; if((s=TTF_RenderUTF8_Blended(ui->font,"m",c))==NULL) { reui_free(ui),ui=NULL; return(NULL); } ui->fontwidth=s->w; SDL_FreeSurface(s),s=NULL; } /* font height */ { SDL_Surface *s; SDL_Color c={0,0,0,0}; if((s=TTF_RenderUTF8_Blended(ui->font,"gjpqy_",c))==NULL) { reui_free(ui),ui=NULL; return(NULL); } ui->fontheight=s->h; SDL_FreeSurface(s),s=NULL; } /* main window */ ui->w=ui->fontwidth*DEFAULTWINDOWWIDTH; ui->w=(ui->w>ui->screenw)?ui->screenw:ui->w; ui->h=ui->fontheight*DEFAULTWINDOWHEIGHT; ui->h=(ui->h>ui->screenh)?ui->screenh:ui->h; if((ui->win=SDL_CreateWindow( DEFAULTTITLEPREFIX, SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED, ui->w,ui->h,SDL_WINDOW_RESIZABLE))==NULL || (ui->renderer=SDL_CreateRenderer(ui->win,-1, SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC))==NULL || (ui->scr=SDL_CreateTexture(ui->renderer,SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING,ui->w,ui->h))==NULL || (ui->onepx=SDL_CreateTexture(ui->renderer,SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC,1,1))==NULL) { reui_free(ui),ui=NULL; return(NULL); } /* finished */ ui->scrdirty=1; ui->rendererdirty=1; return(ui); } void reui_free(reui_t *ui) { if(ui==NULL) return; if(ui->onepx!=NULL) SDL_DestroyTexture(ui->onepx),ui->onepx=NULL; if(ui->font!=NULL) TTF_CloseFont(ui->font),ui->font=NULL; if(ui->parent==NULL && TTF_WasInit()) TTF_Quit(); if(ui->fontdata!=NULL) SDL_FreeRW(ui->fontdata),ui->fontdata=NULL; if(ui->scr!=NULL) SDL_DestroyTexture(ui->scr),ui->scr=NULL; if(ui->renderer!=NULL) SDL_DestroyRenderer(ui->renderer),ui->renderer=NULL; if(ui->win!=NULL) SDL_DestroyWindow(ui->win),ui->win=NULL; if(ui->parent==NULL) SDL_Quit(); free(ui),ui=NULL; } int reui_title(reui_t *ui, char *titlefilename) { char buf[1024]; if(ui==NULL || titlefilename==NULL) return(-1); snprintf(buf,sizeof(buf),"%s%s",DEFAULTTITLEPREFIX,titlefilename); buf[sizeof(buf)-1]='\0'; SDL_SetWindowTitle(ui->win,buf); return(0); } int reui_resize(reui_t *ui, int w, int h) { SDL_Texture *newscr; if(ui==NULL || w<=0 || h<=0) return(-1); if(ui->w==w && ui->h==h) return(0); /* nothing to do */ if((newscr=SDL_CreateTexture(ui->renderer,SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING,w,h))==NULL) return(-1); /* couldn't create texture */ if(ui->scr!=NULL) SDL_DestroyTexture(ui->scr),ui->scr=NULL; ui->scr=newscr; ui->h=h; ui->w=w; return(0); } int reui_setfontheight(reui_t *ui, int fontheight) { TTF_Font *newfont; SDL_Surface *s,*s2; SDL_Color c={0,0,0,0}; if(ui==NULL || fontheight<=0) return(-1); /* sanity check failed */ SDL_RWseek(ui->fontdata,0,RW_SEEK_SET); if((newfont=TTF_OpenFontRW(ui->fontdata,0,fontheight))==NULL) return(-1); /* couln't setup new font */ if((s=TTF_RenderUTF8_Blended(newfont,"m",c))==NULL || (s2=TTF_RenderUTF8_Blended(newfont,"gjpqy_",c))==NULL) { if(s!=NULL) SDL_FreeSurface(s),s=NULL; TTF_CloseFont(newfont),newfont=NULL; return(-1); /* couldn't use new font */ } if(ui->font!=NULL) TTF_CloseFont(ui->font),ui->font=NULL; ui->font=newfont; ui->fontheight=s2->h; ui->fontwidth=s->w; SDL_FreeSurface(s),s=NULL; SDL_FreeSurface(s2),s2=NULL; return(0); } int reui_fill(reui_t *ui, int x, int y, int w, int h, const char *rgba) { SDL_Rect srcrect; SDL_Rect dstrect; Uint32 pixels[1]; if(ui==NULL || rgba==NULL) return(-1); pixels[0]=CHARRGBA2TEXARGB8888(rgba); SDL_UpdateTexture(ui->onepx,NULL,pixels,sizeof(Uint32)); RECTFILL(srcrect,0,0,1,1); RECTFILL(dstrect,x,y,w,h); SDL_RenderCopy(ui->renderer,ui->onepx,&srcrect,&dstrect); ui->rendererdirty=1; return(0); } int reui_fillrounded(reui_t *ui, int x, int y, int w, int h, const char *rgba) { static int rounded5[]={5,3,2,1,1}; static int rounded4[]={4,2,1,1}; int i; if(ui==NULL || rgba==NULL) return(-1); if(w<2 || h<2) return(reui_fill(ui,x,y,w,h,rgba)); if(w<8 || h<8 || ui->fontheight<8 || ui->fontwidth<8) { reui_fill(ui,x+1,y,w-2,1,rgba); reui_fill(ui,x,y+1,w,h-2,rgba); reui_fill(ui,x+1,y+h-1,w-2,1,rgba); } else if(w<10 || h<10 || ui->fontheight<10 || ui->fontwidth<10) { reui_fill(ui,x,y+4,w,h-8,rgba); for(i=0;i<(sizeof(rounded4)/sizeof(rounded4[0]));i++) { reui_fill(ui,x+rounded4[i],i,w-(rounded4[i]<<1),1,rgba); reui_fill(ui,x+rounded4[i],y+h-1-i,w-(rounded4[i]<<1),1,rgba); } } else { reui_fill(ui,x,y+5,w,h-10,rgba); for(i=0;i<(sizeof(rounded5)/sizeof(rounded5[0]));i++) { reui_fill(ui,x+rounded5[i],i,w-(rounded5[i]<<1),1,rgba); reui_fill(ui,x+rounded5[i],y+h-1-i,w-(rounded5[i]<<1),1,rgba); } } return(0); } int reui_fillblended(reui_t *ui, int x, int y, int w, int h, const char *rgba) { SDL_Surface *bgsurface; SDL_Texture *tex; SDL_Rect dstrect; Uint32 color; if(ui==NULL || rgba==NULL) return(-1); if((bgsurface=SDL_CreateRGBSurface(0 ,1 ,1 ,32, ui->rmask, ui->gmask, ui->bmask, ui->amask))==NULL) { return(-1); } color=CHARRGBA2UINT(rgba); RECTFILL(dstrect,0,0,1,1); SDL_FillRect(bgsurface,&dstrect,color); if((tex=SDL_CreateTextureFromSurface(ui->renderer,bgsurface))==NULL) { SDL_FreeSurface(bgsurface),bgsurface=NULL; return(-1); } RECTFILL(dstrect,x,y,w,h); SDL_RenderCopy(ui->renderer,tex,NULL,&dstrect); ui->rendererdirty=1; SDL_FreeSurface(bgsurface),bgsurface=NULL; SDL_DestroyTexture(tex),tex=NULL; return(0); } int reui_scr2renderer(reui_t *ui, int x, int y, int w, int h) { SDL_Rect srcrect,dstrect; if(ui==NULL) return(-1); RECTFILL(srcrect,x,y,w,h); RECTFILL(dstrect,x,y,w,h); SDL_RenderCopy(ui->renderer,ui->scr,&srcrect,&dstrect); ui->rendererdirty=1; return(0); } int reui_present(reui_t *ui) { if(ui==NULL) return(-1); SDL_RenderPresent(ui->renderer); ui->rendererdirty=0; return(0); } int reui_write(reui_t *ui, int x, int y, const char *rgba, const char *str, int nchar) { char buf[1024]; SDL_Surface *fgsurface; SDL_Texture *fg; SDL_Color c={((unsigned char *)rgba)[0], ((unsigned char *)rgba)[1], ((unsigned char *)rgba)[2], ((unsigned char *)rgba)[3]}; SDL_Rect dstrect; if(ncharfont,buf,c))==NULL || (fg=SDL_CreateTextureFromSurface(ui->renderer,fgsurface))==NULL) { if(fgsurface!=NULL) SDL_FreeSurface(fgsurface),fgsurface=NULL; return(-1); } RECTFILL(dstrect,x,y,fgsurface->w,fgsurface->h); SDL_RenderCopy(ui->renderer,fg,NULL,&dstrect); ui->rendererdirty=1; SDL_FreeSurface(fgsurface),fgsurface=NULL; SDL_DestroyTexture(fg),fg=NULL; return(0); } int reui_printf(reui_t *ui, int x, int y, const char *rgba, char *format, ...) { char buf[1024]; va_list l; va_start(l,format); vsnprintf(buf,sizeof(buf),format,l); va_end(l); buf[sizeof(buf)-1]='\0'; if(reui_write(ui,x,y,rgba,buf,strlen(buf))!=0) return(-1); return(0); } int reui_balloon(reui_t *ui, char direction, int x, int y, const char *rgbafg, const char *rgbabg, const char *str, int nchar) { char buf[1024]; SDL_Surface *bgsurface; SDL_Surface *fgsurface,*linefgsurface; SDL_Texture *tex; SDL_Color c={((unsigned char *)rgbafg)[0], ((unsigned char *)rgbafg)[1], ((unsigned char *)rgbafg)[2], ((unsigned char *)rgbafg)[3]}; SDL_Rect dstrect; int trianglew,triangleh; int marginw,marginh; int marginoffx,marginoffy; int radius; int offx,offy; int i,w,iw,maxw,lastspace,lastspacew,nlines; char *ptr,*end; if(ui==NULL || rgbafg==NULL || rgbabg==NULL || (str==NULL && nchar!=0)) return(-1); if(ncharfontwidth:0; if(buf[i]==' ') { lastspace=i; lastspacew=w; } if(w>(ui->w-ui->fontwidth*2)) { nlines++; if(lastspace==-1) { memmove(buf+i+1,buf+i,sizeof(buf)-i-1); buf[i]='\n'; buf[sizeof(buf)-1]='\0'; maxw=(w>maxw)?w:maxw; iw=w=0; continue; } else { buf[lastspace]='\n'; i=lastspace+1; w=lastspacew; maxw=(w>maxw)?w:maxw; iw=w=0; continue; } } } maxw=(w>maxw)?w:maxw; /* render fg */ if(strchr(buf,'\n')==NULL) { /* text without newlines */ if((fgsurface=TTF_RenderUTF8_Blended(ui->font,buf,c))==NULL) return(-1); } else { /* text with newlines */ if((fgsurface=SDL_CreateRGBSurface(0 ,maxw,nlines*ui->fontheight ,32, ui->rmask,ui->gmask, ui->bmask, ui->amask))==NULL) { return(-1); /* couldn't create fg surface */ } for(i=0,ptr=buf,end=strchr(ptr,'\n') ;ptr!=NULL ;ptr=((end!=NULL)?end+1:NULL),end=((ptr!=NULL)?strchr(ptr,'\n'):NULL),i++) { if(end!=NULL) *end='\0'; if(*ptr=='\0') continue; /* blank line */ if((linefgsurface=TTF_RenderUTF8_Blended(ui->font,ptr,c))==NULL) { SDL_FreeSurface(fgsurface),fgsurface=NULL; return(-1); /* couldn't create linefssurface */ } RECTFILL(dstrect,0,i*ui->fontheight,linefgsurface->w,linefgsurface->h); SDL_BlitSurface(linefgsurface,NULL,fgsurface,&dstrect); SDL_FreeSurface(linefgsurface),linefgsurface=NULL; } } /* render bg */ trianglew=ui->fontwidth; triangleh=ui->fontheight/2; marginw=ui->fontwidth*4/3; marginh=ui->fontheight/3; marginoffx=0; marginoffy=-ui->fontheight/18; radius=ui->fontheight/2; if((bgsurface=SDL_CreateRGBSurface(0 ,(fgsurface->w+marginw*2)+((direction=='w' || direction=='e')?trianglew:0) ,(fgsurface->h+marginh*2)+((direction=='n' || direction=='s')?triangleh:0) ,32, ui->rmask, ui->gmask, ui->bmask, ui->amask))==NULL) { if(fgsurface!=NULL) SDL_FreeSurface(fgsurface),fgsurface=NULL; return(-1); } if(direction=='n') { intreui_fillroundedr(bgsurface,0,triangleh-1,bgsurface->w,bgsurface->h-triangleh,radius,rgbabg); intreui_triangle(bgsurface,(bgsurface->w-trianglew)/2,0,trianglew,triangleh,direction,rgbabg); RECTFILL(dstrect,marginw+marginoffx,triangleh+marginh+marginoffy,fgsurface->w,fgsurface->h); SDL_BlitSurface(fgsurface,NULL,bgsurface,&dstrect); offx=-bgsurface->w/2; offy=0; } else if(direction=='e') { intreui_fillroundedr(bgsurface,0,0,bgsurface->w-trianglew,bgsurface->h,radius,rgbabg); intreui_triangle(bgsurface,bgsurface->w-trianglew,(bgsurface->h-triangleh)/2,trianglew,triangleh,direction,rgbabg); RECTFILL(dstrect,marginw+marginoffx,marginh+marginoffy,fgsurface->w,fgsurface->h); SDL_BlitSurface(fgsurface,NULL,bgsurface,&dstrect); offx=-bgsurface->w; offy=-bgsurface->h/2; } else if(direction=='s') { intreui_fillroundedr(bgsurface,0,0,bgsurface->w,bgsurface->h-triangleh,radius,rgbabg); intreui_triangle(bgsurface,(bgsurface->w-trianglew)/2,bgsurface->h-triangleh,trianglew,triangleh,direction,rgbabg); RECTFILL(dstrect,marginw+marginoffx,marginh+marginoffy,fgsurface->w,fgsurface->h); SDL_BlitSurface(fgsurface,NULL,bgsurface,&dstrect); offx=-bgsurface->w/2; offy=-bgsurface->h; } else if(direction=='w') { intreui_fillroundedr(bgsurface,trianglew,0,bgsurface->w-trianglew,bgsurface->h,radius,rgbabg); intreui_triangle(bgsurface,0,(bgsurface->h-triangleh)/2,trianglew,triangleh,direction,rgbabg); RECTFILL(dstrect,trianglew+marginw+marginoffx,marginh+marginoffy,fgsurface->w,fgsurface->h); SDL_BlitSurface(fgsurface,NULL,bgsurface,&dstrect); offx=0; offy=-bgsurface->h/2; } else { /* default: center */ intreui_fillroundedr(bgsurface,0,0,bgsurface->w,bgsurface->h,radius,rgbabg); RECTFILL(dstrect,marginw+marginoffx,marginh+marginoffy,fgsurface->w,fgsurface->h); SDL_BlitSurface(fgsurface,NULL,bgsurface,&dstrect); offx=-bgsurface->w/2; offy=-bgsurface->h/2; } if((tex=SDL_CreateTextureFromSurface(ui->renderer,bgsurface))==NULL) { SDL_FreeSurface(fgsurface),fgsurface=NULL; SDL_FreeSurface(bgsurface),bgsurface=NULL; return(-1); } RECTFILL(dstrect,x+offx,y+offy,bgsurface->w,bgsurface->h); SDL_RenderCopy(ui->renderer,tex,NULL,&dstrect); ui->rendererdirty=1; SDL_FreeSurface(fgsurface),fgsurface=NULL; SDL_FreeSurface(bgsurface),bgsurface=NULL; SDL_DestroyTexture(tex),tex=NULL; return(0); } /* * https://sdl.libsdl.narkive.com/rX3aNgKp/rectangle-with-round-corners-in-sdl * * static int fill_rounded_box_b(SDL_Surface* dst, int xo, int yo, * int w, int h, int r, Uint32 color); * * by "Sami Näätänen" (s***@bayminer.com) * (lightly edited to conform to this file code style and semantics) * * draws a rounded box with... * corner radius of 'r' * width of 'w' * and height of 'h' * * draws the box right and down of... * x-offset xo * y-offset yo * * returns -1 if 2*r is bigger than w or h * and draws nothing * returns 0 on success */ static int intreui_fillroundedr(SDL_Surface *dst, int xo, int yo, int w, int h, int r, const char *rgba) { int yd=(dst->pitch)/(dst->format->BytesPerPixel); Uint32 *pixels=NULL; int x,y,i,j; int rpsqrt2= (int) (r/sqrt(2)); int sy,ey,sx,ex; int d,x2m1; int o,l; Uint32 color; if(dst==NULL || rgba==NULL || xo<0 || y0<0 || (xo+w)>dst->w || (yo+h)>dst->h) return(-1); /* sanity check failed */ color=CHARRGBA2UINT(rgba); w/=2; h/=2; xo+=w; yo+=h; w-=r; h-=r; if(w<0||h<0) return(-1); SDL_LockSurface(dst); pixels=(Uint32*)(dst->pixels); l=dst->w*dst->h; sy=(yo-h)*yd; ey=(yo+h)*yd; sx=(xo-w); ex=(xo+w); for(i=sy;i<=ey;i+=yd) { for(j=sx-r;j<=ex+r;j++) { if((o=i+j)>=0 && o=0) { y--; d-=(y*2); } for(i=sx-x;i<=ex+x;i++) { if((o=(sy-y*yd+i))>=0 && o=0 && o=0 && o=0 && ow)?w:myw; RECTFILL(dstrect,x+(w-myw)/2,y+i,myw,1); SDL_FillRect(dst,&dstrect,color); } } else if(direction=='e') { for(i=0;ih)?h:myh; RECTFILL(dstrect,x+w-1-i,y+(h-myh)/2,1,myh); SDL_FillRect(dst,&dstrect,color); } } else if(direction=='s') { for(i=0;iw)?w:myw; RECTFILL(dstrect,x+(w-myw)/2,y+h-1-i,myw,1); SDL_FillRect(dst,&dstrect,color); } } else if(direction=='w') { for(i=0;ih)?h:myh; RECTFILL(dstrect,x+i,y+(h-myh)/2,1,myh); SDL_FillRect(dst,&dstrect,color); } } else { /* default: center */ RECTFILL(dstrect,x,y,w,h); SDL_FillRect(dst,&dstrect,color); } SDL_UnlockSurface(dst); return(0); }