Files
lcdproc/server/render.c
T
ppokorny 00cc9fca23 + Changed protocol to 0.3 because we now accept both -foo and foo
as argument introducers.  in 0.4-pre10, these were changed
   from foo to -foo and this broke many clients.  This change
   should fix that and be backward/forward compatible.
 + Added a noop function to the protocol.  This is useful for writing
   shell script clients of LCDproc.
 + Changed the shared sock_send_string function to NOT send the
   ending NUL ('\0') byte with the string.  This was confusing
   in Perl clients which saw NUL's as the first character of
   each reply.  WARNING: The LCDPROC client depended on this
   behavior, your client may too.  Use the newline instead.
   See the code in clients/lcdproc/main.c (look for "switch(buf[i])")
   to see one way to handle this.  This should allow new clients
   to connect to old servers and vice-versa.
 + Messed with include structure in .c files.  It should no longer
   be necessary to specify ../.. to get to shared code headers.
   Also, since we are using automake, the global config.h can be
   found with just #include "config.h" as it's location is included
   in a -I to the compilers.  As a result "make distcheck" now works.
2001-05-25 03:14:27 +00:00

442 lines
12 KiB
C

/*
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 <string.h>
#include <stdio.h>
#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 == 1) || 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);
}
if ((s->heartbeat == 2) && heartbeat != HEART_OFF) {
char *phases = "-\\|/";
lcd.chr (lcd.wid, 1, phases[timer & 3]);
}
}
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
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)) {
if (w->x > wid) { w->x=wid; };
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, 255, wid);
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 - screen_width;
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;
}
if (offset <= length) {
strncpy (str, &((w->text)[offset]), screen_width);
str[screen_width] = '\0';
//printf("%s : %d\n",str,length-offset);
} else {
str[0] = '\0';
}
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; i < lines_required; i++) {
strncpy (str, &((w->text)[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; i < begin + available_lines; i++) {
strncpy (str, &((w->text)[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...
// NOTE: y=10 means COLON (:)
if ((w->x > 0) && (w->y >= 0) && (w->y <= 10)) {
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;
}