]> git.webhop.me Git - lcd4linux.git/commitdiff
[lcd4linux @ 2004-01-14 11:33:00 by reinelt]
authorreinelt <reinelt@3ae390bd-cb1e-0410-b409-cd5a39f66f1f>
Wed, 14 Jan 2004 11:33:00 +0000 (11:33 +0000)
committerreinelt <reinelt@3ae390bd-cb1e-0410-b409-cd5a39f66f1f>
Wed, 14 Jan 2004 11:33:00 +0000 (11:33 +0000)
new plugin 'uname' which does what it's called
text widget nearly finished
first results displayed on MatrixOrbital

git-svn-id: https://ssl.bulix.org/svn/lcd4linux/trunk@310 3ae390bd-cb1e-0410-b409-cd5a39f66f1f

15 files changed:
Makefile.am
Makefile.in
cfg.c
cfg.h
drv_MatrixOrbital.c
expr.c
layout.c
lcd4linux.c
plugin.c
plugin_cfg.c
plugin_cpuinfo.c
plugin_uname.c [new file with mode: 0644]
widget.c
widget.h
widget_text.c

index 5e2058c4fc860e1c5560f755526b10d9eb7f6c22..3afc2edfa280a68f8a48e23768ece59ef52c546b 100644 (file)
@@ -1,4 +1,4 @@
-## Process this file with automake to produce Makefile.in
+# Process this file with automake to produce Makefile.in
 
 AUTOMAKE_OPTIONS = gnu
 CLEANFILES = *~
@@ -25,11 +25,12 @@ layout.c layout.h \
 timer.c timer.h \
 evaluator.c evaluator.h \
 widget.c widget.h \
-widget_text.c widget_text.h \
+widget_text.c \
 plugin.c plugin.h \
 plugin_math.c \
 plugin_string.c \
 plugin_cfg.c \
+plugin_uname.c \
 plugin_cpuinfo.c \
 plugin_i2c_sensors.c \
 plugin_xmms.c \
index 483130b7675ced68bf0a1ec6cd0f7aeb8ccb60e0..89fcd4a496fad19f1fbdf09f05e482ba87b0cf60 100644 (file)
@@ -10,6 +10,8 @@
 # even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 # PARTICULAR PURPOSE.
 
+# Process this file with automake to produce Makefile.in
+
 
 SHELL = @SHELL@
 
@@ -104,7 +106,7 @@ lcd4linux_LDADD = @DRIVERS@ @DRVLIBS@
 #remove next line for liblcd4linux
 lcd4linux_DEPENDENCIES = @DRIVERS@
 
-lcd4linux_SOURCES =  lcd4linux.c pid.c pid.h hash.c hash.h parser.c parser.h processor.c processor.h layout.c layout.h timer.c timer.h evaluator.c evaluator.h widget.c widget.h widget_text.c widget_text.h plugin.c plugin.h plugin_math.c plugin_string.c plugin_cfg.c plugin_cpuinfo.c plugin_i2c_sensors.c plugin_xmms.c system.c system.h isdn.c isdn.h wifi.c wifi.h mail.c mail.h seti.c seti.h battery.c battery.h dvb.c dvb.h filter.c filter.h exec.c exec.h expr.c expr.h mail2.c socket.c socket.h imon.c imon.h display.c display.h drv.c drv.h debug.c debug.h cfg.c cfg.h lock.c lock.h pixmap.c pixmap.h bar.c bar.h icon.c icon.h fontmap.c fontmap.h udelay.c udelay.h
+lcd4linux_SOURCES =  lcd4linux.c pid.c pid.h hash.c hash.h parser.c parser.h processor.c processor.h layout.c layout.h timer.c timer.h evaluator.c evaluator.h widget.c widget.h widget_text.c plugin.c plugin.h plugin_math.c plugin_string.c plugin_cfg.c plugin_uname.c plugin_cpuinfo.c plugin_i2c_sensors.c plugin_xmms.c system.c system.h isdn.c isdn.h wifi.c wifi.h mail.c mail.h seti.c seti.h battery.c battery.h dvb.c dvb.h filter.c filter.h exec.c exec.h expr.c expr.h mail2.c socket.c socket.h imon.c imon.h display.c display.h drv.c drv.h debug.c debug.h cfg.c cfg.h lock.c lock.h pixmap.c pixmap.h bar.c bar.h icon.c icon.h fontmap.c fontmap.h udelay.c udelay.h
 
 
 #liblcd4linux_la_DEPENDENCIES = @DRIVERS@
@@ -148,7 +150,7 @@ lcd4linux_OBJECTS =  lcd4linux.$(OBJEXT) pid.$(OBJEXT) hash.$(OBJEXT) \
 parser.$(OBJEXT) processor.$(OBJEXT) layout.$(OBJEXT) timer.$(OBJEXT) \
 evaluator.$(OBJEXT) widget.$(OBJEXT) widget_text.$(OBJEXT) \
 plugin.$(OBJEXT) plugin_math.$(OBJEXT) plugin_string.$(OBJEXT) \
-plugin_cfg.$(OBJEXT) plugin_cpuinfo.$(OBJEXT) \
+plugin_cfg.$(OBJEXT) plugin_uname.$(OBJEXT) plugin_cpuinfo.$(OBJEXT) \
 plugin_i2c_sensors.$(OBJEXT) plugin_xmms.$(OBJEXT) system.$(OBJEXT) \
 isdn.$(OBJEXT) wifi.$(OBJEXT) mail.$(OBJEXT) seti.$(OBJEXT) \
 battery.$(OBJEXT) dvb.$(OBJEXT) filter.$(OBJEXT) exec.$(OBJEXT) \
@@ -182,9 +184,9 @@ DEP_FILES =  .deps/BeckmannEgle.P .deps/Crystalfontz.P .deps/Cwlinux.P \
 .deps/mail.P .deps/mail2.P .deps/parport.P .deps/parser.P .deps/pid.P \
 .deps/pixmap.P .deps/plugin.P .deps/plugin_cfg.P .deps/plugin_cpuinfo.P \
 .deps/plugin_i2c_sensors.P .deps/plugin_math.P .deps/plugin_string.P \
-.deps/plugin_xmms.P .deps/processor.P .deps/seti.P .deps/socket.P \
-.deps/system.P .deps/timer.P .deps/udelay.P .deps/widget.P \
-.deps/widget_text.P .deps/wifi.P
+.deps/plugin_uname.P .deps/plugin_xmms.P .deps/processor.P .deps/seti.P \
+.deps/socket.P .deps/system.P .deps/timer.P .deps/udelay.P \
+.deps/widget.P .deps/widget_text.P .deps/wifi.P
 SOURCES = $(lcd4linux_SOURCES) $(EXTRA_lcd4linux_SOURCES)
 OBJECTS = $(lcd4linux_OBJECTS)
 
diff --git a/cfg.c b/cfg.c
index 7e0becf54cc856e6d8d085e5fac53e4a694a3ca0..df7fa7906aeafabd419277f081a5ba3c7d8280fe 100644 (file)
--- a/cfg.c
+++ b/cfg.c
@@ -1,4 +1,4 @@
-/* $Id: cfg.c,v 1.26 2004/01/11 18:26:02 reinelt Exp $^
+/* $Id: cfg.c,v 1.27 2004/01/14 11:33:00 reinelt Exp $^
  *
  * config file stuff
  *
  *
  *
  * $Log: cfg.c,v $
+ * Revision 1.27  2004/01/14 11:33:00  reinelt
+ * new plugin 'uname' which does what it's called
+ * text widget nearly finished
+ * first results displayed on MatrixOrbital
+ *
  * Revision 1.26  2004/01/11 18:26:02  reinelt
  * further widget and layout processing
  *
  *   This list was allocated be cfg_list() and must be 
  *   freed by the caller!
  *
+ * cfg_get_raw (section, key, defval) 
+ *   return the a value for a given key in a given section 
+ *   or <defval> if key does not exist. Does NOT evaluate
+ *   the expression. Therefore used to get the expression 
+ *   itself!
+ *
  * cfg_get (section, key, defval) 
  *   return the a value for a given key in a given section 
- *   or <defval> if key does not exist
+ *   or <defval> if key does not exist. The specified
+ *   value in the config is treated as a expression and 
+ *   is evaluated!
  *
  * cfg_number (section, key, defval, min, int max, *value) 
  *   return the a value for a given key in a given section 
  *   convert it into a number with syntax checking
- *   check if its in a given range
+ *   check if its in a given range. As it uses cfg_get()
+ *   internally, the evaluator is used here, too.
  * 
  */
 
 #include <sys/stat.h>
 
 #include "debug.h"
+#include "evaluator.h"
 #include "cfg.h"
 
 typedef struct {
@@ -361,7 +376,7 @@ char *l4l_cfg_list (char *section)
 }
 
 
-char *l4l_cfg_get (char *section, char *key, char *defval)
+char *l4l_cfg_get_raw (char *section, char *key, char *defval)
 {
   int len;
   char *buffer;
@@ -396,33 +411,52 @@ char *l4l_cfg_get (char *section, char *key, char *defval)
 }
 
 
-int l4l_cfg_number (char *section, char *key, int defval, int min, int max, int *value) 
+char *l4l_cfg_get (char *section, char *key, char *defval)
 {
-  char *s, *e;
+  char *expression;
+  RESULT result = {0, 0.0, NULL};
   
+  expression=cfg_get_raw(section, key, defval);
+  
+  if (expression!=NULL && *expression!='\0') {
+    if (Eval(expression, &result)==0) {
+      return R2S(&result);
+    }
+  }
+  return defval;
+}
+
+
+int l4l_cfg_number (char *section, char *key, int defval, int min, int max, int *value) 
+{
+  char *expression;
+  RESULT result = {0, 0.0, NULL};
+   
   // start with default value
   // in case of an (uncatched) error, you have the
   // default value set, which may be handy...
   *value=defval;
 
-  s=cfg_get(section, key, NULL);
-  if (s==NULL) {
+  expression=cfg_get_raw(section, key, NULL);
+  if (expression==NULL) {
     return 0;
   }
   
-  *value=strtol(s, &e, 0);
-  if (*e!='\0') {
-    error ("bad %s entry '%s' in %s", key, s, cfg_source());
+  if (Eval(expression, &result)!=0) {
     return -1;
   }
+  *value=R2N(&result);
+  DelResult(&result);
   
   if (*value<min) {
-    error ("bad %s value '%s' in %s, minimum is %d", key, s, cfg_source(), min);
+    error ("bad %s value '%d' in %s, minimum is %d", key, *value, cfg_source(), min);
+    *value=min;
     return -1;
   }
   
   if (*value>max) {
-    error ("bad %s value '%s' in %s, maximum is %d", key, s, cfg_source(), max);
+    error ("bad %s value '%d' in %s, maximum is %d", key, *value, cfg_source(), max);
+    *value=max;
     return -1;
   }
 
@@ -637,10 +671,11 @@ char *l4l_cfg_source (void)
 }
 
 
-int   (*cfg_init)   (char *source)                           = l4l_cfg_init;
-char *(*cfg_source) (void)                                   = l4l_cfg_source;
-int   (*cfg_cmd)    (char *arg)                              = l4l_cfg_cmd;
-char *(*cfg_list)   (char *section)                          = l4l_cfg_list;
-char *(*cfg_get)    (char *section, char *key, char *defval) = l4l_cfg_get;
-int   (*cfg_number) (char *section, char *key, int   defval, 
-                    int min, int max, int *value)           = l4l_cfg_number;
+int   (*cfg_init)    (char *source)                           = l4l_cfg_init;
+char *(*cfg_source)  (void)                                   = l4l_cfg_source;
+int   (*cfg_cmd)     (char *arg)                              = l4l_cfg_cmd;
+char *(*cfg_list)    (char *section)                          = l4l_cfg_list;
+char *(*cfg_get_raw) (char *section, char *key, char *defval) = l4l_cfg_get_raw;
+char *(*cfg_get)     (char *section, char *key, char *defval) = l4l_cfg_get;
+int   (*cfg_number)  (char *section, char *key, int   defval, 
+                     int min, int max, int *value)           = l4l_cfg_number;
diff --git a/cfg.h b/cfg.h
index 49895be400cb9411b9eabd4ee632b4a7226ff274..ffd01f695f6a0e334d36319ab6a87676845ac2d2 100644 (file)
--- a/cfg.h
+++ b/cfg.h
@@ -1,4 +1,4 @@
-/* $Id: cfg.h,v 1.8 2004/01/10 20:22:33 reinelt Exp $
+/* $Id: cfg.h,v 1.9 2004/01/14 11:33:00 reinelt Exp $
  *
  * config file stuff
  *
  *
  *
  * $Log: cfg.h,v $
+ * Revision 1.9  2004/01/14 11:33:00  reinelt
+ * new plugin 'uname' which does what it's called
+ * text widget nearly finished
+ * first results displayed on MatrixOrbital
+ *
  * Revision 1.8  2004/01/10 20:22:33  reinelt
  * added new function 'cfg_list()' (not finished yet)
  * added layout.c (will replace processor.c someday)
 #ifndef _CFG_H_
 #define _CFG_H_
 
-extern int   (*cfg_init)   (char *source);
-extern char *(*cfg_source) (void);
-extern int   (*cfg_cmd)    (char *arg);
-extern char *(*cfg_list)   (char *section);
-extern char *(*cfg_get)    (char *section, char *key, char *defval);
-extern int   (*cfg_number) (char *section, char *key, int   defval, 
-                           int min, int max, int *value);
+extern int   (*cfg_init)    (char *source);
+extern char *(*cfg_source)  (void);
+extern int   (*cfg_cmd)     (char *arg);
+extern char *(*cfg_list)    (char *section);
+extern char *(*cfg_get_raw) (char *section, char *key, char *defval);
+extern char *(*cfg_get)     (char *section, char *key, char *defval);
+extern int   (*cfg_number)  (char *section, char *key, int   defval, 
+                            int min, int max, int *value);
 
-int   l4l_cfg_init   (char *file);
-char *l4l_cfg_source (void);
-int   l4l_cfg_cmd    (char *arg);
-char *l4l_cfg_list   (char *section);
-char *l4l_cfg_get    (char *section, char *key, char *defval);
-int   l4l_cfg_number (char *section, char *key, int   defval, 
-                     int min, int max, int *value);
+int   l4l_cfg_init    (char *file);
+char *l4l_cfg_source  (void);
+int   l4l_cfg_cmd     (char *arg);
+char *l4l_cfg_list    (char *section);
+char *l4l_cfg_get_raw (char *section, char *key, char *defval);
+char *l4l_cfg_get     (char *section, char *key, char *defval);
+int   l4l_cfg_number  (char *section, char *key, int   defval, 
+                      int min, int max, int *value);
 
 #endif
index 5ca694ac5810e9aae58ea8964ebe908ab75cd665..2fdf1efe83c96a7c3c0ef90a5d45a38439664358 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: drv_MatrixOrbital.c,v 1.6 2004/01/11 18:26:02 reinelt Exp $
+/* $Id: drv_MatrixOrbital.c,v 1.7 2004/01/14 11:33:00 reinelt Exp $
  *
  * new style driver for Matrix Orbital serial display modules
  *
  *
  *
  * $Log: drv_MatrixOrbital.c,v $
+ * Revision 1.7  2004/01/14 11:33:00  reinelt
+ * new plugin 'uname' which does what it's called
+ * text widget nearly finished
+ * first results displayed on MatrixOrbital
+ *
  * Revision 1.6  2004/01/11 18:26:02  reinelt
  * further widget and layout processing
  *
@@ -75,6 +80,7 @@
 #include "bar.h"
 #include "icon.h"
 #include "widget.h"
+#include "widget_text.h"
 
 
 // these values are hardcoded
@@ -87,11 +93,12 @@ static speed_t Speed;
 static int Device=-1;
 static int Model;
 
-static int ROWS, COLS, GPOS;
-static int ICONS, PROTOCOL;
+static int DROWS, DCOLS; // display size
+static int LROWS, LCOLS; // layout size
+static int GPOS, ICONS, PROTOCOL;
 
-static char *FrameBuffer1=NULL;
-static char *FrameBuffer2=NULL;
+static char *LayoutFB=NULL;
+static char *DisplayFB=NULL;
 static int GPO[8];
 
 
@@ -239,18 +246,27 @@ static void drv_MO_define_char (int ascii, char *buffer)
 }
 
 
+static void drv_MO_goto (int row, int col)
+{
+  char cmd[5]="\376Gyx";
+  cmd[2]=(char)col+1;
+  cmd[3]=(char)row+1;
+  drv_MO_write(cmd,4);
+}
+
+
 static int drv_MO_clear (int full)
 {
   int gpo;
   
-  memset (FrameBuffer1, ' ', ROWS*COLS*sizeof(char));
+  memset (LayoutFB, ' ', LROWS*LCOLS*sizeof(char));
   
   icon_clear();
   bar_clear();
   memset(GPO, 0, sizeof(GPO));
 
   if (full) {
-    memset (FrameBuffer2, ' ', ROWS*COLS*sizeof(char));
+    memset (DisplayFB, ' ', DROWS*DCOLS*sizeof(char));
     switch (PROTOCOL) {
     case 1:
       drv_MO_write ("\014",  1);  // Clear Screen
@@ -273,21 +289,12 @@ static int drv_MO_clear (int full)
   return 0;
 }
 
-static void drv_MO_goto (int row, int col)
-{
-  char cmd[5]="\376Gyx";
-  cmd[2]=(char)col+1;
-  cmd[3]=(char)row+1;
-  drv_MO_write(cmd,4);
-}
-
-
 static int drv_MO_put (int row, int col, char *text)
 {
-  char *p=FrameBuffer1+row*COLS+col;
+  char *p=LayoutFB+row*DCOLS+col;
   char *t=text;
   
-  while (*t && col++<=COLS) {
+  while (*t && col++<=DCOLS) {
     *p++=*t++;
   }
   return 0;
@@ -383,65 +390,62 @@ static int drv_MO_start (char *section)
   // read module type
   drv_MO_write ("\3767", 2);
   usleep(1000);
-  drv_MO_read (buffer, 1);
-  for (i=0; Models[i].type!=0xff; i++) {
-    if (Models[i].type == (int)*buffer) break;
+  if (drv_MO_read (buffer, 1)==1) {
+    for (i=0; Models[i].type!=0xff; i++) {
+      if (Models[i].type == (int)*buffer) break;
+    }
+    info ("MatrixOrbital: display reports model '%s' (type 0x%02x)", 
+         Models[i].name, Models[i].type);
+
+    // auto-dedection
+    if (Model==-1) Model=i;
+
+    // auto-dedection matches specified model?
+    if (Models[i].type!=0xff && Model!=i) {
+      error ("MatrixOrbital: %s.Model '%s' from %s does not match dedected Model '%s'", 
+            section, model, cfg_source(), Models[i].name);
+      return -1;
+    }
+
+  } else {
+    info ("MatrixOrbital: display detection failed.");
   }
-  info ("MatrixOrbital: display identifies itself as a '%s' (type 0x%02x)", 
-       Models[i].name, Models[i].type);
-  
-  // auto-dedection
-  if (Model==-1) Model=i;
   
-  // auto-dedection matches specified model?
-  if (Model!=i) {
-    error ("MatrixOrbital: %s.Model '%s' from %s does not match dedected Model '%s'", 
-          section, model, cfg_source(), Models[i].name);
-    return -1;
-  }
-
   // read serial number
   drv_MO_write ("\3765", 2);
   usleep(100000);
-  drv_MO_read (buffer, 2);
-  info ("MatrixOrbital: display reports serial number 0x%x", *(short*)buffer);
-  
+  if (drv_MO_read (buffer, 2)==2) {
+    info ("MatrixOrbital: display reports serial number 0x%x", *(short*)buffer);
+  }
+
   // read version number
   drv_MO_write ("\3766", 2);
   usleep(100000);
-  drv_MO_read (buffer, 1);
-  info ("MatrixOrbital: display reports firmware version 0x%x", *buffer);
-
+  if (drv_MO_read (buffer, 1)==1) {
+    info ("MatrixOrbital: display reports firmware version 0x%x", *buffer);
+  }
   
   // initialize global variables
-  ROWS     = Models[Model].rows;
-  COLS     = Models[Model].cols;
+  DROWS    = Models[Model].rows;
+  DCOLS    = Models[Model].cols;
   GPOS     = Models[Model].gpos;
   PROTOCOL = Models[Model].protocol;
 
-
-  // init the framebuffers
-  FrameBuffer1 = (char*)malloc(COLS*ROWS*sizeof(char));
-  FrameBuffer2 = (char*)malloc(COLS*ROWS*sizeof(char));
-  if (FrameBuffer1==NULL || FrameBuffer2==NULL) {
-    error ("MatrixOrbital: framebuffer could not be allocated: malloc() failed");
-    return -1;
-  }
-  
   // init Icons
   if (cfg_number(NULL, "Icons", 0, 0, CHARS, &ICONS)<0) return -1;
   if (ICONS>0) {
     debug ("reserving %d of %d user-defined characters for icons", ICONS, CHARS);
-    icon_init(ROWS, COLS, XRES, YRES, CHARS, ICONS, drv_MO_define_char);
+    icon_init(DROWS, DCOLS, XRES, YRES, CHARS, ICONS, drv_MO_define_char);
   }
   
   // init Bars
-  bar_init(ROWS, COLS, XRES, YRES, CHARS-ICONS);
+  bar_init(DROWS, DCOLS, XRES, YRES, CHARS-ICONS);
   bar_add_segment(  0,  0,255, 32); // ASCII  32 = blank
   bar_add_segment(255,255,255,255); // ASCII 255 = block
   
   
-  drv_MO_clear(1);
+  // Fixme: get rid of this function
+  // drv_MO_clear(1);
   // Fixme: where to init contrast?
   drv_MO_contrast(160);
 
@@ -463,19 +467,19 @@ static int drv_MO_flush (void)
   
   bar_process(drv_MO_define_char);
   
-  for (row=0; row<ROWS; row++) {
-    for (col=0; col<COLS; col++) {
+  for (row=0; row<DROWS; row++) {
+    for (col=0; col<DCOLS; col++) {
       c=bar_peek(row, col);
       if (c==-1) c=icon_peek(row, col);
       if (c!=-1) {
-       FrameBuffer1[row*COLS+col]=(char)c;
+       LayoutFB[row*DCOLS+col]=(char)c;
       }
     }
-    for (col=0; col<COLS; col++) {
-      if (FrameBuffer1[row*COLS+col]==FrameBuffer2[row*COLS+col]) continue;
+    for (col=0; col<DCOLS; col++) {
+      if (LayoutFB[row*DCOLS+col]==DisplayFB[row*DCOLS+col]) continue;
       drv_MO_goto (row, col);
-      for (pos1=col++, pos2=pos1, equal=0; col<COLS; col++) {
-       if (FrameBuffer1[row*COLS+col]==FrameBuffer2[row*COLS+col]) {
+      for (pos1=col++, pos2=pos1, equal=0; col<DCOLS; col++) {
+       if (LayoutFB[row*DCOLS+col]==DisplayFB[row*DCOLS+col]) {
          // If we find just one equal byte, we don't break, because this 
          // would require a goto, which takes one byte, too.
          if (++equal>5) break;
@@ -484,11 +488,11 @@ static int drv_MO_flush (void)
          equal=0;
        }
       }
-      drv_MO_write (FrameBuffer1+row*COLS+pos1, pos2-pos1+1);
+      drv_MO_write (LayoutFB+row*DCOLS+pos1, pos2-pos1+1);
     }
   }
   
-  memcpy (FrameBuffer2, FrameBuffer1, ROWS*COLS*sizeof(char));
+  memcpy (DisplayFB, LayoutFB, DROWS*DCOLS*sizeof(char));
   
   switch (PROTOCOL) {
   case 1:
@@ -651,6 +655,88 @@ static void plugin_rpm (RESULT *result, RESULT *arg1)
 }
 
 
+// ****************************************
+// ***    internal helper functions     ***
+// ****************************************
+
+static void drv_MO_resize (int rows, int cols)
+{
+  char *newFB;
+  int row, col;
+  
+  // Layout FB is large enough
+  if (rows<=LROWS && cols<=LCOLS)
+    return;
+  
+  // allocate new Layout FB
+  newFB=malloc(cols*rows*sizeof(char));
+  memset (newFB, ' ', rows*cols*sizeof(char));
+
+  // transfer contents
+  if (LayoutFB!=NULL) {
+    for (row=0; row<LROWS; row++) {
+      for (col=0; col<LCOLS; col++) {
+       newFB[row*cols+col]=LayoutFB[row*LCOLS+col];
+      }
+    }
+    free (LayoutFB);
+  }
+  
+  LayoutFB = newFB;
+  LCOLS    = cols;
+  LROWS    = rows;
+}
+
+
+// ****************************************
+// ***        widget callbacks          ***
+// ****************************************
+
+
+int drv_MO_draw_text (WIDGET *W)
+{
+  WIDGET_TEXT *T=W->data;
+  char *txt, *fb1, *fb2;
+  int row, col, len, end;
+  
+  debug ("drv_MO_draw_text(%s) x=%2d y=%2d  value=<%s>", W->name, W->col, W->row, T->buffer);
+
+  row=W->row;
+  col=W->col;
+  txt=T->buffer;
+  len=strlen(txt);
+  end=col+len;
+  
+  // maybe grow layout framebuffer
+  drv_MO_resize (W->row, W->col+len-1);
+
+  fb1=LayoutFB+row*LCOLS;
+  fb2=DisplayFB+row*DCOLS;
+  
+  // transfer new text into layout buffer
+  memcpy (fb1+col, txt, len);
+
+  for (; col<=end; col++) {
+    int pos1, pos2, equal;
+    if (fb1[col]==fb2[col]) continue;
+    drv_MO_goto (row, col);
+    for (pos1=col, pos2=pos1, col++, equal=0; col<=end; col++) {
+      if (fb1[col]==fb2[col]) {
+       // If we find just one equal byte, we don't break, because this 
+       // would require a goto, which takes several bytes, too.
+       if (++equal>5) break;
+      } else {
+       pos2=col;
+       equal=0;
+      }
+    }
+    memcpy (fb2+pos1, fb1+pos1, pos2-pos1+1);
+    drv_MO_write (fb2+pos1, pos2-pos1+1);
+  }
+    
+  return 0;
+}
+
 
 // ****************************************
 // ***        exported functions        ***
@@ -679,9 +765,25 @@ int drv_MO_init (char *section)
   if ((ret=drv_MO_start (section))!=0)
     return ret;
   
+  // init display framebuffer
+  DisplayFB = (char*)malloc(DCOLS*DROWS*sizeof(char));
+  memset (DisplayFB, ' ', DROWS*DCOLS*sizeof(char));
+  
+  // init layout framebuffer
+  LROWS = 0;
+  LCOLS = 0;
+  LayoutFB=NULL;
+  drv_MO_resize (DROWS, DCOLS);
+
+  // sanity check
+  if (LayoutFB==NULL || DisplayFB==NULL) {
+    error ("MatrixOrbital: framebuffer could not be allocated: malloc() failed");
+    return -1;
+  }
+  
   // register text widget
   wc=Widget_Text;
-  wc.draw=NULL; //Fixme
+  wc.draw=drv_MO_draw_text;
   widget_register(&wc);
   
   // register plugins
@@ -703,14 +805,14 @@ int drv_MO_quit (void) {
   close (Device);
   unlock_port(Port);
   
-  if (FrameBuffer1) {
-    free(FrameBuffer1);
-    FrameBuffer1=NULL;
+  if (LayoutFB) {
+    free(LayoutFB);
+    LayoutFB=NULL;
   }
   
-  if (FrameBuffer2) {
-    free(FrameBuffer2);
-    FrameBuffer2=NULL;
+  if (DisplayFB) {
+    free(DisplayFB);
+    DisplayFB=NULL;
   }
   
   return (0);
diff --git a/expr.c b/expr.c
index 1b1c5b74f5954fc03f3011213dbfda5849f8cddf..09a6c33541963fac160b152c3314e9ccadae6cf8 100644 (file)
--- a/expr.c
+++ b/expr.c
@@ -1,4 +1,4 @@
-/* $Id: expr.c,v 1.3 2004/01/12 03:51:01 reinelt Exp $
+/* $Id: expr.c,v 1.4 2004/01/14 11:33:00 reinelt Exp $
  *
  * expr ('y*') functions
  * This is only a workaround to make the Evaluator usable until
  *
  *
  * $Log: expr.c,v $
+ * Revision 1.4  2004/01/14 11:33:00  reinelt
+ * new plugin 'uname' which does what it's called
+ * text widget nearly finished
+ * first results displayed on MatrixOrbital
+ *
  * Revision 1.3  2004/01/12 03:51:01  reinelt
  * evaluating the 'Variables' section in the config file
  *
@@ -63,7 +68,7 @@ int Expr(int index, char string[EXPR_TXT_LEN], double *val)
 
   sprintf(yn, "y%d", index);
   expression = cfg_get(NULL, yn, NULL);
-                                           
+  
   if (!expression || !*expression) {
     error("Empty expression for 'y%d'", index);
     errs[index]++;
index faa54ee95595b9395477cbe82f0d73e87f103a4d..4f25a7dba583a82c5b10b645b3b751668befdb61 100644 (file)
--- a/layout.c
+++ b/layout.c
@@ -1,4 +1,4 @@
-/* $Id: layout.c,v 1.5 2004/01/13 08:18:19 reinelt Exp $
+/* $Id: layout.c,v 1.6 2004/01/14 11:33:00 reinelt Exp $
  *
  * new layouter framework
  *
  *
  *
  * $Log: layout.c,v $
+ * Revision 1.6  2004/01/14 11:33:00  reinelt
+ * new plugin 'uname' which does what it's called
+ * text widget nearly finished
+ * first results displayed on MatrixOrbital
+ *
  * Revision 1.5  2004/01/13 08:18:19  reinelt
  * timer queues added
  * liblcd4linux deactivated turing transformation to new layout
@@ -68,8 +73,7 @@
 int layout_addItem (char *name, int row, int col)
 {
   // allocate widget
-  widget_add (name);
-
+  widget_add (name, row-1, col-1);
   return 0;
 }
 
index 0d6290f79c4118ff377248b56075c0d7755203b7..76d1f55f6adc283f1aa6b7c99523658c239aa832 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: lcd4linux.c,v 1.60 2004/01/13 08:18:19 reinelt Exp $
+/* $Id: lcd4linux.c,v 1.61 2004/01/14 11:33:00 reinelt Exp $
  *
  * LCD4Linux
  *
  *
  *
  * $Log: lcd4linux.c,v $
+ * Revision 1.61  2004/01/14 11:33:00  reinelt
+ * new plugin 'uname' which does what it's called
+ * text widget nearly finished
+ * first results displayed on MatrixOrbital
+ *
  * Revision 1.60  2004/01/13 08:18:19  reinelt
  * timer queues added
  * liblcd4linux deactivated turing transformation to new layout
@@ -591,7 +596,7 @@ int main (int argc, char *argv[])
 
   // check the conf to see if quiet startup is wanted 
   if (!quiet) {
-    quiet = atoi(cfg_get(NULL, "Quiet", "0"));
+    cfg_number(NULL, "Quiet", 0, 0, 1, &quiet);
   }
   
   // Fixme: compatibility only...
index 3304a00bfcd7828e5ea24668c8f3e14820291253..6328ea93d5135c826e772281ea3647d0d59a015d 100644 (file)
--- a/plugin.c
+++ b/plugin.c
@@ -1,4 +1,4 @@
-/* $Id: plugin.c,v 1.10 2004/01/13 10:03:01 reinelt Exp $
+/* $Id: plugin.c,v 1.11 2004/01/14 11:33:00 reinelt Exp $
  *
  * plugin handler for the Evaluator
  *
  *
  *
  * $Log: plugin.c,v $
+ * Revision 1.11  2004/01/14 11:33:00  reinelt
+ * new plugin 'uname' which does what it's called
+ * text widget nearly finished
+ * first results displayed on MatrixOrbital
+ *
  * Revision 1.10  2004/01/13 10:03:01  reinelt
  * new util 'hash' for associative arrays
  * new plugin 'cpuinfo'
 int plugin_init_math (void);
 int plugin_init_string (void);
 int plugin_init_cfg (void);
+int plugin_init_uname (void);
 int plugin_init_cpuinfo (void);
 int plugin_init_i2c_sensors (void);
 int plugin_init_xmms (void);
@@ -111,6 +117,7 @@ int plugin_init (void)
   plugin_init_math();
   plugin_init_string();
   plugin_init_cfg();
+  plugin_init_uname();
   plugin_init_cpuinfo();
   // MR: segfaults here
   // plugin_init_i2c_sensors();
index 0b1a2294fb705d237f1912eb05365508ea69becf..de1038566f562b1105ba43fc72db58140e6bbf2b 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: plugin_cfg.c,v 1.1 2004/01/13 10:03:01 reinelt Exp $
+/* $Id: plugin_cfg.c,v 1.2 2004/01/14 11:33:00 reinelt Exp $
  *
  * plugin for config file access
  *
  *
  *
  * $Log: plugin_cfg.c,v $
+ * Revision 1.2  2004/01/14 11:33:00  reinelt
+ * new plugin 'uname' which does what it's called
+ * text widget nearly finished
+ * first results displayed on MatrixOrbital
+ *
  * Revision 1.1  2004/01/13 10:03:01  reinelt
  * new util 'hash' for associative arrays
  * new plugin 'cpuinfo'
@@ -62,7 +67,7 @@ static void load_variables (void)
     if (strchr(key, '.')!=NULL || strchr(key, ':') !=0) {
       error ("ignoring variable '%s' from %s: structures not allowed", key, cfg_source());
     } else {
-      expression=cfg_get (section, key, "");
+      expression=cfg_get_raw (section, key, "");
       if (expression!=NULL && *expression!='\0') {
        if (Eval(expression, &result)==0) {
          debug ("Variable %s = '%s' (%f)", key, R2S(&result), R2N(&result));
index 031429f7eee20fc8c729fb77481ac5171a2194f4..60b0880ab20f78d69e664b02641cec438c392ca5 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: plugin_cpuinfo.c,v 1.1 2004/01/13 10:03:01 reinelt Exp $
+/* $Id: plugin_cpuinfo.c,v 1.2 2004/01/14 11:33:00 reinelt Exp $
  *
  * plugin for /proc/cpuinfo parsing
  *
  *
  *
  * $Log: plugin_cpuinfo.c,v $
+ * Revision 1.2  2004/01/14 11:33:00  reinelt
+ * new plugin 'uname' which does what it's called
+ * text widget nearly finished
+ * first results displayed on MatrixOrbital
+ *
  * Revision 1.1  2004/01/13 10:03:01  reinelt
  * new util 'hash' for associative arrays
  * new plugin 'cpuinfo'
@@ -67,7 +72,8 @@ static void my_cpuinfo (RESULT *result, RESULT *arg1)
     stream=fopen("/proc/cpuinfo", "r");
     if (stream==NULL) {
       error ("fopen(/proc/cpuinfo) failed: %s", strerror(errno));
-      goto error;
+      SetResult(&result, R_STRING, ""); 
+      return;
     }
     
     while (!feof(stream)) {
@@ -91,9 +97,7 @@ static void my_cpuinfo (RESULT *result, RESULT *arg1)
       hash_set (&CPUinfo, key, val);
       
     }
-    
     fclose (stream);
-          
     time(&now);
   }
   
@@ -101,8 +105,6 @@ static void my_cpuinfo (RESULT *result, RESULT *arg1)
   val=hash_get(&CPUinfo, key);
   if (val==NULL) val="";
 
- error:
-  // val="";
   SetResult(&result, R_STRING, val); 
 }
 
diff --git a/plugin_uname.c b/plugin_uname.c
new file mode 100644 (file)
index 0000000..95ae17e
--- /dev/null
@@ -0,0 +1,95 @@
+/* $Id: plugin_uname.c,v 1.1 2004/01/14 11:33:00 reinelt Exp $
+ *
+ * plugin for uname() syscall
+ *
+ * Copyright 2003 Michael Reinelt <reinelt@eunet.at>
+ * Copyright 2004 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net>
+ *
+ * This file is part of LCD4Linux.
+ *
+ * LCD4Linux 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.
+ *
+ * LCD4Linux 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * $Log: plugin_uname.c,v $
+ * Revision 1.1  2004/01/14 11:33:00  reinelt
+ * new plugin 'uname' which does what it's called
+ * text widget nearly finished
+ * first results displayed on MatrixOrbital
+ *
+ */
+
+/* 
+ * exported functions:
+ *
+ * int plugin_init_uname (void)
+ *  adds uname() functions
+ *
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/utsname.h>
+
+#include "debug.h"
+#include "plugin.h"
+
+
+
+static void my_uname (RESULT *result, RESULT *arg1)
+{
+  struct utsname utsbuf;
+  char *key, *value;
+  
+  key=R2S(arg1);
+  
+  if (uname(&utsbuf)!=0) {
+    error("uname() failed: %s", strerror(errno));
+    SetResult(&result, R_STRING, ""); 
+    return;
+  }
+
+  if (strcasecmp(key, "sysname")==0) {
+    value=utsbuf.sysname;
+  } else if  (strcasecmp(key, "nodename")==0) {
+    value=utsbuf.nodename;
+  } else if  (strcasecmp(key, "nodename")==0) {
+    value=utsbuf.nodename;
+  } else if  (strcasecmp(key, "release")==0) {
+    value=utsbuf.release;
+  } else if  (strcasecmp(key, "version")==0) {
+    value=utsbuf.version;
+  } else if  (strcasecmp(key, "machine")==0) {
+    value=utsbuf.machine;
+#ifdef _GNU_SOURCE
+  } else if  (strcasecmp(key, "domainname")==0) {
+    value=utsbuf.domainname;
+#endif
+  } else {
+    error("uname: unknown field '%s'", key);
+    value="";
+  }
+
+  SetResult(&result, R_STRING, value); 
+}
+
+
+int plugin_init_uname (void)
+{
+  AddFunction ("uname",   1, my_uname);
+  return 0;
+}
+
index 57a4b5b96c616f76ceffa58c69030da5400e7db5..b646e38ed0a7b8a26a3b706f636793fe9921811c 100644 (file)
--- a/widget.c
+++ b/widget.c
@@ -1,4 +1,4 @@
-/* $Id: widget.c,v 1.7 2004/01/13 08:18:20 reinelt Exp $
+/* $Id: widget.c,v 1.8 2004/01/14 11:33:00 reinelt Exp $
  *
  * generic widget handling
  *
  *
  *
  * $Log: widget.c,v $
+ * Revision 1.8  2004/01/14 11:33:00  reinelt
+ * new plugin 'uname' which does what it's called
+ * text widget nearly finished
+ * first results displayed on MatrixOrbital
+ *
  * Revision 1.7  2004/01/13 08:18:20  reinelt
  * timer queues added
  * liblcd4linux deactivated turing transformation to new layout
@@ -106,7 +111,7 @@ int widget_register (WIDGET_CLASS *widget)
 }
 
 
-int widget_add (char *name)
+int widget_add (char *name, int row, int col)
 {
   int i;
   char *section;
@@ -125,6 +130,7 @@ int widget_add (char *name)
   class=cfg_get(section, "class", NULL);
   if (class==NULL || *class=='\0') {
     error ("error: widget '%s' has no class!", name);
+    if (class) free (class);
     return -1;
   }
   
@@ -136,6 +142,7 @@ int widget_add (char *name)
       break;
     }
   }
+  if (class) free (class);
   if (i==nClasses) {
     error ("widget '%s': class '%s' not supported");
     return -1;
@@ -162,6 +169,8 @@ int widget_add (char *name)
   
   Widget->name  = name;
   Widget->class = Class;
+  Widget->row   = row;
+  Widget->col   = col;
   
   if (Class->init!=0) {
     Class->init(Widget);
index 95a85869f06381d6dc810d3d8c412e662093b94d..c1b2785fa22f648bade029aa9807c3b0ad8b146f 100644 (file)
--- a/widget.h
+++ b/widget.h
@@ -1,4 +1,4 @@
-/* $Id: widget.h,v 1.6 2004/01/13 08:18:20 reinelt Exp $
+/* $Id: widget.h,v 1.7 2004/01/14 11:33:00 reinelt Exp $
  *
  * generic widget handling
  *
  *
  *
  * $Log: widget.h,v $
+ * Revision 1.7  2004/01/14 11:33:00  reinelt
+ * new plugin 'uname' which does what it's called
+ * text widget nearly finished
+ * first results displayed on MatrixOrbital
+ *
  * Revision 1.6  2004/01/13 08:18:20  reinelt
  * timer queues added
  * liblcd4linux deactivated turing transformation to new layout
@@ -59,7 +64,6 @@ struct WIDGET; // forward declaration
 typedef struct WIDGET_CLASS {
   char *name;
   int (*init)   (struct WIDGET *Self);
-  int (*update) (struct WIDGET *Self); // Fixme: do we really need this?
   int (*draw)   (struct WIDGET *Self);
   int (*quit)   (struct WIDGET *Self);
 } WIDGET_CLASS;
@@ -68,16 +72,14 @@ typedef struct WIDGET_CLASS {
 typedef struct WIDGET{
   char         *name;
   WIDGET_CLASS *class;
+  int           row;
+  int           col;
   void         *data;
 } WIDGET;
 
 
 
 int widget_register (WIDGET_CLASS *widget);
-int widget_add      (char *name);
-
-
-// some basic widgets
-WIDGET_CLASS Widget_Text;
+int widget_add      (char *name, int row, int col);
 
 #endif
index 4fc1ef8d7cc1d5384a33501fcb5d07cd53f40ad5..95401edd526c3d37636db1aa2269345a525c6068 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: widget_text.c,v 1.3 2004/01/13 08:18:20 reinelt Exp $
+/* $Id: widget_text.c,v 1.4 2004/01/14 11:33:00 reinelt Exp $
  *
  * simple text widget handling
  *
  *
  *
  * $Log: widget_text.c,v $
+ * Revision 1.4  2004/01/14 11:33:00  reinelt
+ * new plugin 'uname' which does what it's called
+ * text widget nearly finished
+ * first results displayed on MatrixOrbital
+ *
  * Revision 1.3  2004/01/13 08:18:20  reinelt
  * timer queues added
  * liblcd4linux deactivated turing transformation to new layout
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <ctype.h>
 
 #include "debug.h"
 #include "cfg.h"
+#include "evaluator.h"
 #include "timer.h"
 #include "widget.h"
+#include "widget_text.h"
 
 
-typedef struct WIDGET_TEXT {
-  char *value;
-  char *align;
-  int   width;
-  int   update;
-} WIDGET_TEXT;
+void widget_text_scroll (void *Self)
+{
+  WIDGET      *W = (WIDGET*)Self;
+  WIDGET_TEXT *T = W->data;
+  
+  int num, len, pad;
+  char *src, *dst;
+
+  src=T->value;
+  dst=T->buffer;
+  num=0;
+  len=strlen(T->value);
 
+  switch (T->align) {
+  case ALIGN_LEFT:
+    pad=0;
+    break;
+  case ALIGN_CENTER:
+    pad=(T->width - len)/2;  
+    if (pad<0) pad=0;
+    break;
+  case ALIGN_RIGHT:
+    pad=T->width - len; 
+    if (pad<0) pad=0;
+    break;
+  case ALIGN_MARQUEE:
+    pad=T->width - T->scroll;
+    T->scroll++;
+    if (T->scroll >= T->width+len) T->scroll=0;
+    break;
+  default: // not reached 
+    pad=0;
+  }
+  
+  // pad blanks on the beginning
+  while (pad>0) {
+    *(dst++)=' ';
+    num++;
+    pad--;
+  }
+  
+  // overread src (marquee)
+  while (pad<0) {
+    src++;
+    pad++;
+  }
+  
+  // copy content
+  while (num < T->width) {
+    if (*src=='\0') break;
+    *(dst++)=*(src++);
+    num++;
+  }
+  
+  // pad blanks on the end
+  while (num < T->width) {
+    *(dst++)=' ';
+    num++;
+  }
+  *dst='\0';
+  
+  // finally, draw it!
+  if (W->class->draw)
+    W->class->draw(W);
+}
+  
 
-void widget_text_callback (void *vSelf)
+void widget_text_update (void *Self)
 {
-  WIDGET *Self=(WIDGET*)vSelf;
+  WIDGET      *W = (WIDGET*)Self;
+  WIDGET_TEXT *T = W->data;
+  RESULT result = {0, 0.0, NULL};
+  char *value;
   
-   debug ("callback: Self->name=%s", Self->name);
+  // evaluate expression
+  Eval(T->expression, &result);
+  
+  // string or number?
+  if (T->precision==0xC0DE) {
+    value=strdup(R2S(&result));
+  } else {
+    double number=R2N(&result);
+    int width=T->width;
+    int precision=T->precision;
+    // print zero bytes so we can specify NULL as target 
+    // and get the length of the resulting string
+    int size=snprintf (NULL, 0, "%.*f", precision, number);
+    // number does not fit into field width: try to reduce precision
+    if (size>width && precision>0) {
+      int delta=size-width;
+      if (delta>precision) delta=precision;
+      precision-=delta;
+      size-=delta;
+      // zero precision: omit decimal point, too
+      if (precision==0) size--;
+    }
+    // number still doesn't fit: display '*****' 
+    if (size>width) {
+      debug ("overflow");
+      value=malloc(width+1);
+      memset (value, '*', width);
+      *(value+width)='\0';
+    } else {
+      value=malloc(size+1);
+      snprintf (value, size+1, "%.*f", precision, number);
+    }
+  }
+  
+  // has value changed?
+  if (T->value == NULL || strcmp(T->value, value)!=0) {
+    // free old value
+    if (T->value) free (T->value);
+    T->value=value;
+    // reset marquee counter
+    T->scroll=0;
+    // if there's a marquee scroller active, it has its own
+    // update callback timer, so we do nothing here; otherwise
+    // we simply call this scroll callback directly
+    if (T->align!=ALIGN_MARQUEE) {
+      widget_text_scroll (Self);
+    }
+  } else {
+    // value is the same, so free our buffer
+    free (value);
+  }
+
+  DelResult (&result);
 }
 
 
-int widget_text_init (WIDGET *Self) {
-  
-  char *section;
-  WIDGET_TEXT *data;
+int widget_text_init (WIDGET *Self) 
+{
+  char *section; char *c;
+  WIDGET_TEXT *T;
   
   // prepare config section
   // strlen("Widget:")=7
@@ -83,20 +205,65 @@ int widget_text_init (WIDGET *Self) {
   strcpy(section, "Widget:");
   strcat(section, Self->name);
   
-  data=malloc(sizeof(WIDGET_TEXT));
+  T=malloc(sizeof(WIDGET_TEXT));
+  memset (T, 0, sizeof(WIDGET_TEXT));
+
+  // get raw expression (we evaluate it ourselves)
+  T->expression = cfg_get_raw (section, "expression",  "''");
   
-  data->value = cfg_get (section, "value",  "''");
-  data->align = cfg_get (section, "align",  "L");
+  // field width, default 10
+  cfg_number (section, "width", 10,  0, 99999, &(T->width));
   
-  cfg_number (section, "width",   5,  0, 99999, &(data->width));
-  cfg_number (section, "update", -1, -1, 99999, &(data->update));
+  // precision: number of digits after the decimal point (default: none)
+  // Note: this is the *maximum* precision on small values,
+  // for larger values the precision may be reduced to fit into the field width.
+  // The default value 0xC0DE is used to distinguish between numbers and strings:
+  // if no precision is given, the result is always treated as a string. If a
+  // precision is specified, the result is treated as a number.
+  cfg_number (section, "precision", 0xC0DE, 0, 80, &(T->precision));
   
-  free (section);
-  Self->data=data;
+  // field alignment: Left (default), Center, Right or Marquee
+  c = cfg_get (section, "align",  "L");
+  switch (toupper(*c)) {
+  case 'L':
+    T->align=ALIGN_LEFT;
+    break;
+  case 'C':
+    T->align=ALIGN_CENTER;
+    break;
+  case 'R':
+    T->align=ALIGN_RIGHT;
+    break;
+  case 'M':
+    T->align=ALIGN_MARQUEE;
+    break;
+  default:
+    error ("widget %s has unknown alignment '%s', using 'Left'", section, c);
+    T->align=ALIGN_LEFT;
+  }
+  free (c);
+  
+  // update interval (msec), default 1 sec
+  cfg_number (section, "update", 1000, 10, 99999, &(T->update));
   
-  debug ("timer_add: Self=%p Self->name=%s", Self, Self->name);
-  timer_add (widget_text_callback, Self, 200, 0);
+  // marquee scroller speed: interval (msec), default 500msec
+  if (T->align==ALIGN_MARQUEE) {
+    cfg_number (section, "speed", 500, 10, 99999, &(T->speed));
+  }
   
+  // buffer
+  T->buffer=malloc(T->width+1);
+  
+  free (section);
+  Self->data=T;
+  
+  timer_add (widget_text_update, Self, T->update, 0);
+
+  // a marquee scroller has its own timer and callback
+  if (T->align==ALIGN_MARQUEE) {
+    timer_add (widget_text_scroll, Self, T->speed, 0);
+  }
+
   return 0;
 }
 
@@ -117,7 +284,6 @@ int widget_text_quit (WIDGET *Self) {
 WIDGET_CLASS Widget_Text = {
   name:   "text",
   init:   widget_text_init,
-  update: NULL,
   draw:   NULL,
   quit:   widget_text_quit,
 };