/* render.c Draws screens on the LCD. This needs to be greatly expanded and redone for greater flexibility. For example, it should support multiple screen sizes, more flexible widgets, and multiple simultaneous screens. This will probably take a while to do. :( THIS FILE IS MESSY! Anyone care to rewrite it nicely? Please?? :) */ #include #include #include "../shared/debug.h" #include "../shared/LL.h" #include "drivers/lcd.h" #include "screen.h" #include "screenlist.h" #include "widget.h" #include "render.h" int heartbeat=HEART_OPEN; int backlight=BACKLIGHT_OPEN; int backlight_state = BACKLIGHT_OPEN; int backlight_brightness=255; int backlight_off_brightness=0; int output_state = 0; static int reset; #define BUFSIZE 1024 static int draw_frame(LL *list, char fscroll, int left, int top, int right, int bottom, int fwid, int fhgt, int fspeed, int timer); int draw_screen(screen *s, int timer) { static screen *old_s=NULL; int tmp=0; //debug("Render...\n"); //return 0; reset = 1; //debug("draw_screen: %8x, %i\n", (int)s, timer); if(!s) return -1; if(s == old_s) reset = 0; old_s = s; lcd.clear(); switch(backlight_state) { case BACKLIGHT_OFF: lcd.backlight(backlight_off_brightness); break; case BACKLIGHT_ON: lcd.backlight(backlight_brightness); break; default: if(backlight_state & BACKLIGHT_FLASH) { tmp = (!((timer&7) == 7)); if(backlight_state & 1) lcd.backlight(tmp?backlight_brightness:backlight_off_brightness); //lcd.backlight(backlight_brightness * (!((timer&7) == 7))); else lcd.backlight(!tmp?backlight_brightness:backlight_off_brightness); //lcd.backlight(backlight_brightness * ((timer&7) == 7)); } else if(backlight_state & BACKLIGHT_BLINK) { tmp = (!((timer&14) == 14)); if(backlight_state & 1) lcd.backlight(tmp?backlight_brightness:backlight_off_brightness); //lcd.backlight(backlight_brightness * (!((timer&14) == 14))); else lcd.backlight(!tmp?backlight_brightness:backlight_off_brightness); //lcd.backlight(backlight_brightness * ((timer&14) == 14)); } break; } lcd.output(output_state); draw_frame(s->widgets, 'v', 0, 0, lcd.wid, lcd.hgt, s->wid, s->hgt, s->duration/s->hgt, timer); //debug("draw_screen done\n"); if(heartbeat) { if(s->heartbeat || heartbeat == HEART_ON) { // Set this to pulsate like a real heart beat... // (binary is fun... :) lcd.icon(!((timer+4)&5), 0); lcd.chr(lcd.wid, 1, 0); } } lcd.flush(); //debug("draw_screen: %8x, %i\n", s, timer); return 0; } static int draw_frame(LL *list, char fscroll, int left, int top, int right, int bottom, int fwid, int fhgt, int fspeed, int timer) { char str[BUFSIZE]; // scratch buffer char strmarq[(BUFSIZE*2)+3]; // scratch buffer widget *w; int wid, hgt; // Width and height of visible frame area int x, y; int fx, fy; // Scrolling offset for the frame... int length, speed; //int lines; int reset = 1; wid = right - left; // This is the size of the visible frame area hgt = bottom - top; fx = 0; fy = 0; if(fscroll == 'v') { if(fspeed > 0) fy = (timer-fspeed) / fspeed; if(fspeed < 0) fy = (-fspeed) * timer; if(fy < 0) fy = 0; // Make sure the whole frame gets displayed, at least... if(!screenlist_action) screenlist_action = RENDER_HOLD; if((fy) > fhgt - 1) { // Release hold after it has been displayed if(!screenlist_action || screenlist_action == RENDER_HOLD) screenlist_action = 0; } fy %= fhgt; if(fy > fhgt - hgt) fy = fhgt - hgt; } else if (fscroll == 'h') { // TODO: Frames don't scroll horizontally yet! } //debug("draw_screen: %8x, %i\n", s, timer); if(!list) return -1; //debug("draw_frame: %8x, %i\n", frame, timer); LL_Rewind(list); do { w = (widget *)LL_Get(list); if(!w) return -1; // TODO: Make this cleaner and more flexible! switch(w->type) { case WID_STRING: if((w->x > 0) && (w->y > 0) && (w->text)) { if((w->y <= hgt + fy) && (w->y > fy)) { strncpy(str, w->text, wid - w->x + 1); str[wid - w->x + 1] = 0; lcd.string(w->x + left, w->y + top - fy, str); } } break; case WID_HBAR: if(reset) { lcd.init_hbar(); reset = 0; } if((w->x > 0) && (w->y > 0)) { if((w->y <= hgt + fy) && (w->y > fy)) { if(w->length > 0) { if((w->length / lcd.cellwid) < wid - w->x + 1) lcd.hbar(w->x + left, w->y + top - fy, w->length); else lcd.hbar(w->x + left, w->y + top - fy, wid * lcd.cellwid); } else if(w->length < 0) { // TODO: Rearrange stuff to get left-extending // hbars to draw correctly... // .. er, this'll require driver modifications, // so I'll leave it out for now. } } } break; case WID_VBAR: // FIXME: Vbars don't work in frames! if(reset) { lcd.init_vbar(); reset = 0; } if((w->x > 0) && (w->y > 0)) { if(w->length > 0) { lcd.vbar(w->x, w->length); } else if(w->length < 0) { // TODO: Rearrange stuff to get down-extending // vbars to draw correctly... // .. er, this'll require driver modifications, // so I'll leave it out for now. } } break; case WID_ICON: // FIXME: Not implemented break; case WID_TITLE: // FIXME: Doesn't work quite right in frames... if(!w->text) break; if(wid < 8) break; memset(str, 0xf8, wid); //#### 0xf8 was 255. (quickhack for ISE display!) str[2] = ' '; length = strlen(w->text); if(length <= wid-6) { memcpy(str+3, w->text, length); str[length+3] = ' '; } else // Scroll the title, if it doesn't fit... { speed = 1; x = timer / speed; y = x / length; // Make sure the whole title gets displayed, at least... if(!screenlist_action) screenlist_action = RENDER_HOLD; if(x > length - 6) { // Release hold after it has been displayed if(!screenlist_action || screenlist_action == RENDER_HOLD) screenlist_action = 0; } x %= (length); x -= 3; if(x < 0) x = 0; if(x > length - (wid-6)) x = length - (wid-6); if(y&1) // Scrolling backwards... { x = (length-(wid-6)) - x; } strncpy(str+3, w->text+x, (wid-6)); str[wid-3] = ' '; } str[wid] = 0; lcd.string(1 + left, 1 + top, str); break; case WID_SCROLLER: // FIXME: doesn't work in frames... { int offset; int screen_width; if (!w->text) break; if (w->right < w->left) break; //printf("rendering: %s %d\n",w->text,timer); screen_width = w->right - w->left + 1; switch (w->length) { // actually, direction... // FIXED: Horz scrollers don't show the // last letter in the string... (1-off error?) case 'h': length = strlen(w->text) + 1 ; if (length <= screen_width) { /* it fits within the box, just render it */ lcd.string(w->left,w->top,w->text); } else { int effLength = length-1 +3; //(we add 3 chars because of wrapping around) int necessaryTimeUnits = 0; if (!screenlist_action) screenlist_action = RENDER_HOLD; if (w->speed > 0) { necessaryTimeUnits = effLength * w->speed; //if (((timer / (effLength*w->speed)) % 2) == 0) { //wiggle one way offset = (timer % (effLength*w->speed)) / w->speed; //} //else //{ //wiggle the other // offset = (((timer % (effLength*w->speed)) // - (effLength*w->speed) + 1) // / w->speed) * -1; //} } else if (w->speed < 0) { necessaryTimeUnits = effLength / (w->speed * -1); // if (((timer / (effLength/(w->speed*-1))) % 2) == 0) //{ offset = (timer % (effLength/(w->speed*-1))) * w->speed * -1; //} //else // { offset = (((timer % (effLength/(w->speed*-1))) * w->speed * -1) - effLength + 1) * -1; //} } else { offset = 0; if(screenlist_action == RENDER_HOLD) screenlist_action = 0; } if (timer > necessaryTimeUnits) { if(screenlist_action == RENDER_HOLD) screenlist_action = 0; } //just extend the text.. strcpy(strmarq,w->text); strcat(strmarq," | "); strncat(strmarq,w->text,screen_width); //now offsets can be beyond normal range.. str[0]='\0'; strncat(str,&((strmarq)[offset]),screen_width); //if (offset <= length) // { //is offset still withing range? //if (offsettext)) // { //} //do we have "space left" that we should fill? //strncat(str," | ", strlen(str)-screen_width); //strncat(str,w->text, strlen(str)-screen_width-3); // } //str[screen_width] = '\0'; //printf("%s : %d\n",str,length-offset); //} //else //{ // // } lcd.string(w->left,w->top,str); } break; // FIXME: Vert scrollers don't always seem to scroll // back up after hitting the bottom. They jump back to // the top instead... (nevermind?) case 'v': { int i=0; length = strlen(w->text); if (length <= screen_width) { /* no scrolling required...*/ lcd.string(w->left,w->top,w->text); } else { int lines_required = (length / screen_width) + (length % screen_width ? 1 : 0); int available_lines = (w->bottom - w->top + 1); if (lines_required <= available_lines) { // easy... for(i=0;itext)[i*screen_width]), screen_width); str[screen_width] = '\0'; lcd.string(w->left,w->top + i,str); } } else { int necessaryTimeUnits = 0; int effLines = lines_required - available_lines + 1; int begin = 0; if (!screenlist_action) screenlist_action = RENDER_HOLD; //printf("length: %d sw: %d lines req: %d avail lines: %d effLines: %d \n",length,screen_width,lines_required,available_lines,effLines); if (w->speed > 0) { necessaryTimeUnits = effLines * w->speed; if (((timer / (effLines*w->speed)) % 2) == 0) { //printf("up "); begin = (timer % (effLines*w->speed)) / w->speed; } else { //printf("down "); begin = (((timer % (effLines*w->speed)) - (effLines*w->speed) + 1)/w->speed) * -1; } } else if (w->speed < 0) { necessaryTimeUnits = effLines / (w->speed * -1); if (((timer / (effLines/(w->speed*-1))) % 2) == 0) { begin = (timer % (effLines/(w->speed*-1))) * w->speed * -1; } else { begin = (((timer % (effLines/(w->speed*-1))) * w->speed * -1) - effLines + 1) * -1; } } else { begin = 0; } //printf("rendering begin: %d timer: %d effLines: %d\n",begin,timer,effLines); for(i=begin;itext)[i*(screen_width)]), screen_width); str[screen_width] = '\0'; //printf("rendering: '%s' of %s\n", //str,w->text); lcd.string(w->left,w->top + (i-begin),str); } if (timer > necessaryTimeUnits) { if(screenlist_action == RENDER_HOLD) screenlist_action = 0; } } } break; } } break; } case WID_FRAME: { // FIXME: doesn't handle nested frames quite right! // doesn't handle scrolling in nested frames at all... int new_left, new_top, new_right, new_bottom; new_left = left + w->left - 1; new_top = top + w->top - 1; new_right = left + w->right; new_bottom = top + w->bottom; if(new_right > right) new_right = right; if(new_bottom > bottom) new_bottom = bottom; if(new_left >= right || new_top >= bottom) { // Do nothing if it's invisible... } else { draw_frame(w->kids, w->length, new_left, new_top, new_right, new_bottom, w->wid, w->hgt, w->speed, timer); } } break; case WID_NUM: // FIXME: doesn't work in frames... if((w->x > 0) && (w->y >= 0) && (w->y <= 9)) { if(reset) { lcd.init_num(); reset = 0; } lcd.num(w->x + left, w->y); } break; case WID_NONE: default: break; } } while(LL_Next(list) == 0); return 0; }