/* * render.c * This file is part of LCDd, the lcdproc server. * * This file is released under the GNU General Public License. Refer to the * COPYING file distributed with this package. * * Copyright (c) 1999, William Ferrell, Scott Scriven * 2001, Joris Robijn * * * 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?? :) * * NOTE: (from David Douthitt) Multiple screen sizes? Multiple simultaneous * screens? Horrors of horrors... next thing you know it'll be making coffee... * Better believe it'll take a while to do... * */ #include #include #include #include "shared/report.h" #include "shared/LL.h" #include "drivers.h" #include "screen.h" #include "screenlist.h" #include "widget.h" #include "render.h" int heartbeat = HEARTBEAT_OPEN; int heartbeat_fallback = HEARTBEAT_ON; /* If no heartbeat setting has been set at all */ int backlight = BACKLIGHT_OPEN; int backlight_fallback = BACKLIGHT_ON; /* If no backlight setting has been set at all */ int backlight_brightness = 255; int backlight_off_brightness = 0; int output_state = 0; char * server_msg_text; int server_msg_expire = 0; static int reset; #define BUFSIZE 1024 static int render_frame (LinkedList * list, char fscroll, int left, int top, int right, int bottom, int fwid, int fhgt, int fspeed, long int timer); int render_screen (Screen * s, long int timer) { static Screen * old_s = NULL; int tmp_state = 0; debug(RPT_DEBUG, "%s( screen=[%.40s], timer=%d ) ==== START RENDERING ====", __FUNCTION__, s->id, timer ); reset = 1; if (!s) return -1; if (s == old_s) reset = 0; old_s = s; /* Clear the LCD screen... */ drivers_clear (); /* FIXME drivers_backlight -- * * If the screen's backlight isn't set (default) then we * inherit the backlight state from the parent client. This allows * the client to override it's childrens settings. * The server can also override the clients and screens settings. */ if (backlight != BACKLIGHT_OPEN) { tmp_state = backlight; } else if (s->client && s->client->backlight != BACKLIGHT_OPEN) { tmp_state = s->client->backlight; } else if (s->backlight != BACKLIGHT_OPEN) { tmp_state = s->backlight; } else { tmp_state = backlight_fallback; } /* Set up backlight to the correct state... */ /* NOTE: dirty stripping of other options... */ /* Backlight flash: check timer and flip backlight as appropriate */ if (tmp_state & BACKLIGHT_FLASH) { drivers_backlight ( ( (tmp_state & BACKLIGHT_ON) ^ ((timer & 7) == 7) ) ? BACKLIGHT_ON : BACKLIGHT_OFF ); /* Backlight blink: check timer and flip backlight as appropriate */ } else if (tmp_state & BACKLIGHT_BLINK) { drivers_backlight ( ( (tmp_state & BACKLIGHT_ON) ^ ((timer & 14) == 14) ) ? BACKLIGHT_ON : BACKLIGHT_OFF ); } else { /* Simple: Only send lowest bit then...*/ drivers_backlight (tmp_state & BACKLIGHT_ON); } /* Output ports from LCD - outputs depend on the current screen */ drivers_output (output_state); /* Draw a frame... */ render_frame (s->widgetlist, 'v', 0, 0, display_props->width, display_props->height, s->width, s->height, (((s->duration / s->height) < 1) ? 1 : (s->duration / s->height)), timer); /* Set the cursor */ drivers_cursor (s->cursor_x, s->cursor_y, s->cursor); if (heartbeat != HEARTBEAT_OPEN) { tmp_state = heartbeat; } else if (s->client && s->client->heartbeat != HEARTBEAT_OPEN) { tmp_state = s->client->heartbeat; } else if (s->heartbeat != HEARTBEAT_OPEN) { tmp_state = s->heartbeat; } else { tmp_state = heartbeat_fallback; } drivers_heartbeat(tmp_state); /* If there is an server message that is not expired, display it */ if( server_msg_expire > 0 ) { drivers_string( display_props->width - strlen(server_msg_text) + 1, display_props->height, server_msg_text ); server_msg_expire --; if( server_msg_expire == 0 ) { free( server_msg_text ); } } /* flush display out, frame and all... */ drivers_flush (); debug(RPT_DEBUG, "==== END RENDERING ====" ); return 0; } /* The following function is positively ghastly (as was mentioned above!) */ /* Best thing to do is to remove support for frames... but anyway... */ /* */ static int render_frame (LinkedList * list, char fscroll, /* direction of scrolling */ int left, /* left edge of frame */ int top, /* top edge of frame */ int right, /* right edge of frame */ int bottom, /* bottom edge of frame */ int fwid, /* frame width? */ int fhgt, /* frame height? */ int fspeed, /* speed of scrolling... */ long int timer) /* ? */ { #define VerticalScrolling (fscroll == 'v') #define HorizontalScrolling (fscroll == 'h') char str[BUFSIZE]; /* scratch buffer */ Widget * w; int vis_width, vis_height; /* Width and height of visible frame area */ int x, y; int fx, fy; /* Scrolling offset for the frame... */ int length, speed; /*int lines; */ int str_length = BUFSIZE-1; int reset = 1; debug( RPT_DEBUG, "%s( list=%p, fscroll='%c', left=%d, top=%d, " "right=%d, bottom=%d, fwid=%d, fhgt=%d, fspeed=%d, timer=%d )", __FUNCTION__, list, fscroll, left,top, right, bottom, fwid, fhgt, fspeed, timer ); vis_width = right - left; /* This is the size of the visible frame area */ vis_height = bottom - top; fx = 0; fy = 0; if (VerticalScrolling) { if (fspeed > 0) fy = (timer - fspeed) / fspeed; else if (fspeed < 0) fy = (-fspeed) * timer; if (fy < 0) fy = 0; if (fhgt == 0) { fy = 0; } else { fy %= fhgt; } if (fy > fhgt - vis_height) fy = fhgt - vis_height; } else if (HorizontalScrolling) { /* TODO: Frames don't scroll horizontally yet! */ } if (!list) return -1; 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 <= vis_height + fy) && (w->y > fy)) { if (w->x > vis_width) w->x=vis_width; str_length = abs(vis_width - w->x + 1); if (str_length >= BUFSIZE) str_length = BUFSIZE - 1; strncpy (str, w->text, str_length); str[str_length+1] = 0; drivers_string (w->x + left, w->y + top - fy, str); } } break; case WID_HBAR: if (reset) { //drivers_init_hbar (); reset = 0; } if ((w->x > 0) && (w->y > 0)) { if ((w->y <= vis_height + fy) && (w->y > fy)) { if (w->length > 0) { if ((w->length / display_props->cellwidth) < vis_width - w->x + 1) { /*was: drivers_hbar (w->x + left, w->y + top - fy, w->length); */ /* improvised len and promille */ int full_len = display_props->width - w->x - left + 1; int promille = (long) 1000 * w->length / ( display_props->cellwidth * full_len ); drivers_hbar (w->x + left, w->y + top - fy, full_len, promille, BAR_PATTERN_FILLED); } else { /*was: drivers_hbar (w->x + left, w->y + top - fy, wid * display_props->cellwidth); */ /* Improvised len and promille while we have the old widget language */ int full_len = ( display_props->width - w->x - left + 1 ); drivers_hbar (w->x + left, w->y + top - fy, full_len, 1000, BAR_PATTERN_FILLED); } } 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) { //drivers_init_vbar (); reset = 0; } if ((w->x > 0) && (w->y > 0)) { if (w->length > 0) { /* Improvised len and promille while we have the old widget language */ int full_len = display_props->height; int promille = (long) 1000 * w->length / display_props->cellheight / full_len; drivers_vbar (w->x, display_props->height, full_len, promille, BAR_PATTERN_FILLED); } 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: drivers_icon (w->x, w->y, w->length); break; case WID_TITLE: /* FIXME: Doesn't work quite right in frames...*/ if (!w->text) break; if (vis_width < 8) break; drivers_icon (w->x + left, w->y + top, ICON_BLOCK_FILLED); drivers_icon (w->x + left + 1, w->y + top, ICON_BLOCK_FILLED); length = strlen (w->text); if (length >= BUFSIZE) length = BUFSIZE - 1; if (length <= vis_width - 6) { strncpy (str, w->text, length); str[length+1] = 0; x = length + 5; } else /* Scroll the title, if it doesn't fit...*/ { speed = 1; x = timer / speed; y = x / length; x %= (length); if (x < 0) x = 0; if (x > length - (vis_width - 6)) x = length - (vis_width - 6); if (y & 1) /* Scrolling backwards...*/ { x = (length - (vis_width - 6)) - x; } str_length = abs(vis_width - 6); if (str_length >= BUFSIZE) str_length = BUFSIZE -1; strncpy (str, w->text + x, str_length); str[str_length+1] = 0; x = vis_width - 1; } drivers_string (w->x + 3 + left, w->y + top, str); for (; x<=vis_width; x++) { drivers_icon (w->x + x - 1 + left, w->y + top, ICON_BLOCK_FILLED); } 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; /*debug(RPT_DEBUG, "%s: %s %d",__FUNCTION__,w->text,timer);*/ screen_width = abs(w->right - w->left + 1); if (screen_width >= BUFSIZE) screen_width = BUFSIZE -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 */ drivers_string (w->left, w->top, w->text); } else { int effLength = length - screen_width; int necessaryTimeUnits = 0; 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 (offset <= length) { strncpy (str, &((w->text)[offset]), screen_width); str[screen_width] = '\0'; /*debug(RPT_DEBUG, "scroller %s : %d", str, length-offset); */ } else { str[0] = '\0'; } drivers_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... */ drivers_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; i < lines_required; i++) { strncpy (str, &((w->text)[i * screen_width]), screen_width); str[screen_width] = '\0'; drivers_string (w->left, w->top + i, str); } } else { int necessaryTimeUnits = 0; int effLines = lines_required - available_lines + 1; int begin = 0; /*debug(RPT_DEBUG, "length: %d sw: %d lines req: %d avail lines: %d effLines: %d ",length,screen_width,lines_required,available_lines,effLines);*/ if (w->speed > 0) { necessaryTimeUnits = effLines * w->speed; if (((timer / (effLines * w->speed)) % 2) == 0) { /*debug(RPT_DEBUG, "up ");*/ begin = (timer % (effLines * w->speed)) / w->speed; } else { /*debug(RPT_DEBUG, "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; } /*debug(RPT_DEBUG, "rendering begin: %d timer: %d effLines: %d",begin,timer,effLines); */ for (i = begin; i < begin + available_lines; i++) { strncpy (str, &((w->text)[i * (screen_width)]), screen_width); str[screen_width] = '\0'; /*debug(RPT_DEBUG, "rendering: '%s' of %s", */ /*str,w->text); */ drivers_string (w->left, w->top + (i - begin), str); } } } 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 { render_frame (w->frame_screen->widgetlist, w->length, new_left, new_top, new_right, new_bottom, w->width, w->height, w->speed, timer); } } break; case WID_NUM: /* FIXME: doesn't work in frames...*/ /* NOTE: y=10 means COLON (:)*/ if ((w->x > 0) && (w->y >= 0) && (w->y <= 10)) { if (reset) { //drivers_init_num (); reset = 0; } drivers_num (w->x + left, w->y); } break; case WID_NONE: default: break; } } while (LL_Next (list) == 0); return 0; } int server_msg( const char * text, int expire ) { debug( RPT_DEBUG, "%s( text=\"%.40s\", expire=%d )", __FUNCTION__, text, expire ); if( strlen(text) > 15 || expire <= 0 ) { return -1; } /* Still a message active ? */ if( server_msg_expire > 0 ) { free( server_msg_text ); } /* Store new message */ server_msg_text = malloc( strlen(text) + 3 ); strcpy( server_msg_text, "| " ); strcat( server_msg_text, text ); server_msg_expire = expire; return 0; }