diff -r ec507c1f1df7 -r cdf68044adaf brewpanel/sdlgui.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/brewpanel/sdlgui.c Sat Nov 07 22:04:17 2015 +0100 @@ -0,0 +1,698 @@ +/***************************************************************************** + * Copyright (C) 2015 + * + * Michiel Broek + * + * This file is part of the mbsePi-apps emulator + * + * The gui code is based on the gui from the emulator ARAnyM, + * Copyright (c) 2004 Petr Stehlik of ARAnyM dev team + * + * This progrm is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * mbsePi-apps is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mbsePi-apps; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + *****************************************************************************/ + +#include "brewpanel.h" +#include "sdlgui.h" +#include "lcdfont10x16.h" + + +static SDL_Surface *pSdlGuiScrn; /* Pointer to the actual main SDL screen surface */ +static SDL_Surface *pFontGfx = NULL; /* The LCD font graphics */ +static int fontwidth, fontheight; /* Width & height of the actual font */ +TTF_Font *pFont = NULL; /* TTF font for buttons etc. */ + +extern int my_shutdown; + + +/*-----------------------------------------------------------------------*/ +/* + * Load an 1 plane XBM into a 8 planes SDL_Surface. + */ +static SDL_Surface *SDLGui_LoadXBM(int w, int h, const char *pXbmBits) +{ + SDL_Surface *bitmap; + Uint8 *dstbits; + const Uint8 *srcbits; + int x, y, srcpitch, mask; + + srcbits = (Uint8 *)pXbmBits; + + /* Allocate the bitmap */ + if ((bitmap = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 8, 0, 0, 0, 0)) == NULL) { + syslog(LOG_NOTICE, "Failed to allocate bitmap: %s", SDL_GetError()); + return NULL; + } + + srcpitch = ((w + 7) / 8); + dstbits = (Uint8 *)bitmap->pixels; + mask = 1; + + /* Copy the pixels */ + for (y = 0 ; y < h ; y++) { + for (x = 0 ; x < w ; x++) { + dstbits[x] = (srcbits[x / 8] & mask) ? 1 : 0; + mask <<= 1; + mask |= (mask >> 8); + mask &= 0xFF; + } + dstbits += bitmap->pitch; + srcbits += srcpitch; + } + + return bitmap; +} + + + +/* + * Initialize the GUI. + */ +int SDLGui_Init(void) +{ + char *Pt = NULL; + + SDL_Color blackWhiteColors[2] = {{255, 255, 255, 0}, {0, 0, 0, 0}}; + + /* + * Initialize the LCD font graphics: + */ + pFontGfx = SDLGui_LoadXBM(lcdfont10x16_width, lcdfont10x16_height, lcdfont10x16_bits); + if (pFontGfx == NULL) { + syslog(LOG_NOTICE, "Error: Can not init font graphics!"); + return -1; + } + + /* Set color palette of the LCD font graphics: */ + SDL_SetColors(pFontGfx, blackWhiteColors, 0, 2); + + /* Set font color 0 as transparent: */ + SDL_SetColorKey(pFontGfx, (SDL_SRCCOLORKEY|SDL_RLEACCEL), 0); + + if (TTF_Init() == -1) { + syslog(LOG_NOTICE, "Could not init SDL_ttf"); + return -1; + } + + /* + * Load TTF font for the dialogs + */ + Pt = calloc(1024, sizeof(char)); + sprintf(Pt, "%s", "/usr/share/fonts/TTF/DejaVuSans.ttf"); + if ((pFont = TTF_OpenFont(Pt, 14 )) == NULL) { + sprintf(Pt, "%s", "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf"); + if ((pFont = TTF_OpenFont(Pt, 14 )) == NULL) { + sprintf(Pt, "%s", "/usr/share/fonts/truetype/freefont/DejaVuSans.ttf"); + if ((pFont = TTF_OpenFont(Pt, 14 )) == NULL) { + syslog(LOG_NOTICE, "Could not load DejaVuSans.ttf"); + return -1; + } + } + } + syslog(LOG_NOTICE, "Using ttf font: %s\n", Pt); + free(Pt); + Pt = NULL; + + return 0; +} + + + +/* + * Uninitialize the GUI. + */ +int SDLGui_UnInit(void) +{ + if (pFont) + TTF_CloseFont(pFont); + pFont = NULL; + + if (pFontGfx) + SDL_FreeSurface(pFontGfx); + pFontGfx = NULL; + + return 0; +} + + + +/* + * Inform the SDL-GUI about the actual SDL_Surface screen pointer and + * prepare the font to suit the actual resolution. + */ +int SDLGui_SetScreen(SDL_Surface *pScrn) +{ + pSdlGuiScrn = pScrn; + + if (pFontGfx == NULL) { + syslog(LOG_NOTICE, "Error: A problem with the font occured!"); + return -1; + } + + /* Get the font width and height: */ + fontwidth = pFontGfx->w/16; + fontheight = pFontGfx->h/16; + + return 0; +} + + + +/* + * Center a dialog so that it appears in the middle of the screen. + * Note: We only store the coordinates in the root box of the dialog, + * all other objects in the dialog are positioned relatively to this one. + */ +void SDLGui_CenterDlg(SGOBJ *dlg) +{ + dlg[0].x = (pSdlGuiScrn->w/1-dlg[0].w)/2; + dlg[0].y = (pSdlGuiScrn->h/1-dlg[0].h)/2; +} + + + +/* + * Draw a text string using TTF + */ +static void SDLGui_TTF(int x, int y, const char *txt) +{ + SDL_Rect offset; + SDL_Color textColor = { 0, 0, 0 }; + SDL_Surface* message = NULL; + + message = TTF_RenderText_Solid(pFont, txt, textColor); + offset.x = x; + offset.y = y; + SDL_BlitSurface(message, NULL, pSdlGuiScrn, &offset); + SDL_FreeSurface(message); + message = NULL; +} + + + +/* + * Draw a dialog TTF text object. + */ +static void SDLGui_DrawTTF(const SGOBJ *tdlg, int objnum) +{ + int x, y; + + x = (tdlg[0].x + tdlg[objnum].x); + y = (tdlg[0].y + tdlg[objnum].y); + SDLGui_TTF(x, y, tdlg[objnum].txt); +} + + + +/* + * Draw a text string. + */ +static void SDLGui_Text(int x, int y, const char *txt) +{ + int i; + char c; + SDL_Rect sr, dr; + + for (i=0; txt[i]!=0; i++) { + c = txt[i]; + sr.x=fontwidth*(c%16); + sr.y=fontheight*(c/16); + sr.w=fontwidth; + sr.h=fontheight; + dr.x=x+i*(fontwidth+2); + dr.y=y; + dr.w=fontwidth; + dr.h=fontheight; + SDL_BlitSurface(pFontGfx, &sr, pSdlGuiScrn, &dr); + } +} + + + +/* + * Draw a dialog text object. + */ +static void SDLGui_DrawText(const SGOBJ *tdlg, int objnum) +{ + int x, y; + + x = (tdlg[0].x + tdlg[objnum].x); + y = (tdlg[0].y + tdlg[objnum].y); + SDLGui_Text(x, y, tdlg[objnum].txt); +} + + + +/* + * Draw a dialog LCD object. + */ +static void SDLGui_DrawLCD(const SGOBJ *bdlg, int objnum) +{ + SDL_Rect rect; + int x, y, w, h, offset, border = 4; + Uint32 bg0 = SDL_MapRGB(pSdlGuiScrn->format, 94,147, 69); + Uint32 bg1 = SDL_MapRGB(pSdlGuiScrn->format,156,235, 4); + Uint32 bc = SDL_MapRGB(pSdlGuiScrn->format, 32, 32, 32); + Uint32 bg; + + /* + * Width and height are given in character columns and rows, + * so calculate the display size in pixels. + */ + w = bdlg[objnum].w * (fontwidth + 2) + 10; + h = bdlg[objnum].h * (fontheight + 2) + 4; + + if (bdlg[objnum].x == -1) { + /* + * Auto center + */ + x = (bdlg[0].w - w) / 2; + } else { + x = bdlg[objnum].x; + } + y = bdlg[objnum].y; + if (objnum > 0) { /* Since the root object is a box, too, */ + /* we have to look for it now here and only */ + x += bdlg[0].x; /* add its absolute coordinates if we need to */ + y += bdlg[0].y; + } + + if (bdlg[objnum].state & SG_SELECTED) { + bg = bg1; + } else { + bg = bg0; + } + + /* The root box should be bigger than the screen, so we disable the offset there: */ + if (objnum != 0) + offset = border; + else + offset = 0; + + /* Draw background: */ + rect.x = x; + rect.y = y; + rect.w = w; + rect.h = h; + SDL_FillRect(pSdlGuiScrn, &rect, bg); + + /* Draw upper border: */ + rect.x = x - offset; + rect.y = y - offset; + rect.w = w + offset + offset; + rect.h = border; + SDL_FillRect(pSdlGuiScrn, &rect, bc); + + /* Draw left border: */ + rect.x = x - offset; + rect.y = y; + rect.w = border; + rect.h = h; + SDL_FillRect(pSdlGuiScrn, &rect, bc); + + /* Draw bottom border: */ + rect.x = x - offset; + rect.y = y + h - border + offset; + rect.w = w + offset + offset; + rect.h = border; + SDL_FillRect(pSdlGuiScrn, &rect, bc); + + /* Draw right border: */ + rect.x = x + w - border + offset; + rect.y = y; + rect.w = border; + rect.h = h; + SDL_FillRect(pSdlGuiScrn, &rect, bc); +} + + + +/* + * Draw a dialog box object. + */ +static void SDLGui_DrawBox(const SGOBJ *bdlg, int objnum) +{ + SDL_Rect rect; + int x, y, w, h, offset, shade = 2; + Uint32 grey = SDL_MapRGB(pSdlGuiScrn->format,192,192,192); + Uint32 upleftc, downrightc; + + x = bdlg[objnum].x; + y = bdlg[objnum].y; + if (objnum > 0) { /* Since the root object is a box, too, */ + /* we have to look for it now here and only */ + x += bdlg[0].x; /* add its absolute coordinates if we need to */ + y += bdlg[0].y; + } + w = bdlg[objnum].w; + h = bdlg[objnum].h; + + if (bdlg[objnum].state & SG_SELECTED) { + upleftc = SDL_MapRGB(pSdlGuiScrn->format,128,128,128); + downrightc = SDL_MapRGB(pSdlGuiScrn->format,255,255,255); + } else { + upleftc = SDL_MapRGB(pSdlGuiScrn->format,255,255,255); + downrightc = SDL_MapRGB(pSdlGuiScrn->format,128,128,128); + } + + /* The root box should be bigger than the screen, so we disable the offset there: */ + if (objnum != 0) + offset = shade; + else + offset = 0; + + /* Draw background: */ + rect.x = x; + rect.y = y; + rect.w = w; + rect.h = h; + SDL_FillRect(pSdlGuiScrn, &rect, grey); + + /* Draw upper border: */ + rect.x = x; + rect.y = y - offset; + rect.w = w; + rect.h = shade; + SDL_FillRect(pSdlGuiScrn, &rect, upleftc); + + /* Draw left border: */ + rect.x = x - offset; + rect.y = y; + rect.w = shade; + rect.h = h; + SDL_FillRect(pSdlGuiScrn, &rect, upleftc); + + /* Draw bottom border: */ + rect.x = x; + rect.y = y + h - shade + offset; + rect.w = w; + rect.h = shade; + SDL_FillRect(pSdlGuiScrn, &rect, downrightc); + + /* Draw right border: */ + rect.x = x + w - shade + offset; + rect.y = y; + rect.w = shade; + rect.h = h; + SDL_FillRect(pSdlGuiScrn, &rect, downrightc); +} + + + +/* + * Draw a normal button. + */ +static void SDLGui_DrawButton(const SGOBJ *bdlg, int objnum) +{ + int x, y, w, h; + + SDLGui_DrawBox(bdlg, objnum); + /* + * Use bold text and get outer dimensions of the text + */ + TTF_SetFontStyle(pFont, TTF_STYLE_BOLD); + TTF_SizeText(pFont, bdlg[objnum].txt, &w, &h); + x = bdlg[0].x + bdlg[objnum].x + (bdlg[objnum].w - w) / 2; + y = bdlg[0].y + bdlg[objnum].y + (bdlg[objnum].h - h) / 2; + + if (bdlg[objnum].state & SG_SELECTED) { + x += 1; + y += 1; + } + + if ((bdlg[objnum].flags & SG_HIDE) == 0) + SDLGui_TTF(x, y, bdlg[objnum].txt); + TTF_SetFontStyle(pFont, TTF_STYLE_NORMAL); +} + + + +/* + * Draw a whole dialog. + */ +void SDLGui_DrawDialog(const SGOBJ *dlg) +{ + int i; + + for (i = 0; dlg[i].type != -1; i++) { + switch (dlg[i].type) { + case SGBOX: + SDLGui_DrawBox(dlg, i); + break; + case SGLCD: + SDLGui_DrawLCD(dlg, i); + break; + case SGTEXT: + SDLGui_DrawText(dlg, i); + break; + case SGTTF: + SDLGui_DrawTTF(dlg, i); + break; + case SGBUTTON: + SDLGui_DrawButton(dlg, i); + break; + } + } + + SDL_UpdateRect(pSdlGuiScrn, 0,0,0,0); +} + + + +/* + * Search an object at a certain position. + */ +static int SDLGui_FindObj(const SGOBJ *dlg, int fx, int fy) +{ + int len, i, ob = -1, xpos, ypos; + + len = 0; + while (dlg[len].type != -1) + len++; + + xpos = fx; + ypos = fy; + /* Now search for the object: */ + for (i = len; i >= 0; i--) { + if (xpos >= dlg[0].x + dlg[i].x && ypos >= dlg[0].y + dlg[i].y && + xpos < dlg[0].x + dlg[i].x + dlg[i].w && ypos < dlg[0].y + dlg[i].y + dlg[i].h) { + ob = i; + break; + } + } + + return ob; +} + + + +/* + * Search a button with a special flag (e.g. SG_DEFAULT or SG_CANCEL). + */ +static int SDLGui_SearchFlaggedButton(const SGOBJ *dlg, int flag) +{ + int i = 0; + + while (dlg[i].type != -1) { + if (dlg[i].flags & flag) + return i; + i++; + } + + return 0; +} + + + +/* + * Show and process a dialog. Returns the button number that has been + * pressed or SDLGUI_UNKNOWNEVENT if an unsupported event occured (will be + * stored in parameter pEventOut). + */ +int SDLGui_DoDialog(SGOBJ *dlg, SDL_Event *pEventOut) +{ + int obj = 0, oldbutton = 0, retbutton = 0, i, j, b; + SDL_Event sdlEvent; + SDL_Surface *pBgSurface; + SDL_Rect dlgrect, bgrect; + +// if (pSdlGuiScrn->h / fontheight < dlg[0].h) +// { +// syslog(LOG_NOTICE, "Screen size too small for dialog!"); +// return SDLGUI_ERROR; +// } + + dlgrect.x = dlg[0].x; + dlgrect.y = dlg[0].y; + dlgrect.w = dlg[0].w; + dlgrect.h = dlg[0].h; + + bgrect.x = bgrect.y = 0; + bgrect.w = dlgrect.w; + bgrect.h = dlgrect.h; + + /* + * Save background + */ + pBgSurface = SDL_CreateRGBSurface(SDL_SWSURFACE, dlgrect.w, dlgrect.h, pSdlGuiScrn->format->BitsPerPixel, + pSdlGuiScrn->format->Rmask, pSdlGuiScrn->format->Gmask, pSdlGuiScrn->format->Bmask, pSdlGuiScrn->format->Amask); + if (pSdlGuiScrn->format->palette != NULL) { + SDL_SetColors(pBgSurface, pSdlGuiScrn->format->palette->colors, 0, pSdlGuiScrn->format->palette->ncolors-1); + } + + if (pBgSurface != NULL) { + SDL_BlitSurface(pSdlGuiScrn, &dlgrect, pBgSurface, &bgrect); + } else { + syslog(LOG_NOTICE, "SDLGUI_DoDialog: CreateRGBSurface failed: %s", SDL_GetError()); + } + + /* (Re-)draw the dialog */ + SDLGui_DrawDialog(dlg); + + /* + * Is the left mouse button still pressed? Yes -> Handle TOUCHEXIT objects here + */ + SDL_PumpEvents(); + b = SDL_GetMouseState(&i, &j); + obj = SDLGui_FindObj(dlg, i, j); + if (obj > 0 && (dlg[obj].flags & SG_TOUCHEXIT) ) { + oldbutton = obj; + if (b & SDL_BUTTON(1)) { + dlg[obj].state |= SG_SELECTED; + retbutton = obj; + } + } + + /* The main loop */ + while (retbutton == 0 && !my_shutdown) { + if (SDL_WaitEvent(&sdlEvent) == 1) /* Wait for events */ + switch (sdlEvent.type) { + case SDL_QUIT: + retbutton = SDLGUI_QUIT; + break; + + case SDL_MOUSEBUTTONDOWN: + if (sdlEvent.button.button != SDL_BUTTON_LEFT) { + /* Not left mouse button -> unsupported event */ + if (pEventOut) + retbutton = SDLGUI_UNKNOWNEVENT; + break; + } + /* It was the left button: Find the object under the mouse cursor */ + obj = SDLGui_FindObj(dlg, sdlEvent.button.x, sdlEvent.button.y); + if (obj > 0) { + if (dlg[obj].type == SGBUTTON) { + dlg[obj].state |= SG_SELECTED; + SDLGui_DrawButton(dlg, obj); + SDL_UpdateRect(pSdlGuiScrn, dlg[0].x + dlg[obj].x - 2, dlg[0].y + dlg[obj].y - 2, dlg[obj].w + 4, dlg[obj].h + 4); + oldbutton=obj; + } + if ( dlg[obj].flags & SG_TOUCHEXIT ) { + dlg[obj].state |= SG_SELECTED; + retbutton = obj; + } + } + break; + + case SDL_MOUSEBUTTONUP: + if (sdlEvent.button.button != SDL_BUTTON_LEFT) { + /* Not left mouse button -> unsupported event */ + if (pEventOut) + retbutton = SDLGUI_UNKNOWNEVENT; + break; + } + /* It was the left button: Find the object under the mouse cursor */ + obj = SDLGui_FindObj(dlg, sdlEvent.button.x, sdlEvent.button.y); + if (obj > 0) { + switch (dlg[obj].type) { + case SGBUTTON: + if (oldbutton==obj) + retbutton=obj; + break; + } + } + if (oldbutton > 0) { + dlg[oldbutton].state &= ~SG_SELECTED; + SDLGui_DrawButton(dlg, oldbutton); + SDL_UpdateRect(pSdlGuiScrn, (dlg[0].x+dlg[oldbutton].x)*fontwidth-2, (dlg[0].y+dlg[oldbutton].y)*fontheight-2, + dlg[oldbutton].w*fontwidth+4, dlg[oldbutton].h*fontheight+4); + oldbutton = 0; + } + if (obj >= 0 && (dlg[obj].flags & SG_EXIT)) { + retbutton = obj; + } + break; + + case SDL_MOUSEMOTION: + break; + + case SDL_KEYDOWN: /* Key pressed */ + if (sdlEvent.key.keysym.sym == SDLK_RETURN || sdlEvent.key.keysym.sym == SDLK_KP_ENTER) { + retbutton = SDLGui_SearchFlaggedButton(dlg, SG_DEFAULT); + } else if (sdlEvent.key.keysym.sym == SDLK_ESCAPE) { + retbutton = SDLGui_SearchFlaggedButton(dlg, SG_CANCEL); + } else if (pEventOut) { + retbutton = SDLGUI_UNKNOWNEVENT; + } + break; + + default: + if (pEventOut) + retbutton = SDLGUI_UNKNOWNEVENT; + break; + } + } + + /* Restore background */ + if (pBgSurface) { + SDL_BlitSurface(pBgSurface, &bgrect, pSdlGuiScrn, &dlgrect); + SDL_FreeSurface(pBgSurface); + } + + /* Copy event data of unsupported events if caller wants to have it */ + if (retbutton == SDLGUI_UNKNOWNEVENT && pEventOut) + memcpy(pEventOut, &sdlEvent, sizeof(SDL_Event)); + + if (retbutton == SDLGUI_QUIT) + my_shutdown = TRUE; + + return retbutton; +} + + + +void SDLGui_LCDwrite(SGOBJ *dlg, int x, int y, Uint8 c, int lcdindex) +{ + int i, index; + + fprintf(stdout, "SDLGui_LCDwrite( , %d, %d, %c, %d)\n", x, y, c, lcdindex); + + i = index = 0; + for (;;) { + if (dlg[i].type == -1) { + syslog(LOG_NOTICE, "SDLGui_LCDwrite() lcdindex=%d not found", lcdindex); + return; + } + if (dlg[i].type == SGLCD) { + if (index == lcdindex) + break; + index++; + } + i++; + } + fprintf(stdout, "SDLGui_LCDwrite i=%d LCD=%dx%d\n", i, dlg[i].w, dlg[i].h); + +} + +