Logo Search packages:      
Sourcecode: lcdproc version File versions  Download package

render.c

/*
 * 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
 *               2002, Rene Wagner
 *
 *
 * 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 <string.h>
#include <stdio.h>
#include <stdlib.h>

#include "shared/report.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 heartbeat_state = HEART_OPEN;
int backlight = BACKLIGHT_OPEN;
int backlight_state = BACKLIGHT_OPEN;
int backlight_brightness = DEF_BACKLIGHT_BRIGHTNESS;
int backlight_off_brightness = DEF_BACKLIGHT_OFF_BRIGHTNESS;
int output_state = 0;

static int reset;

#define BUFSIZE 1024

static int draw_frame (LinkedList * 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, tmp_state = 0;
      char *slash_phases = "-\\|/";

      /*debug(RPT_DEBUG, "Render...");*/
      /*return 0;*/

      reset = 1;

      /*debug(RPT_DEBUG, "draw_screen: %8x, %i", (int)s, timer);*/

      if (!s)
            return -1;

      if (s == old_s)
            reset = 0;
      old_s = s;

      /* Clear the LCD screen...*/
      lcd_ptr->clear ();

      /* FIXME lcd_ptr->backlight --
       *
       * This should be in a separate function altogether.
       * Perhaps several: lcd_ptr->backlight_off, lcd_ptr->backlight_on,
       * lcd_ptr->backlight_brightness, lcd_ptr->backlight_flash ...
       */

      /* If the screen's backlight_state isn't set (default) then we
       * inherit the backlight state from the parent client.
       */
      if (s->backlight_state == BACKLIGHT_NOTSET) {
            if (s->parent) tmp_state = s->parent->backlight_state;
      } else {
            /* If the client wants to override settings of its screens
             * we inherit the clients backlight state anyway
             */
            if ((s->parent)&&(s->parent->backlight_state!=BACKLIGHT_OPEN)) {
                  tmp_state = s->parent->backlight_state;
            } else {
                  tmp_state = s->backlight_state;
            }
      }

      /* Set up backlight to the correct state...
       * NOTE: dirty stripping of other options...
       */

      switch (backlight){
            case BACKLIGHT_ON:
                  /*backlight mode "on"*/
                  lcd_ptr->backlight (backlight_brightness);
                  break;
            case BACKLIGHT_OFF:
                  /*backlight mode "off"*/
                  lcd_ptr->backlight (backlight_off_brightness);
                  break;
            default:
                  /* Only in BACKLIGHT_OPEN mode clients are allowed
                   * to change the backlight state.
                   */
                  switch (tmp_state & 1) {
                        case BACKLIGHT_OFF:
                              lcd_ptr->backlight (backlight_off_brightness);
                              break;
                        /* Backlight on (easy)*/
                        case BACKLIGHT_ON:
                              lcd_ptr->backlight (backlight_brightness);
                              break;
                        default:
                              /* Backlight flash: check timer and flip backlight as appropriate*/
                              if (tmp_state & BACKLIGHT_FLASH) {
                                    tmp = (!((timer & 7) == 7));
                                    if (tmp_state & 1)
                                          lcd_ptr->backlight (tmp ? backlight_brightness : backlight_off_brightness);
                                    /*lcd_ptr->backlight(backlight_brightness * (!((timer&7) == 7)));*/
                                    else
                                          lcd_ptr->backlight (!tmp ? backlight_brightness : backlight_off_brightness);
                                    /*lcd_ptr->backlight(backlight_brightness * ((timer&7) == 7));*/

                              /* Backlight blink: check timer and flip backlight as appropriate*/
                              } else if (tmp_state & BACKLIGHT_BLINK) {
                                    tmp = (!((timer & 14) == 14));
                                    if (tmp_state & 1)
                                          lcd_ptr->backlight (tmp ? backlight_brightness : backlight_off_brightness);
                                    /*lcd_ptr->backlight(backlight_brightness * (!((timer&14) == 14)));*/
                                    else
                                          lcd_ptr->backlight (!tmp ? backlight_brightness : backlight_off_brightness);
                                    /*lcd_ptr->backlight(backlight_brightness * ((timer&14) == 14));*/
                              }
                              break;
                  }
                  break;
      }

      /* Output ports from LCD - outputs depend on the current screen*/
      lcd_ptr->output (output_state);

      /* Draw a frame...*/
      draw_frame (s->widgets, 'v', 0, 0, lcd_ptr->wid, lcd_ptr->hgt, s->wid, s->hgt, (((s->duration / s->hgt) < 1) ? 1 : (s->duration / s->hgt)), timer);

      /*debug(RPT_DEBUG, "draw_screen done");*/

      /* Set up heartbeat to the correct state... */

      switch (heartbeat){
            case HEARTBEAT_ON:
                  /* heartbeat mode "on" */
                  lcd_ptr->heartbeat(HEARTBEAT_ON);
                  break;
            case HEARTBEAT_OFF:
                  /* heartbeat mode "off" */
                  /* do nothing */
                  break;
            case HEARTBEAT_SLASH:
                  /* Replace heartbeat with a rotating slash */
                  lcd_ptr->chr (lcd_ptr->wid, 1, slash_phases[timer & 3]);
                  break;
            default:
                  /* Only in HEARTBEAT_OPEN mode clients are allowed
                   * to change the heartbeat state.
                   */
                  switch (s->heartbeat) {
                        case HEARTBEAT_OFF:
                              /* do nothing */
                              break;
                        case HEARTBEAT_ON:
                              /* Heartbeat on */
                              lcd_ptr->heartbeat(HEARTBEAT_ON);
                              break;
                        case HEARTBEAT_SLASH:
                              /* Replace heartbeat with a rotating slash */
                              lcd_ptr->chr (lcd_ptr->wid, 1, slash_phases[timer & 3]);
                              break;
                  }
                  break;
      }

      /* flush display out, frame and all...*/
      lcd_ptr->flush ();

      /*debug(RPT_DEBUG, "draw_screen: %8x, %i", s, timer);*/

      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
draw_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...*/
            int timer)  /* ?*/
{

#define     VerticalScrolling (fscroll == 'v')
#define     HorizontalScrolling (fscroll == 'h')

      static int timer_last = 0;
      int timer_delta = timer - timer_last;
      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 str_length = BUFSIZE-1;

      int reset = 1;

      wid = abs(right - left);              /* This is the size of the visible frame area*/
      if (wid >= BUFSIZE)
            wid=BUFSIZE-1;
      hgt = abs(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;

            /* Make sure the whole frame gets displayed, at least...
             * ...by setting the action to RENDER_HOLD if no other action
             * is currently defined...
             */

            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;
            }

            if (fhgt == 0) { fy = 0; } else { fy %= fhgt; }
            if (fy > fhgt - hgt)
                  fy = fhgt - hgt;

      } else if (HorizontalScrolling) {
            /* TODO:  Frames don't scroll horizontally yet!*/
      }
      /*debug(RPT_DEBUG, "draw_screen: %8x, %i", s, timer);*/

      if (!list)
            return -1;

      /*debug(RPT_DEBUG, "draw_frame: %8x, %i", frame, timer);*/

#define PositiveX(a)    ((a)->x > 0)
#define PositiveY(a)    ((a)->y > 0)
#define ValidPoint(a)   (PositiveX(a) && PositiveY(a))
#define TextPresent(a)  ((a)->text)

      LL_Rewind (list);
      do {
            w = (widget *) LL_Get (list);
            if (!w)
                  return -1;

            // Increment widget timers 
            if (timer_delta < 0)
                  w->timer = 0;
            else
                  w->timer += timer_delta;
                  
            /* TODO:  Make this cleaner and more flexible!*/
            switch (w->type) {
                  case WID_STRING:
                        if (ValidPoint(w) && TextPresent(w)) {
                              if ((w->y <= hgt + fy) && (w->y > fy)) {
                                    if (w->x > wid) w->x=wid;
                                    str_length = abs(wid - w->x + 1);
                                    if (str_length >= BUFSIZE)
                                          str_length = BUFSIZE - 1;
                                    strncpy (str, w->text, str_length);
                                    str[str_length] = 0;
                                    lcd_ptr->string (w->x + left, w->y + top - fy, str);
                              }
                        }
                        break;
                  case WID_HBAR:
                        if (reset) {
                              lcd_ptr->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_ptr->cellwid) < wid - w->x + 1)
                                                lcd_ptr->hbar (w->x + left, w->y + top - fy, w->length);
                                          else
                                                lcd_ptr->hbar (w->x + left, w->y + top - fy, wid * lcd_ptr->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_ptr->init_vbar ();
                              reset = 0;
                        }
                        if ((w->x > 0) && (w->y > 0)) {
                              if (w->length > 0) {
                                    lcd_ptr->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 >= BUFSIZE - 3)
                              length = BUFSIZE - 4;
                        if (length <= wid - 6) {
                              strncpy (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;
                              }
                              str_length = abs(wid - 6);
                              if (str_length >= BUFSIZE - 3)
                                    str_length = BUFSIZE - 4;
                              strncpy (str + 3, w->text + x, str_length);
                              str[str_length + 3] = ' ';
                        }
                        str[wid] = 0;

                        lcd_ptr->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;
                              /*debug(RPT_DEBUG, "rendering: %s %d",w->text,w->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 'm': // Marquee
                                    length = strlen (w->text);
                                    if (length <= screen_width) {
                                          /* it fits within the box, just render it */
                                          lcd_ptr->string (w->left, w->top, w->text);
                                    } else {
                                          int necessaryTimeUnits = 0;
                                          
                                          if (!screenlist_action)
                                                screenlist_action = RENDER_HOLD;
                                          if (w->speed > 0) {
                                                necessaryTimeUnits = length * w->speed;
                                                offset = (w->timer % (length * w->speed)) / w->speed;
                                          } else if (w->speed < 0) {
                                                necessaryTimeUnits = length / (w->speed * -1);
                                                offset = (w->timer % (length / (w->speed * -1))) * w->speed * -1;
                                          } else {
                                                offset = 0;
                                                if (screenlist_action == RENDER_HOLD)
                                                      screenlist_action = 0;
                                          }
                                          if (w->timer > necessaryTimeUnits) {
                                                if (screenlist_action == RENDER_HOLD)
                                                      screenlist_action = 0;
                                          }
                                          if (offset <= length) {
                                                int room = screen_width - (length - offset);

                                                strncpy (str, &w->text[offset], screen_width);

                                                // if there's more room, restart at the beginning
                                                if (room > 0) {
                                                      strncat (str, w->text, room);
                                                }

                                                str[screen_width] = '\0';

                                                /*debug(RPT_DEBUG, "scroller %s : %d", str, length-offset);*/
                                          } else {
                                                str[0] = '\0';
                                          }
                                          lcd_ptr->string (w->left, w->top, str);
                                    }
                                    break;
                              case 'h':
                                    length = strlen (w->text) + 1;
                                    if (length <= screen_width) {
                                          /* it fits within the box, just render it */
                                          lcd_ptr->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 (((w->timer / (effLength * w->speed)) % 2) == 0) {
                                                      /*wiggle one way*/
                                                      offset = (w->timer % (effLength * w->speed))
                                                             / w->speed;
                                                } else {
                                                      /*wiggle the other*/
                                                      offset = (((w->timer % (effLength * w->speed))
                                                                          - (effLength * w->speed) + 1)
                                                                         / w->speed) * -1;
                                                }
                                          } else if (w->speed < 0) {
                                                necessaryTimeUnits = effLength / (w->speed * -1);
                                                if (((w->timer / (effLength / (w->speed * -1))) % 2) == 0) {
                                                      offset = (w->timer % (effLength / (w->speed * -1)))
                                                             * w->speed * -1;
                                                } else {
                                                      offset = (((w->timer % (effLength / (w->speed * -1)))
                                                                          * w->speed * -1) - effLength + 1) * -1;
                                                }
                                          } else {
                                                offset = 0;
                                                if (screenlist_action == RENDER_HOLD)
                                                      screenlist_action = 0;
                                          }
                                          if (w->timer > necessaryTimeUnits) {
                                                if (screenlist_action == RENDER_HOLD)
                                                      screenlist_action = 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';
                                          }
                                          lcd_ptr->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_ptr->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_ptr->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;
                                                      /*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 (((w->timer / (effLines * w->speed)) % 2) == 0) {
                                                                  /*debug(RPT_DEBUG, "up ");*/
                                                                  begin = (w->timer % (effLines * w->speed))
                                                                         / w->speed;
                                                            } else {
                                                                  /*debug(RPT_DEBUG, "down ");*/
                                                                  begin = (((w->timer % (effLines * w->speed))
                                                                                     - (effLines * w->speed) + 1) / w->speed)
                                                                         * -1;
                                                            }
                                                      } else if (w->speed < 0) {
                                                            necessaryTimeUnits = effLines / (w->speed * -1);
                                                            if (((w->timer / (effLines / (w->speed * -1))) % 2) == 0) {
                                                                  begin = (w->timer % (effLines / (w->speed * -1)))
                                                                         * w->speed * -1;
                                                            } else {
                                                                  begin = (((w->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,w->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);*/
                                                            lcd_ptr->string (w->left, w->top + (i - begin), str);
                                                      }
                                                      if (w->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_ptr->init_num ();
                                    reset = 0;
                              }
                              lcd_ptr->num (w->x + left, w->y);
                        }
                        break;
                  case WID_NONE:
                  default:
                        break;
            }
      } while (LL_Next (list) == 0);

      timer_last = timer;
      return 0;
}

Generated by  Doxygen 1.6.0   Back to index