Initial import
diff --git a/contiki/ctk/ctk.c b/contiki/ctk/ctk.c
new file mode 100644
index 0000000..2c70085
--- /dev/null
+++ b/contiki/ctk/ctk.c
@@ -0,0 +1,1120 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the "ctk" console GUI toolkit for cc65
+ *
+ * $Id: ctk.c,v 1.1 2003/03/19 14:13:34 adamdunkels Exp $
+ *
+ */
+
+#include "ek.h"
+#include "dispatcher.h"
+#include "ctk.h"
+#include "ctk-draw.h"
+#include "ctk-conf.h"
+
+static unsigned char height, width;
+
+static unsigned char mode;
+
+static struct ctk_window desktop_window;
+static struct ctk_window *windows;
+static struct ctk_window *dialog;
+
+#if CTK_CONF_MENUS
+static struct ctk_menus menus;
+static struct ctk_menu *lastmenu;
+static struct ctk_menu desktopmenu;
+#endif /* CTK_CONF_MENUS */
+
+#ifndef NULL
+#define NULL (void *)0
+#endif /* NULL */
+
+
+#define REDRAW_NONE         0
+#define REDRAW_ALL          1
+#define REDRAW_FOCUS        2
+#define REDRAW_WIDGETS      4
+#define REDRAW_MENUS        8
+#define REDRAW_MENUPART     16
+
+#define MAX_REDRAWWIDGETS 4
+static unsigned char redraw;
+static struct ctk_widget *redraw_widgets[MAX_REDRAWWIDGETS];
+static unsigned char redraw_widgetptr;
+static unsigned char maxnitems;
+
+static unsigned char iconx, icony;
+#define ICONX_START  (width - 5)
+#define ICONY_START  0
+#define ICONX_DELTA  -8
+#define ICONY_DELTA  5
+#define ICONY_MAX    (height - 4)
+
+static void idle(void);
+static void sighandler(ek_signal_t s, ek_data_t data);
+static struct dispatcher_proc p =
+  {DISPATCHER_PROC("CTK Contiki GUI", idle, sighandler, NULL)};
+static ek_id_t ctkid;
+
+
+ek_signal_t ctk_signal_keypress,
+  ctk_signal_timer,
+  ctk_signal_button_activate,
+  ctk_signal_button_hover,
+  ctk_signal_hyperlink_activate,
+  ctk_signal_hyperlink_hover,
+  ctk_signal_menu_activate,
+  ctk_signal_window_close;
+		       
+
+static unsigned short screensaver_timer;
+#define SCREENSAVER_TIMEOUT (5*60)
+
+#if CTK_CONF_MENUS
+/*-----------------------------------------------------------------------------------*/
+/* make_desktopmenu(void)
+ *
+ * Creates the leftmost menu, "Desktop". Since the desktop menu
+ * contains the list of all open windows, this function will be called
+ * whenever a window is opened or closed.
+ */
+static void
+make_desktopmenu(void)
+{
+  struct ctk_window *w;
+  
+  desktopmenu.nitems = 0;
+  
+  if(windows == NULL) {
+    ctk_menuitem_add(&desktopmenu, "(No windows)");
+  } else {
+    for(w = windows; w != NULL; w = w->next) {
+      ctk_menuitem_add(&desktopmenu, w->title);
+    }
+  }
+}
+#endif /* CTK_CONF_MENUS */
+/*-----------------------------------------------------------------------------------*/
+/* ctk_init(void)
+ *
+ * Initializes CTK. Must be called before any other CTK function.
+ */
+void
+ctk_init(void)
+{
+  ctkid = dispatcher_start(&p);
+  
+  windows = NULL;
+  dialog = NULL;
+
+#if CTK_CONF_MENUS 
+  ctk_menu_new(&desktopmenu, "Desktop");
+  make_desktopmenu();
+  menus.menus = menus.desktopmenu = &desktopmenu;
+#endif /* CTK_CONF_MENUS */
+
+  ctk_draw_init();
+
+  height = ctk_draw_height();
+  width = ctk_draw_width();
+
+  desktop_window.active = NULL;
+
+
+  ctk_signal_keypress = dispatcher_sigalloc();
+  ctk_signal_timer = dispatcher_sigalloc();
+  ctk_signal_button_activate = dispatcher_sigalloc();
+  ctk_signal_button_hover = dispatcher_sigalloc();
+  ctk_signal_hyperlink_activate = dispatcher_sigalloc();
+  ctk_signal_hyperlink_hover = dispatcher_sigalloc();
+  ctk_signal_menu_activate = dispatcher_sigalloc();
+  ctk_signal_window_close = dispatcher_sigalloc();
+  
+  dispatcher_listen(ctk_signal_timer);
+  dispatcher_timer(ctk_signal_timer, NULL, CLK_TCK);
+
+  mode = CTK_MODE_NORMAL;
+
+  iconx = ICONX_START;
+  icony = ICONY_START;
+  
+}
+/*-----------------------------------------------------------------------------------*/
+/* void ctk_mode_set()
+ */
+void
+ctk_mode_set(unsigned char m) {
+  mode = m;
+}
+/*-----------------------------------------------------------------------------------*/
+unsigned char
+ctk_mode_get(void) {
+  return mode;
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_icon_add(register struct ctk_widget *icon,
+	     ek_id_t id)
+{
+#if CTK_CONF_ICONS
+  icon->x = iconx;
+  icon->y = icony;
+  icon->widget.icon.owner = id;
+
+  icony += ICONY_DELTA;
+  if(icony >= ICONY_MAX) {
+    icony = ICONY_START;
+    iconx += ICONX_DELTA;
+  }
+  
+  ctk_widget_add(&desktop_window, icon);
+#endif /* CTK_CONF_ICONS */
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_dialog_open(struct ctk_window *d)
+{
+  dialog = d;
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_dialog_close(void)
+{
+  dialog = NULL;
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_window_open(register struct ctk_window *w)
+{
+  struct ctk_window *w2;
+  
+  /* Check if already open. */
+  for(w2 = windows; w2 != w && w2 != NULL; w2 = w2->next);
+  if(w2 == NULL) {
+   /* Not open, so we add it at the head of the list of open
+       windows. */
+    w->next = windows;
+    if(windows != NULL) {
+      windows->prev = w;
+    }
+    windows = w;
+    w->prev = NULL;
+  } else {
+    /* Window already open, so we move it to the front of the windows
+       list. */
+    if(w != windows) {
+      if(w->next != NULL) {
+	w->next->prev = w->prev;
+      }
+      if(w->prev != NULL) {
+	w->prev->next = w->next;
+      }
+      w->next = windows;
+      windows->prev = w;
+      windows = w;
+      w->prev = NULL;
+    }
+  }
+  
+#if CTK_CONF_MENUS
+  /* Recreate the Desktop menu's window entries.*/
+  make_desktopmenu();
+#endif /* CTK_CONF_MENUS */
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_window_close(struct ctk_window *w)
+{
+  struct ctk_window *w2;
+
+  if(w == NULL) {
+    return;
+  }
+  
+  /* Check if the window to be closed is the first window on the
+     list. */
+  if(w == windows) {
+    windows = w->next;
+    if(windows != NULL) {
+      windows->prev = NULL;
+    }
+    w->next = w->prev = NULL;
+  } else {
+    /* Otherwise we step through the list until we find the window
+       before the one to be closed. We then redirect its ->next
+       pointer and its ->next->prev. */
+    for(w2 = windows; w2->next != w; w2 = w2->next);
+
+    if(w->next != NULL) {
+      w->next->prev = w->prev;
+    }
+    w2->next = w->next;
+    
+    w->next = w->prev = NULL;
+  }
+  
+#if CTK_CONF_MENUS
+  /* Recreate the Desktop menu's window entries.*/
+  make_desktopmenu();
+#endif /* CTK_CONF_MENUS */
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+make_windowbuttons(register struct ctk_window *window)
+{
+#if CTK_CONF_WINDOWMOVE
+  CTK_BUTTON_NEW(&window->titlebutton, 0, -1, window->titlelen, window->title);
+#else
+  CTK_LABEL_NEW(&window->titlebutton, 0, -1, window->titlelen, 1, window->title);
+#endif /* CTK_CONF_WINDOWMOVE */
+  CTK_WIDGET_ADD(window, &window->titlebutton);
+
+
+#if CTK_CONF_WINDOWCLOSE
+  CTK_BUTTON_NEW(&window->closebutton, window->w - 3, -1, 1, "x");
+#else
+  CTK_LABEL_NEW(&window->closebutton, window->w - 4, -1, 3, 1, "   ");
+#endif /* CTK_CONF_WINDOWCLOSE */  
+  CTK_WIDGET_ADD(window, &window->closebutton);
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_window_clear(struct ctk_window *window)
+{
+  window->active = window->inactive = window->focused = NULL;
+  
+  make_windowbuttons(window);
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_menu_add(struct ctk_menu *menu)
+{
+#if CTK_CONF_MENUS
+  struct ctk_menu *m;
+
+  if(lastmenu == NULL) {
+    lastmenu = menu;
+  }
+    
+  for(m = menus.menus; m->next != NULL; m = m->next) {
+    if(m == menu) {
+      return;
+    }
+  }
+  m->next = menu;
+  menu->next = NULL;
+#endif /* CTK_CONF_MENUS */
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_menu_remove(struct ctk_menu *menu)
+{
+#if CTK_CONF_MENUS
+  struct ctk_menu *m;
+
+  for(m = menus.menus; m->next != NULL; m = m->next) {
+    if(m->next == menu) {
+      m->next = menu->next;
+      return;
+    }
+  }
+#endif /* CTK_CONF_MENUS */
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+do_redraw_all(unsigned char clipy1, unsigned char clipy2)
+{
+  struct ctk_window *w;
+  struct ctk_widget *widget;
+
+  if(mode != CTK_MODE_NORMAL &&
+     mode != CTK_MODE_WINDOWMOVE) {
+    return;
+  }
+  
+  ctk_draw_clear(clipy1, clipy2);
+
+  /* Draw widgets in root window */
+  for(widget = desktop_window.active;
+      widget != NULL; widget = widget->next) {
+    ctk_draw_widget(widget, 0, clipy1, clipy2);
+  }
+  
+  /* Draw windows */
+  if(windows != NULL) {
+    /* Find the last window.*/
+    for(w = windows; w->next != NULL; w = w->next);
+
+    /* Draw the windows from back to front. */
+    for(; w != windows; w = w->prev) {
+      ctk_draw_clear_window(w, 0, clipy1, clipy2);
+      ctk_draw_window(w, 0, clipy1, clipy2);
+    }
+    /* Draw focused window */
+    ctk_draw_clear_window(windows, CTK_FOCUS_WINDOW, clipy1, clipy2);
+    ctk_draw_window(windows, CTK_FOCUS_WINDOW, clipy1, clipy2);
+  }
+
+  /* Draw dialog (if any) */
+  if(dialog != NULL) {
+    ctk_draw_dialog(dialog);
+  }
+
+#if CTK_CONF_MENUS
+  ctk_draw_menus(&menus);
+#endif /* CTK_CONF_MENUS */
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_redraw(void)
+{
+  if(DISPATCHER_CURRENT() == ctkid) {
+    if(mode == CTK_MODE_NORMAL ||
+       mode == CTK_MODE_WINDOWMOVE) {
+      do_redraw_all(1, height);
+    }
+  } else {
+    redraw |= REDRAW_ALL;
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_window_redraw(struct ctk_window *w)
+{
+  /* Only redraw the window if it is a dialog or if it is the foremost
+     window. */
+  if(mode != CTK_MODE_NORMAL) {
+    return;
+  }
+  
+  if(w == dialog) {
+    ctk_draw_dialog(w);
+  } else if(dialog == NULL &&
+#if CTK_CONF_MENUS
+	    menus.open == NULL &&
+#endif /* CTK_CONF_MENUS */		    
+	    windows == w) {
+    ctk_draw_window(w, CTK_FOCUS_WINDOW,
+		    0, height);
+  }  
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+window_new(register struct ctk_window *window,
+	   unsigned char w, unsigned char h,
+	   char *title)
+{
+  window->x = window->y = 0;
+  window->w = w;
+  window->h = h;
+  window->title = title;
+  if(title != NULL) {
+    window->titlelen = strlen(title);
+  } else {
+    window->titlelen = 0;
+  }
+  window->next = window->prev = NULL;
+  window->owner = DISPATCHER_CURRENT();
+  window->active = window->inactive = window->focused = NULL;
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_window_new(struct ctk_window *window,
+	       unsigned char w, unsigned char h,
+	       char *title)
+{
+  window_new(window, w, h, title);
+
+  make_windowbuttons(window);  
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_dialog_new(register struct ctk_window *window,
+	       unsigned char w, unsigned char h)
+{
+  window_new(window, w, h, NULL);
+
+  window->x = (width - w) / 2;
+  window->y = (height - h - 1) / 2; 
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_menu_new(register struct ctk_menu *menu,
+	     char *title)
+{
+#if CTK_CONF_MENUS
+  menu->next = NULL;
+  menu->title = title;
+  menu->titlelen = strlen(title);
+  menu->active = 0;
+  menu->nitems = 0;
+#endif /* CTK_CONF_MENUS */
+}
+/*-----------------------------------------------------------------------------------*/
+unsigned char
+ctk_menuitem_add(register struct ctk_menu *menu,
+		 char *name)
+{
+#if CTK_CONF_MENUS
+  if(menu->nitems == CTK_CONF_MAXMENUITEMS) {
+    return 0;
+  }
+  menu->items[menu->nitems].title = name;
+  menu->items[menu->nitems].titlelen = strlen(name); 
+  return menu->nitems++;
+#else
+  return 0;
+#endif /* CTK_CONF_MENUS */
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+add_redrawwidget(struct ctk_widget *w)
+{
+  static unsigned char i;
+  
+  if(redraw_widgetptr == MAX_REDRAWWIDGETS) {
+    redraw |= REDRAW_FOCUS;
+  } else {
+    redraw |= REDRAW_WIDGETS;
+    /* Check if it is in the queue already. If so, we don't add it
+       again. */
+    for(i = 0; i < redraw_widgetptr; ++i) {
+      if(redraw_widgets[i] == w) {
+	return;
+      }
+    }
+    redraw_widgets[redraw_widgetptr++] = w;
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_widget_redraw(struct ctk_widget *widget)
+{
+  struct ctk_window *window;
+
+  if(mode != CTK_MODE_NORMAL) {
+    return;
+  }
+
+  /* If this function isn't called by CTK itself, we only queue the
+     redraw request. */
+  if(DISPATCHER_CURRENT() != ctkid) {
+    redraw |= REDRAW_WIDGETS;
+    add_redrawwidget(widget);
+  } else {
+    
+    /* Only redraw widgets that are in the foremost window. If we
+       would allow redrawing widgets in non-focused windows, we would
+       have to redraw all the windows that cover the non-focused
+       window as well, which would lead to flickering.
+
+       Also, we avoid drawing any widgets when the menus are active.
+    */
+    
+#if CTK_CONF_MENUS
+    if(menus.open == NULL)
+#endif /* CTK_CONF_MENUS */
+      {
+	window = widget->window;
+	if(window == dialog) {
+	  ctk_draw_widget(widget, CTK_FOCUS_DIALOG, 0, height);
+	} else if(window == windows) {
+	  ctk_draw_widget(widget, CTK_FOCUS_WINDOW, 0, height);	      
+	} else if(window == &desktop_window) {
+	  ctk_draw_widget(widget, 0, 0, height);
+	}
+      }
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_widget_add(register struct ctk_window *window,
+	       register struct ctk_widget *widget)
+{  
+  if(widget->type == CTK_WIDGET_LABEL ||
+     widget->type == CTK_WIDGET_SEPARATOR) {
+    widget->next = window->inactive;
+    window->inactive = widget;
+    widget->window = window;
+  } else {
+    widget->next = window->active;
+    window->active = widget;
+    widget->window = window;
+    if(window->focused == NULL) {
+      window->focused = widget;
+    }
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+#define UP 0
+#define DOWN 1
+#define LEFT 2
+#define RIGHT 3
+static void
+switch_focus_widget(unsigned char direction)
+{
+  register struct ctk_window *window;
+  register struct ctk_widget *focus;
+  struct ctk_widget *widget;
+  
+  
+  if(dialog != NULL) {
+    window = dialog;
+  } else {
+    window = windows;
+  }
+
+  /* If there are no windows open, we move focus around between the
+     icons on the root window instead. */
+  if(window == NULL) {
+    window = &desktop_window;
+  }
+ 
+  focus = window->focused;
+  add_redrawwidget(focus);
+  
+  if((direction & 1) == 0) {
+    /* Move focus "up" */
+    focus = focus->next;
+  } else {
+    /* Move focus "down" */
+    for(widget = window->active;
+	widget != NULL; widget = widget->next) {
+	if(widget->next == focus) {
+	  break;
+	}      
+    }    
+    focus = widget;
+    if(focus == NULL) {
+      if(window->active != NULL) {	
+	for(focus = window->active;
+	    focus->next != NULL; focus = focus->next);	
+      }
+    }
+  }
+  if(focus == NULL) {
+    focus = window->active;
+  }
+  
+  if(focus != window->focused) {
+    window->focused = focus;
+    /* The operation changed the focus, so we emit a "hover" signal
+       for those widgets that support it. */
+    
+    if(window->focused->type == CTK_WIDGET_HYPERLINK) {    
+      dispatcher_emit(ctk_signal_hyperlink_hover, window->focused,
+		      window->owner);
+    } else if(window->focused->type == CTK_WIDGET_BUTTON) {    
+      dispatcher_emit(ctk_signal_button_hover, window->focused,
+		      window->owner);      
+    } 
+    
+    add_redrawwidget(window->focused);
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+#if CTK_CONF_MENUS
+static void 
+switch_open_menu(unsigned char rightleft)
+{
+  struct ctk_menu *menu;
+  
+  if(rightleft == 0) {
+    /* Move right */
+    for(menu = menus.menus; menu != NULL; menu = menu->next) {
+      if(menu->next == menus.open) {
+	break;
+      }
+    }
+    lastmenu = menus.open;
+    menus.open = menu;
+    if(menus.open == NULL) {
+      for(menu = menus.menus;
+	  menu->next != NULL; menu = menu->next);
+      menus.open = menu;
+    }
+  } else {
+    /* Move to left */
+    lastmenu = menus.open;
+    menus.open = menus.open->next;
+    if(menus.open == NULL) {
+      menus.open = menus.menus;
+    }
+  }
+
+  menus.open->active = 0;  
+  /*  ctk_redraw();*/
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+switch_menu_item(unsigned char updown)
+{
+  register struct ctk_menu *m;
+
+  m = menus.open;
+  
+  if(updown == 0) {
+    /* Move up */
+    if(m->active == 0) {
+      m->active = m->nitems - 1;
+    } else {
+      --m->active;
+      if(m->items[m->active].title[0] == '-') {
+	--m->active;
+      }
+    }
+  } else {
+    /* Move down */
+    if(m->active >= m->nitems - 1) {
+      m->active = 0;
+    } else {      
+      ++m->active;
+      if(m->items[m->active].title[0] == '-') {
+	++m->active;
+      }
+    }
+  }
+  
+}
+#endif /* CTK_CONF_MENUS */
+/*-----------------------------------------------------------------------------------*/
+static unsigned char 
+activate(register struct ctk_widget *w)
+{
+  static unsigned char len;
+  
+  if(w->type == CTK_WIDGET_BUTTON) {
+    if(w == (struct ctk_widget *)&windows->closebutton) {
+#if CTK_CONF_WINDOWCLOSE
+      /*dispatcher_emit(ctk_signal_window_close, windows, w->window->owner);*/
+      ctk_window_close(windows);
+      return REDRAW_ALL;
+#endif /* CTK_CONF_WINDOWCLOSE */
+    } else if(w == (struct ctk_widget *)&windows->titlebutton) {
+#if CTK_CONF_WINDOWCLOSE
+      mode = CTK_MODE_WINDOWMOVE;
+#endif /* CTK_CONF_WINDOWCLOSE */
+    } else {
+      dispatcher_emit(ctk_signal_button_activate, w,
+		      w->window->owner);
+    }
+#if CTK_CONF_ICONS
+  } else if(w->type == CTK_WIDGET_ICON) {
+    dispatcher_emit(ctk_signal_button_activate, w,
+		    w->widget.icon.owner);
+#endif /* CTK_CONF_ICONS */
+  } else if(w->type == CTK_WIDGET_HYPERLINK) {    
+    dispatcher_emit(ctk_signal_hyperlink_activate, w,
+		    DISPATCHER_BROADCAST);
+  } else if(w->type == CTK_WIDGET_TEXTENTRY) {
+    if(w->widget.textentry.state == CTK_TEXTENTRY_NORMAL) {      
+      w->widget.textentry.state = CTK_TEXTENTRY_EDIT;
+      len = strlen(w->widget.textentry.text);
+      if(w->widget.textentry.xpos > len) {
+	w->widget.textentry.xpos = len;
+      }
+    } else if(w->widget.textentry.state == CTK_TEXTENTRY_EDIT) {
+      w->widget.textentry.state = CTK_TEXTENTRY_NORMAL;
+    }
+    add_redrawwidget(w);
+    return REDRAW_WIDGETS;
+  }
+  return REDRAW_NONE;
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+textentry_input(ctk_arch_key_t c,
+		register struct ctk_textentry *t)
+{
+  static char *cptr, *cptr2;
+  static unsigned char len, txpos, typos, tlen;
+
+  txpos = t->xpos;
+  typos = t->ypos;
+  tlen = t->len;
+
+  cptr = &t->text[txpos + typos * tlen];
+      
+  switch(c) {
+  case CH_CURS_LEFT:
+    if(txpos > 0) {
+      --txpos;
+    }
+    break;
+    
+  case CH_CURS_RIGHT:    
+    if(txpos < tlen &&
+       *cptr != 0) {
+      ++txpos;
+    }
+    break;
+
+  case CH_CURS_UP:
+#if CTK_CONF_TEXTENTRY_MULTILINE
+    if(t->h == 1) {
+      txpos = 0;
+    } else {
+      if(typos > 0) {
+	--typos;
+      } else {
+	t->state = CTK_TEXTENTRY_NORMAL;
+      }
+    }
+#else
+    txpos = 0;
+#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
+    break;
+    
+  case CH_CURS_DOWN:
+#if CTK_CONF_TEXTENTRY_MULTILINE
+    if(t->h == 1) {
+      txpos = strlen(t->text);
+    } else {
+      if(typos < t->h - 1) {
+	++typos;
+      } else {
+	t->state = CTK_TEXTENTRY_NORMAL;
+      }
+    }
+#else
+    txpos = strlen(t->text);
+#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
+    break;
+    
+  case CH_ENTER:
+#if CTK_CONF_TEXTENTRY_MULTILINE
+    if(t->h == 1) {
+      t->state = CTK_TEXTENTRY_NORMAL;
+    } else {
+      if(typos < t->h - 1) {
+	++typos;
+	txpos = 0;
+      } else {
+	t->state = CTK_TEXTENTRY_NORMAL;
+      }
+    }
+#else
+    t->state = CTK_TEXTENTRY_NORMAL;
+#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
+    break;
+    
+  default:
+    len = tlen - txpos - 1;
+    if(c == CH_DEL) {
+      if(txpos > 0 && len > 0) {
+	strncpy(cptr - 1, cptr,
+		len);
+	*(cptr + len - 1) = 0;
+	--txpos;
+      }
+    } else {
+      if(len > 0) {
+	cptr2 = cptr + len - 1;
+	while(cptr2 + 1 > cptr) {
+	  *(cptr2 + 1) = *cptr2;
+	  --cptr2;
+	}
+	
+	*cptr = c;
+	++txpos;
+      }
+    }
+    break;
+  }
+
+  t->xpos = txpos;
+  t->ypos = typos;
+}
+/*-----------------------------------------------------------------------------------*/
+#if CTK_CONF_MENUS
+static unsigned char
+menus_input(ctk_arch_key_t c)
+{
+  struct ctk_window *w;
+
+  if(menus.open->nitems > maxnitems) {
+    maxnitems = menus.open->nitems;
+  }
+
+  
+  switch(c) {
+  case CH_CURS_RIGHT:
+    switch_open_menu(1);
+	
+    return REDRAW_MENUPART;
+
+  case CH_CURS_DOWN:
+    switch_menu_item(1);
+    return REDRAW_MENUS;
+
+  case CH_CURS_LEFT:
+    switch_open_menu(0);
+    return REDRAW_MENUPART;
+
+  case CH_CURS_UP:
+    switch_menu_item(0);
+    return REDRAW_MENUS;
+    
+  case CH_ENTER:
+    lastmenu = menus.open;
+    if(menus.open == &desktopmenu) {
+      for(w = windows; w != NULL; w = w->next) {
+	if(w->title == desktopmenu.items[desktopmenu.active].title) {
+	  ctk_window_open(w);
+	  menus.open = NULL;
+	  return REDRAW_ALL;
+	}
+      }
+    } else {
+      dispatcher_emit(ctk_signal_menu_activate, menus.open,
+		      DISPATCHER_BROADCAST);
+    }
+    menus.open = NULL;
+    return REDRAW_MENUPART;
+
+  case CH_F1:
+    lastmenu = menus.open;
+    menus.open = NULL;
+    return REDRAW_MENUPART;
+  }
+}
+#endif /* CTK_CONF_MENUS */
+/*-----------------------------------------------------------------------------------*/
+static void
+idle(void)     
+{
+  static ctk_arch_key_t c;
+  static unsigned char i;  
+  register struct ctk_window *window;
+  register struct ctk_widget *widget;
+
+#if CTK_CONF_MENUS
+  if(menus.open != NULL) {
+    maxnitems = menus.open->nitems;
+  } else {
+    maxnitems = 0;
+  }
+#endif /* CTK_CONF_MENUS */
+  
+  if(mode == CTK_MODE_SCREENSAVER) {
+#ifdef CTK_SCREENSAVER_RUN
+    CTK_SCREENSAVER_RUN();
+#endif /* CTK_SCREENSAVER_RUN */
+    if(ctk_arch_keyavail()) {
+      mode = CTK_MODE_NORMAL;
+      ctk_draw_init();
+      ctk_redraw();
+    }
+  } else if(mode == CTK_MODE_NORMAL) {  
+    while(ctk_arch_keyavail()) {
+      
+      screensaver_timer = 0;
+      
+      c = ctk_arch_getkey();
+      
+      
+      if(dialog != NULL) {
+	window = dialog;
+      } else if(windows != NULL) {
+	window = windows;
+      } else {
+	window = &desktop_window;
+      }      
+      widget = window->focused;
+
+	  
+      if(widget != NULL &&
+	 widget->type == CTK_WIDGET_TEXTENTRY &&
+	 widget->widget.textentry.state == CTK_TEXTENTRY_EDIT) {
+	textentry_input(c, (struct ctk_textentry *)widget);
+	add_redrawwidget(widget);
+#if CTK_CONF_MENUS
+      } else if(menus.open != NULL) {
+	redraw |= menus_input(c);
+#endif /* CTK_CONF_MENUS */
+      } else {      
+	switch(c) {
+	case CH_CURS_RIGHT:
+	  switch_focus_widget(RIGHT);
+	  break;
+	case CH_CURS_DOWN:
+	  switch_focus_widget(DOWN);
+	  break;
+	case CH_CURS_LEFT:
+	  switch_focus_widget(LEFT);
+	  break;
+	case CH_CURS_UP:
+	  switch_focus_widget(UP);
+	  break;
+	case CH_ENTER:
+	  redraw |= activate(widget);
+	  break;
+#if CTK_CONF_MENUS
+	case CTK_CONF_MENU_KEY:
+	  if(dialog == NULL) {
+	    if(lastmenu == NULL) {
+	      menus.open = menus.menus;
+	    } else {
+	      menus.open = lastmenu;
+	    }
+	    menus.open->active = 0;
+	    redraw |= REDRAW_MENUS;
+	  } 
+	  break;
+#endif /* CTK_CONF_MENUS */
+	case CTK_CONF_WINDOWSWITCH_KEY:
+	  if(windows != NULL) {
+	    for(window = windows; window->next != NULL;
+		window = window->next);
+	    ctk_window_open(window);
+	    ctk_redraw();
+	  }
+	  break;
+	default:
+	  if(widget->type == CTK_WIDGET_TEXTENTRY) {
+	    widget->widget.textentry.state = CTK_TEXTENTRY_EDIT;
+	    textentry_input(c, (struct ctk_textentry *)widget);
+	    add_redrawwidget(widget);
+	  } else {
+	    dispatcher_emit(ctk_signal_keypress, (void *)c,
+			    window->owner);
+	  }
+	  break;
+	}
+      }
+
+      if(redraw & REDRAW_WIDGETS) {
+	for(i = 0; i < redraw_widgetptr; ++i) {
+	  ctk_widget_redraw(redraw_widgets[i]);
+	}
+	redraw &= ~REDRAW_WIDGETS;
+	redraw_widgetptr = 0;
+      }
+    }
+    if(redraw & REDRAW_ALL) {
+      do_redraw_all(1, height);
+#if CTK_CONF_MENUS
+    } else if(redraw & REDRAW_MENUPART) {
+      do_redraw_all(1, maxnitems + 1);
+    } else if(redraw & REDRAW_MENUS) {
+      ctk_draw_menus(&menus);
+#endif /* CTK_CONF_MENUS */
+    } else if(redraw & REDRAW_FOCUS) {
+      if(dialog != NULL) {
+	ctk_window_redraw(dialog);
+      } else if(windows != NULL) {
+	ctk_window_redraw(windows);	
+      } else {
+	ctk_window_redraw(&desktop_window);	
+      }
+    } else if(redraw & REDRAW_WIDGETS) {
+      for(i = 0; i < redraw_widgetptr; ++i) {
+	ctk_widget_redraw(redraw_widgets[i]);
+      }
+    }    
+    redraw = 0;
+    redraw_widgetptr = 0;
+#if CTK_CONF_WINDOWMOVE
+  } else if(mode == CTK_MODE_WINDOWMOVE) {
+
+    redraw = 0;
+
+    window = windows;
+    
+    while(mode == CTK_MODE_WINDOWMOVE && ctk_arch_keyavail()) {
+    
+      screensaver_timer = 0;
+      
+      c = ctk_arch_getkey();
+      
+      switch(c) {
+      case CH_CURS_RIGHT:
+	++window->x;
+	if(window->x + window->w + 1 >= width) {
+	  --window->x;
+	}
+	redraw = REDRAW_ALL;
+	break;
+      case CH_CURS_LEFT:
+	if(window->x > 0) {
+	  --window->x;
+	}
+	redraw = REDRAW_ALL;
+	break;
+      case CH_CURS_DOWN:
+	++window->y;
+	if(window->y + window->h + 2 >= height) {
+	  --window->y;
+	}
+	redraw = REDRAW_ALL;
+	break;
+      case CH_CURS_UP:
+	if(window->y > 0) {
+	  --window->y;
+	}
+	redraw = REDRAW_ALL;
+	break;
+      case CH_ENTER:
+      case CH_ESC:
+	mode = CTK_MODE_NORMAL;
+	redraw = REDRAW_ALL;
+	break;
+      }
+    }
+    if(redraw & REDRAW_ALL) {
+      do_redraw_all(1, height);
+    }
+    redraw = 0;
+#endif /* CTK_CONF_WINDOWMOVE */
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+sighandler(ek_signal_t s, ek_data_t data)
+{
+  if(s == ctk_signal_timer) {
+    if(mode == CTK_MODE_NORMAL) {
+      ++screensaver_timer;
+      if(screensaver_timer == SCREENSAVER_TIMEOUT) {
+#ifdef CTK_SCREENSAVER_INIT
+	CTK_SCREENSAVER_INIT();
+#endif /* CTK_SCREENSAVER_INIT */
+	mode = CTK_MODE_SCREENSAVER;
+	screensaver_timer = 0;
+      }
+    }
+    dispatcher_timer(ctk_signal_timer, data, CLK_TCK);
+  }
+}
+/*-----------------------------------------------------------------------------------*/