Contiki 2.6

ctk.c

Go to the documentation of this file.
00001 /**
00002  * \defgroup ctk CTK graphical user interface
00003  *
00004  * The Contiki Toolkit (CTK) provides the graphical user interface for
00005  * the Contiki system.
00006  *
00007  * @{
00008  */
00009 
00010 /**
00011  * \file
00012  * The Contiki Toolkit CTK, the Contiki GUI.
00013  * \author Adam Dunkels <adam@dunkels.com>
00014  */
00015 
00016 /*
00017  * Copyright (c) 2002-2003, Adam Dunkels.
00018  * All rights reserved.
00019  *
00020  * Redistribution and use in source and binary forms, with or without
00021  * modification, are permitted provided that the following conditions
00022  * are met:
00023  * 1. Redistributions of source code must retain the above copyright
00024  *    notice, this list of conditions and the following disclaimer.
00025  * 2. Redistributions in binary form must reproduce the above
00026  *    copyright notice, this list of conditions and the following
00027  *    disclaimer in the documentation and/or other materials provided
00028  *    with the distribution.
00029  * 3. The name of the author may not be used to endorse or promote
00030  *    products derived from this software without specific prior
00031  *    written permission.
00032  *
00033  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
00034  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00035  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00036  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
00037  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00038  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
00039  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00040  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
00041  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00042  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00043  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00044  *
00045  * This file is part of the Contiki operating system.
00046  *
00047  * $Id: ctk.c,v 1.26 2010/09/09 20:21:26 oliverschmidt Exp $
00048  *
00049  */
00050 
00051 #include <string.h>
00052 
00053 #include "contiki.h"
00054 
00055 #include "ctk/ctk.h"
00056 #include "ctk/ctk-draw.h"
00057 #include "ctk/ctk-mouse.h"
00058 
00059 static unsigned char height, width;
00060 
00061 static unsigned char mode;
00062 
00063 #if CTK_CONF_WINDOWS
00064 static struct ctk_window desktop_window;
00065 static struct ctk_window *windows;
00066 static struct ctk_window *dialog;
00067 #else /* CTK_CONF_WINDOWS */
00068 static struct ctk_window *window;
00069 #endif /* CTK_CONF_WINDOWS */
00070 
00071 #if CTK_CONF_MENUS
00072 static struct ctk_menus menus;
00073 static struct ctk_menu *lastmenu;
00074 static struct ctk_menu desktopmenu;
00075 static unsigned char maxnitems;
00076 #endif /* CTK_CONF_MENUS */
00077 
00078 #ifndef NULL
00079 #define NULL (void *)0
00080 #endif /* NULL */
00081 
00082 #define REDRAW_NONE         0
00083 #define REDRAW_ALL          1
00084 #define REDRAW_FOCUS        2
00085 #define REDRAW_WIDGETS      4
00086 #define REDRAW_MENUS        8
00087 #define REDRAW_MENUPART     16
00088 
00089 #define MAX_REDRAWWIDGETS 4
00090 static unsigned char redraw;
00091 static struct ctk_widget *redraw_widgets[MAX_REDRAWWIDGETS];
00092 static unsigned char redraw_widgetptr;
00093 
00094 #if CTK_CONF_ICONS
00095 static unsigned char iconx, icony;
00096 #define ICONX_START  (width - 6)
00097 #define ICONY_START  (height - 6 - CTK_CONF_MENUS)
00098 #define ICONX_DELTA  -16
00099 #define ICONY_DELTA  -5
00100 #define ICONY_MAX    height
00101 #endif /* CTK_CONF_ICONS */
00102 
00103 #ifndef ctk_arch_keyavail
00104 unsigned char ctk_arch_keyavail(void);
00105 #endif /* ctk_arch_keyavail */
00106 
00107 #ifndef ctk_arch_getkey
00108 ctk_arch_key_t ctk_arch_getkey(void);
00109 #endif /* ctk_arch_getkey */
00110 
00111 #ifndef ctk_arch_isprint
00112 unsigned char ctk_arch_isprint(ctk_arch_key_t key);
00113 #endif /* ctk_arch_isprint */
00114 
00115 PROCESS(ctk_process, "CTK Contiki GUI");
00116 
00117 /**
00118  * \defgroup ctkevents CTK events
00119  * @{
00120  */
00121 process_event_t
00122 
00123   /**
00124    * Emitted for every key being pressed.
00125    *
00126    * The key is passed as signal data.*/
00127   ctk_signal_keypress,
00128   
00129   /** Emitted when a widget is activated (pressed). A pointer to the
00130       widget is passed as signal data. */
00131   ctk_signal_widget_activate,
00132   
00133   /** Same as ctk_signal_widget_activate. */
00134   ctk_signal_button_activate,
00135 
00136   /** Emitted when a widget is selected. A pointer to the widget is
00137       passed as signal data. */
00138   ctk_signal_widget_select,
00139   
00140   /** Same as ctk_signal_widget_select. */
00141   ctk_signal_button_hover,
00142 
00143   /** Emitted when a hyperlink is activated. The signal is broadcast
00144       to all listeners. */
00145   ctk_signal_hyperlink_activate,
00146 
00147   /** Same as ctk_signal_widget_select. */
00148   ctk_signal_hyperlink_hover;
00149 
00150   /** Emitted when a menu item is activated. The number of the menu
00151       item is passed as signal data. */
00152 process_event_t ctk_signal_menu_activate;
00153 
00154   /** Emitted when a window is closed. A pointer to the window is
00155       passed as signal data. */
00156 process_event_t ctk_signal_window_close;
00157 
00158 #if CTK_CONF_MOUSE_SUPPORT
00159   /** Emitted when the mouse pointer is moved. A NULL pointer is
00160       passed as signal data and it is up to the listening process to
00161       check the position of the mouse using the CTK mouse API.*/
00162 process_event_t ctk_signal_pointer_move,
00163   /** Emitted when a mouse button is pressed. The button is passed as
00164       signal data to the listening process. */
00165   ctk_signal_pointer_button;
00166 #endif /* CTK_CONF_MOUSE_SUPPORT */
00167 
00168 #if CTK_CONF_SCREENSAVER
00169 /** Emitted when the user has been idle long enough for the
00170     screensaver to start. */
00171 process_event_t ctk_signal_screensaver_stop,
00172   /** Emitted when the user presses a key or moves the mouse when the
00173       screensaver is active. */
00174   ctk_signal_screensaver_start;
00175 #endif /* CTK_CONF_SCREENSAVER */
00176 
00177 /** @} */
00178 
00179 #if CTK_CONF_MOUSE_SUPPORT
00180 unsigned short mouse_x, mouse_y, mouse_button;
00181 #endif /* CTK_CONF_MOUSE_SUPPORT */
00182 
00183 #if CTK_CONF_SCREENSAVER
00184 static unsigned short screensaver_timer = 0;
00185 unsigned short ctk_screensaver_timeout = (5*60);
00186 static struct timer timer;
00187 #endif /* CTK_CONF_SCREENSAVER */
00188 
00189 static void CC_FASTCALL
00190 textentry_input(ctk_arch_key_t c,
00191                 CC_REGISTER_ARG struct ctk_textentry *t);
00192 
00193 #if CTK_CONF_MENUS
00194 /*---------------------------------------------------------------------------*/
00195 /**
00196  * \internal Creates the Desktop menu.
00197  *
00198  * Creates the leftmost menu, "Desktop". Since the desktop menu
00199  * contains the list of all open windows, this function will be called
00200  * whenever a window is opened or closed.
00201  */
00202 /*---------------------------------------------------------------------------*/
00203 static void
00204 make_desktopmenu(void)
00205 {
00206   struct ctk_window *w;
00207   
00208   desktopmenu.nitems = 0;
00209   
00210   if(windows == NULL) {
00211     ctk_menuitem_add(&desktopmenu, "(No windows)");
00212   } else {
00213     for(w = windows; w != NULL; w = w->next) {
00214       ctk_menuitem_add(&desktopmenu, w->title);
00215     }
00216   }
00217 }
00218 #endif /* CTK_CONF_MENUS */
00219 /*---------------------------------------------------------------------------*/
00220 #if CTK_CONF_ICONS
00221 static void
00222 arrange_icons(void)
00223 {
00224   struct ctk_widget *icon;
00225 
00226   iconx = ICONX_START;
00227   icony = ICONY_START;
00228   
00229   for(icon = desktop_window.active; icon != NULL; icon = icon->next) {
00230     
00231     icon->x = iconx;
00232     icon->y = icony;
00233     
00234     icony += ICONY_DELTA;
00235     if(icony >= ICONY_MAX) {
00236       icony = ICONY_START;
00237       iconx += ICONX_DELTA;
00238     }
00239   }
00240 }
00241 #endif /* CTK_CONF_ICONS */
00242 /*---------------------------------------------------------------------------*/
00243 void
00244 ctk_restore(void)
00245 {
00246   ctk_draw_init();
00247 
00248   height = ctk_draw_height();
00249   width = ctk_draw_width();
00250 
00251 #if CTK_CONF_ICONS
00252   arrange_icons();
00253 #endif /* CTK_CONF_ICONS */
00254 
00255   redraw = REDRAW_ALL;
00256 }
00257 /*---------------------------------------------------------------------------*/
00258 
00259 /**
00260  * \addtogroup ctkappfunc
00261  * @{
00262  */
00263 
00264 /*---------------------------------------------------------------------------*/
00265 /**
00266  * Sets the current CTK mode.
00267  *
00268  * The CTK mode can be either CTK_MODE_NORMAL, CTK_MODE_SCREENSAVER or
00269  * CTK_MODE_EXTERNAL. CTK_MODE_NORMAL is the normal mode, in which
00270  * keypresses and mouse pointer movements are processed and the screen
00271  * is redrawn. In CTK_MODE_SCREENSAVER, no screen redraws are
00272  * performed and the first key press or pointer movement will cause
00273  * the ctk_signal_screensaver_stop to be emitted. In the
00274  * CTK_MODE_EXTERNAL mode, key presses and pointer movements are
00275  * ignored and no screen redraws are made.
00276  *
00277  * \param m The mode.
00278  */
00279 /*---------------------------------------------------------------------------*/
00280 void
00281 ctk_mode_set(unsigned char m) {
00282   mode = m;
00283 }
00284 /*---------------------------------------------------------------------------*/
00285 /**
00286  * Retrieves the current CTK mode.
00287  *
00288  * \return The current CTK mode.
00289  */
00290 /*---------------------------------------------------------------------------*/
00291 unsigned char
00292 ctk_mode_get(void) {
00293   return mode;
00294 }
00295 /*---------------------------------------------------------------------------*/
00296 /**
00297  * Add an icon to the desktop.
00298  *
00299  * \param icon The icon to be added.
00300  *
00301  * \param p The process that owns the icon.
00302  */
00303 /*---------------------------------------------------------------------------*/
00304 void
00305 ctk_icon_add(CC_REGISTER_ARG struct ctk_widget *icon, struct process *p)
00306 {
00307 #if CTK_CONF_ICONS
00308   icon->widget.icon.owner = p;
00309   ctk_widget_add(&desktop_window, icon);
00310   arrange_icons();
00311 #endif /* CTK_CONF_ICONS */
00312 }
00313 #if CTK_CONF_WINDOWS
00314 /*---------------------------------------------------------------------------*/
00315 /**
00316  * Open a dialog box.
00317  *
00318  * \param d The dialog to be opened.
00319  */
00320 /*---------------------------------------------------------------------------*/
00321 void
00322 ctk_dialog_open(struct ctk_window *d)
00323 {
00324   dialog = d;
00325   redraw |= REDRAW_FOCUS;
00326 }
00327 /*---------------------------------------------------------------------------*/
00328 /**
00329  * Close the dialog box, if one is open.
00330  *
00331  */
00332 /*---------------------------------------------------------------------------*/
00333 void
00334 ctk_dialog_close(void)
00335 {
00336   dialog = NULL;
00337   redraw |= REDRAW_ALL;
00338 }
00339 #endif /* CTK_CONF_WINDOWS */
00340 /*---------------------------------------------------------------------------*/
00341 /**
00342  * Open a window, or bring window to front if already open.
00343  *
00344  * \param w The window to be opened.
00345  */
00346 /*---------------------------------------------------------------------------*/
00347 void
00348 ctk_window_open(CC_REGISTER_ARG struct ctk_window *w)
00349 {
00350 #if CTK_CONF_WINDOWS
00351   struct ctk_window *w2;
00352   
00353   /* Check if already open. */
00354   for(w2 = windows; w2 != w && w2 != NULL; w2 = w2->next);
00355   if(w2 == NULL) {
00356    /* Not open, so we add it at the head of the list of open
00357        windows. */
00358     w->next = windows;
00359     if(windows != NULL) {
00360       windows->prev = w;
00361     }
00362     windows = w;
00363     w->prev = NULL;
00364   } else {
00365     /* Window already open, so we move it to the front of the windows
00366        list. */
00367     if(w != windows) {
00368       if(w->next != NULL) {
00369         w->next->prev = w->prev;
00370       }
00371       if(w->prev != NULL) {
00372         w->prev->next = w->next;
00373       }
00374       w->next = windows;
00375       windows->prev = w;
00376       windows = w;
00377       w->prev = NULL;
00378     }
00379   }
00380 #else /* CTK_CONF_WINDOWS */
00381   window = w;
00382 #endif /* CTK_CONF_WINDOWS */
00383 
00384 #if CTK_CONF_MENUS
00385   /* Recreate the Desktop menu's window entries.*/
00386   make_desktopmenu();
00387 #endif /* CTK_CONF_MENUS */
00388 
00389   redraw |= REDRAW_ALL;
00390 }
00391 /*---------------------------------------------------------------------------*/
00392 /**
00393  * Close a window if it is open.
00394  *
00395  * If the window is not open, this function does nothing.
00396  *
00397  * \param w The window to be closed.
00398  */
00399 /*---------------------------------------------------------------------------*/
00400 void
00401 ctk_window_close(struct ctk_window *w)
00402 {
00403 #if CTK_CONF_WINDOWCLOSE
00404   static struct ctk_window *w2;
00405 
00406   if(w == NULL) {
00407     return;
00408   }
00409   
00410   /* Check if the window to be closed is the first window on the list. */
00411   if(w == windows) {
00412     windows = w->next;
00413     if(windows != NULL) {
00414       windows->prev = NULL;
00415     }
00416     w->next = w->prev = NULL;
00417   } else {
00418     /* Otherwise we step through the list until we find the window
00419        before the one to be closed. We then redirect its ->next
00420        pointer and its ->next->prev. */
00421     for(w2 = windows; w2 != NULL && w2->next != w; w2 = w2->next);
00422 
00423     if(w2 == NULL) {
00424       /* The window wasn't open, so there is nothing more for us to do. */
00425       return;
00426     }
00427 
00428     if(w->next != NULL) {
00429       w->next->prev = w->prev;
00430     }
00431     w2->next = w->next;
00432     
00433     w->next = w->prev = NULL;
00434   }
00435   
00436 #if CTK_CONF_MENUS
00437   /* Recreate the Desktop menu's window entries.*/
00438   make_desktopmenu();
00439 #endif /* CTK_CONF_MENUS */
00440   redraw |= REDRAW_ALL;
00441 #endif /* CTK_CONF_WINDOWCLOSE */
00442 }
00443 #if CTK_CONF_WINDOWS
00444 /*---------------------------------------------------------------------------*/
00445 /**
00446  * \internal Create the move and close buttons on the window titlebar.
00447  */
00448 /*---------------------------------------------------------------------------*/
00449 static void
00450 make_windowbuttons(CC_REGISTER_ARG struct ctk_window *window)
00451 {
00452   unsigned char placement;
00453 
00454   if(ctk_draw_windowtitle_height >= 2) {
00455     placement = -1 - ctk_draw_windowtitle_height/2;
00456   } else {
00457     placement = -1;
00458   }
00459 #if CTK_CONF_WINDOWMOVE
00460   CTK_BUTTON_NEW(&window->titlebutton, 0, placement,
00461                  window->titlelen, window->title);
00462 #else
00463   CTK_LABEL_NEW(&window->titlebutton, 0, placement,
00464                 window->titlelen, 1, window->title);
00465 #endif /* CTK_CONF_WINDOWMOVE */
00466   CTK_WIDGET_ADD(window, &window->titlebutton);
00467 
00468 #if CTK_CONF_WINDOWCLOSE
00469   CTK_BUTTON_NEW(&window->closebutton, window->w - 3, placement,
00470                  1, "x");
00471 #else
00472   CTK_LABEL_NEW(&window->closebutton, window->w - 4, placement,
00473                 3, 1, "   ");
00474 #endif /* CTK_CONF_WINDOWCLOSE */
00475   CTK_WIDGET_ADD(window, &window->closebutton);
00476 }
00477 #endif /* CTK_CONF_WINDOWS */
00478 /*---------------------------------------------------------------------------*/
00479 /**
00480  * Remove all widgets from a window.
00481  *
00482  * \param w The window to be cleared.
00483  */
00484 /*---------------------------------------------------------------------------*/
00485 void
00486 ctk_window_clear(struct ctk_window *w)
00487 {
00488   w->active = w->inactive = w->focused = NULL;
00489   
00490 #if CTK_CONF_WINDOWS
00491   make_windowbuttons(w);
00492 #endif /* CTK_CONF_WINDOWS */
00493 }
00494 /*---------------------------------------------------------------------------*/
00495 /**
00496  * Add a menu to the menu bar.
00497  *
00498  * \param menu The menu to be added.
00499  *
00500  * \note Do not call this function multiple times for the same menu,
00501  * as no check is made to see if the menu already is in the menu bar.
00502  */
00503 /*---------------------------------------------------------------------------*/
00504 void
00505 ctk_menu_add(struct ctk_menu *menu)
00506 {
00507 #if CTK_CONF_MENUS
00508   struct ctk_menu *m;
00509 
00510   if(lastmenu == NULL) {
00511     lastmenu = menu;
00512   }
00513     
00514   for(m = menus.menus; m->next != NULL; m = m->next) {
00515     if(m == menu) {
00516       return;
00517     }
00518   }
00519   m->next = menu;
00520   menu->next = NULL;
00521 
00522   redraw |= REDRAW_MENUPART;
00523 #endif /* CTK_CONF_MENUS */
00524 }
00525 /*---------------------------------------------------------------------------*/
00526 /**
00527  * Remove a menu from the menu bar.
00528  *
00529  * \param menu The menu to be removed.
00530  */
00531 /*---------------------------------------------------------------------------*/
00532 void
00533 ctk_menu_remove(struct ctk_menu *menu)
00534 {
00535 #if CTK_CONF_MENUS
00536   struct ctk_menu *m;
00537 
00538   for(m = menus.menus; m->next != NULL; m = m->next) {
00539     if(m->next == menu) {
00540       m->next = menu->next;
00541       if(menu == lastmenu) {
00542         lastmenu = NULL;
00543       }
00544       redraw |= REDRAW_MENUPART;
00545       return;
00546     }
00547   }
00548 #endif /* CTK_CONF_MENUS */
00549 }
00550 /*---------------------------------------------------------------------------*/
00551 /**
00552  * \internal Redraws everything on the screen within the clip
00553  * interval.
00554  *
00555  * \param clipy1 The upper bound of the clip interval
00556  * \param clipy2 The lower bound of the clip interval
00557  */
00558 /*---------------------------------------------------------------------------*/
00559 static void CC_FASTCALL
00560 do_redraw_all(unsigned char clipy1, unsigned char clipy2)
00561 {
00562 #if CTK_CONF_WINDOWS
00563   static struct ctk_widget *widget;
00564   struct ctk_window *w;
00565   unsigned char focus;
00566 #endif /* CTK_CONF_WINDOWS */
00567 
00568   if(mode != CTK_MODE_NORMAL && mode != CTK_MODE_WINDOWMOVE) {
00569     return;
00570   }
00571   
00572   ctk_draw_clear(clipy1, clipy2);
00573 
00574 #if CTK_CONF_WINDOWS  
00575   /* Draw widgets in root window */
00576   for(widget = desktop_window.active;
00577       widget != NULL; widget = widget->next) {
00578     ctk_draw_widget(widget, windows != NULL? 0: CTK_FOCUS_WINDOW, clipy1, clipy2);
00579   }
00580 
00581   /* Draw windows */
00582   if(windows != NULL) {
00583     /* Find the last window.*/
00584     for(w = windows; w->next != NULL; w = w->next);
00585 
00586     /* Draw the windows from back to front. */
00587     for(; w != windows; w = w->prev) {
00588       ctk_draw_clear_window(w, 0, clipy1, clipy2);
00589       ctk_draw_window(w, 0, clipy1, clipy2, 1);
00590     }
00591 
00592     /* Draw focused window */
00593     focus = mode == CTK_MODE_WINDOWMOVE?
00594             CTK_FOCUS_WIDGET|CTK_FOCUS_WINDOW:
00595             CTK_FOCUS_WINDOW;
00596     ctk_draw_clear_window(windows, focus, clipy1, clipy2);
00597     ctk_draw_window(windows, focus, clipy1, clipy2, 1);
00598   }
00599 
00600   /* Draw dialog (if any) */
00601   if(dialog != NULL) {
00602     ctk_draw_dialog(dialog);
00603   }
00604 #else /* CTK_CONF_WINDOWS */
00605   if(window != NULL) {
00606     ctk_draw_clear_window(window, CTK_FOCUS_WINDOW, clipy1, clipy2);
00607     ctk_draw_window(window, CTK_FOCUS_WINDOW, clipy1, clipy2, 0);
00608   }
00609 #endif /* CTK_CONF_WINDOWS */
00610 
00611 #if CTK_CONF_MENUS
00612   ctk_draw_menus(&menus);
00613 #endif /* CTK_CONF_MENUS */
00614 }
00615 #if CTK_CONF_WINDOWS
00616 /*---------------------------------------------------------------------------*/
00617 /**
00618  * Redraw the entire desktop.
00619  *
00620  * \param d The desktop to be redrawn.
00621  *
00622  * \note Currently the parameter d is not used, but must be set to
00623  * NULL.
00624  *
00625  */
00626 /*---------------------------------------------------------------------------*/
00627 void
00628 ctk_desktop_redraw(struct ctk_desktop *d)
00629 {
00630   if(PROCESS_CURRENT() == &ctk_process) {
00631     if(mode == CTK_MODE_NORMAL || mode == CTK_MODE_WINDOWMOVE) {
00632       do_redraw_all(CTK_CONF_MENUS, height);
00633     }
00634   } else {
00635     height = ctk_draw_height();
00636     width = ctk_draw_width();
00637     
00638     redraw |= REDRAW_ALL;
00639   }
00640 }
00641 #endif /* CTK_CONF_WINDOWS */
00642 /*---------------------------------------------------------------------------*/
00643 /**
00644  * Redraw a window.
00645  *
00646  * This function redraws the window, but only if it is the foremost
00647  * one on the desktop.
00648  *
00649  * \param w The window to be redrawn.
00650  */
00651 /*---------------------------------------------------------------------------*/
00652 void
00653 ctk_window_redraw(struct ctk_window *w)
00654 {
00655   /* Only redraw the window if it is a dialog or if it is the foremost
00656      window. */
00657   if(mode != CTK_MODE_NORMAL) {
00658     return;
00659   }
00660   
00661 #if CTK_CONF_WINDOWS
00662   if(w == dialog) {
00663     ctk_draw_dialog(w);
00664   } else if(dialog == NULL &&
00665 #if CTK_CONF_MENUS
00666             menus.open == NULL &&
00667 #endif /* CTK_CONF_MENUS */
00668             windows == w)
00669 #endif /* CTK_CONF_WINDOWS */
00670   {
00671     ctk_draw_window(w, CTK_FOCUS_WINDOW, 0, height, 0);
00672   }
00673 }
00674 /*---------------------------------------------------------------------------*/
00675 /**
00676  * \internal Creates a new window.
00677  *
00678  * \param window The window to be created.
00679  * \param w The width of the window.
00680  * \param h The height of the window.
00681  * \param title The title of the window.
00682  */
00683 /*---------------------------------------------------------------------------*/
00684 static void
00685 window_new(CC_REGISTER_ARG struct ctk_window *window,
00686            unsigned char w, unsigned char h, char *title)
00687 {
00688 #if CTK_CONF_WINDOWS
00689   if(w >= width - 2) {
00690     window->x = 0;
00691   } else {
00692     window->x = (width - w - 2) / 2;
00693   }
00694   if(h >= height - 2 - ctk_draw_windowtitle_height) {
00695     window->y = 0;
00696   } else {
00697     window->y = (height - h - 2 - ctk_draw_windowtitle_height) / 2;
00698   }
00699 #endif /* CTK_CONF_WINDOWS */
00700 
00701   window->w = w;
00702   window->h = h;
00703   window->title = title;
00704   if(title != NULL) {
00705     window->titlelen = (unsigned char)strlen(title);
00706   } else {
00707     window->titlelen = 0;
00708   }
00709   window->next = window->prev = NULL;
00710   window->owner = PROCESS_CURRENT();
00711   window->active = window->inactive = window->focused = NULL;
00712 }
00713 /*---------------------------------------------------------------------------*/
00714 /**
00715  * Create a new window.
00716  *
00717  * Creates a new window. The memory for the window structure must
00718  * already be allocated by the caller, and is usually done with a
00719  * static declaration.
00720  *
00721  * This function sets up the internal structure of the ctk_window
00722  * struct and creates the move and close buttons, but it does not open
00723  * the window. The window must be explicitly opened by calling the
00724  * ctk_window_open() function.
00725  *
00726  * \param window The window to be created.
00727  * \param w The width of the new window.
00728  * \param h The height of the new window.
00729  * \param title The title of the new window.
00730  */
00731 /*---------------------------------------------------------------------------*/
00732 void
00733 ctk_window_new(struct ctk_window *window,
00734                unsigned char w, unsigned char h, char *title)
00735 {
00736   window_new(window, w, h, title);
00737 
00738 #if CTK_CONF_WINDOWS
00739   make_windowbuttons(window);
00740 #endif /* CTK_CONF_WINDOWS */
00741 }
00742 #if CTK_CONF_WINDOWS
00743 /*---------------------------------------------------------------------------*/
00744 /**
00745  * Creates a new dialog.
00746  *
00747  * This function only sets up the internal structure of the ctk_window
00748  * struct but does not open the dialog. The dialog must be explicitly
00749  * opened by calling the ctk_dialog_open() function.
00750  *
00751  * \param dialog The dialog to be created.
00752  * \param w The width of the dialog.
00753  * \param h The height of the dialog.
00754  */
00755 /*---------------------------------------------------------------------------*/
00756 void
00757 ctk_dialog_new(CC_REGISTER_ARG struct ctk_window *dialog,
00758                unsigned char w, unsigned char h)
00759 {
00760   window_new(dialog, w, h, NULL);
00761 }
00762 #endif /* CTK_CONF_WINDOWS */
00763 /*---------------------------------------------------------------------------*/
00764 /**
00765  * Creates a new menu.
00766  *
00767  * This function sets up the internal structure of the menu, but does
00768  * not add it to the menubar. Use the function ctk_menu_add() for that
00769  * purpose.
00770  *
00771  * \param menu The menu to be created.
00772  * \param title The title of the menu.
00773  */
00774 /*---------------------------------------------------------------------------*/
00775 void
00776 ctk_menu_new(CC_REGISTER_ARG struct ctk_menu *menu, char *title)
00777 {
00778 #if CTK_CONF_MENUS
00779   menu->next = NULL;
00780   menu->title = title;
00781   menu->titlelen = (unsigned char)strlen(title);
00782   menu->active = 0;
00783   menu->nitems = 0;
00784 #endif /* CTK_CONF_MENUS */
00785 }
00786 /*---------------------------------------------------------------------------*/
00787 /**
00788  * Adds a menu item to a menu.
00789  *
00790  * In CTK, each menu item is identified by a number which is unique
00791  * within each menu. When a menu item is selected, a
00792  * ctk_menuitem_activated signal is emitted and the menu item number
00793  * is passed as signal data with the signal.
00794  *
00795  * \param menu The menu to which the menu item should be added.
00796  * \param name The name of the menu item.
00797  * \return The number of the menu item.
00798  */
00799 /*---------------------------------------------------------------------------*/
00800 unsigned char
00801 ctk_menuitem_add(CC_REGISTER_ARG struct ctk_menu *menu, char *name)
00802 {
00803 #if CTK_CONF_MENUS
00804   if(menu->nitems == CTK_MAXMENUITEMS) {
00805     return 0;
00806   }
00807   menu->items[menu->nitems].title = name;
00808   menu->items[menu->nitems].titlelen = (unsigned char)strlen(name);
00809   return menu->nitems++;
00810 #else
00811   return 0;
00812 #endif /* CTK_CONF_MENUS */
00813 }
00814 /*---------------------------------------------------------------------------*/
00815 /**
00816  * \internal Adds a widget to the list of widgets that should be
00817  * redrawn.
00818  *
00819  * \param w The widget that should be redrawn.
00820  */
00821 /*---------------------------------------------------------------------------*/
00822 static void CC_FASTCALL
00823 add_redrawwidget(struct ctk_widget *w)
00824 {
00825   static unsigned char i;
00826   
00827   if(redraw_widgetptr == MAX_REDRAWWIDGETS) {
00828     redraw |= REDRAW_FOCUS;
00829   } else {
00830     redraw |= REDRAW_WIDGETS;
00831     /* Check if it is in the queue already. If so, we don't add it
00832        again. */
00833     for(i = 0; i < redraw_widgetptr; ++i) {
00834       if(redraw_widgets[i] == w) {
00835         return;
00836       }
00837     }
00838     redraw_widgets[redraw_widgetptr++] = w;
00839   }
00840 }
00841 /*---------------------------------------------------------------------------*/
00842 /**
00843  * \internal Checks if a widget redrawn and adds it to the list of
00844  * widgets to be redrawn.
00845  *
00846  * A widget can be redrawn only if the current CTK mode is
00847  * CTK_MODE_NORMAL, if no menu is open, and the widget is in the
00848  * foremost window.
00849  *
00850  * \param widget The widget that should be redrawn.
00851  */
00852 /*---------------------------------------------------------------------------*/
00853 static void
00854 widget_redraw(struct ctk_widget *widget)
00855 {
00856   struct ctk_window *window;
00857 
00858   if(mode != CTK_MODE_NORMAL || widget == NULL) {
00859     return;
00860   }
00861 
00862   /* Only redraw widgets that are in the foremost window. If we would
00863      allow redrawing widgets in non-focused windows, we would have to
00864      redraw all the windows that cover the non-focused window as well,
00865      which would lead to flickering.
00866 
00867      Also, we avoid drawing any widgets when the menus are active.
00868     */
00869     
00870 #if CTK_CONF_MENUS
00871   if(menus.open == NULL)
00872 #endif /* CTK_CONF_MENUS */
00873   {
00874     window = widget->window;
00875 #if CTK_CONF_WINDOWS
00876     if(window == dialog) {
00877       ctk_draw_widget(widget, CTK_FOCUS_DIALOG, 0, height);
00878     } else if(dialog == NULL &&
00879               (window == windows ||
00880                window == &desktop_window))
00881 #endif /* CTK_CONF_WINDOWS */
00882     {
00883       ctk_draw_widget(widget, CTK_FOCUS_WINDOW, 0, height);
00884     }
00885   }
00886 }
00887 /*---------------------------------------------------------------------------*/
00888 /**
00889  * Redraws a widget.
00890  *
00891  * This function will set a flag which causes the widget to be redrawn
00892  * next time the CTK process is scheduled.
00893  *
00894  * \param widget The widget that is to be redrawn.
00895  *
00896  * \note This function should usually not be called directly since it
00897  * requires typecasting of the widget parameter. The wrapper macro
00898  * CTK_WIDGET_REDRAW() does the required typecast and should be used
00899  * instead.
00900  */
00901 /*---------------------------------------------------------------------------*/
00902 void
00903 ctk_widget_redraw(struct ctk_widget *widget)
00904 {
00905   if(mode != CTK_MODE_NORMAL || widget == NULL) {
00906     return;
00907   }
00908 
00909   /* Since this function isn't called by CTK itself, we only queue the
00910      redraw request. */
00911   add_redrawwidget(widget);
00912 }
00913 /*---------------------------------------------------------------------------*/
00914 /**
00915  * Adds a widget to a window.
00916  *
00917  * This function adds a widget to a window. The order of which the
00918  * widgets are added is important, as it sets the order to which
00919  * widgets are cycled with the widget selection keys.
00920  *
00921  * \param window The window to which the widhet should be added.
00922  * \param widget The widget to be added.
00923  */
00924 /*---------------------------------------------------------------------------*/
00925 void CC_FASTCALL
00926 ctk_widget_add(CC_REGISTER_ARG struct ctk_window *window,
00927                CC_REGISTER_ARG struct ctk_widget *widget)
00928 {
00929   if(widget->type == CTK_WIDGET_LABEL ||
00930      widget->type == CTK_WIDGET_SEPARATOR) {
00931     widget->next = window->inactive;
00932     window->inactive = widget;
00933     widget->window = window;
00934   } else {
00935     widget->next = window->active;
00936     window->active = widget;
00937     widget->window = window;
00938   }
00939 }
00940 /*---------------------------------------------------------------------------*/
00941 /**
00942  * Gets the width of the desktop.
00943  *
00944  * \param d The desktop.
00945  * \return The width of the desktop, in characters.
00946  *
00947  * \note The d parameter is currently unused and must be set to NULL.
00948  */
00949 /*---------------------------------------------------------------------------*/
00950 unsigned char
00951 ctk_desktop_width(struct ctk_desktop *d)
00952 {
00953   return ctk_draw_width();
00954 }
00955 /*---------------------------------------------------------------------------*/
00956 /**
00957  * Gets the height of the desktop.
00958  *
00959  * \param d The desktop.
00960  * \return The height of the desktop, in characters.
00961  *
00962  * \note The d parameter is currently unused and must be set to NULL.
00963  */
00964 /*---------------------------------------------------------------------------*/
00965 unsigned char
00966 ctk_desktop_height(struct ctk_desktop *d)
00967 {
00968   return ctk_draw_height();
00969 }
00970 /*---------------------------------------------------------------------------*/
00971 /**
00972  * \internal Selects a widget in the window of the widget.
00973  *
00974  * \param focus The widget to be focused.
00975  */
00976 /*---------------------------------------------------------------------------*/
00977 static void CC_FASTCALL
00978 select_widget(struct ctk_widget *focus)
00979 {
00980   struct ctk_window *window;
00981 
00982   window = focus->window;
00983   
00984   if(focus != window->focused) {
00985     window->focused = focus;
00986     /* The operation changed the focus, so we emit a "hover" signal
00987        for those widgets that support it. */
00988     
00989     if(window->focused->type == CTK_WIDGET_HYPERLINK) {
00990       process_post(window->owner, ctk_signal_hyperlink_hover, window->focused);
00991     } else if(window->focused->type == CTK_WIDGET_BUTTON) {
00992       process_post(window->owner, ctk_signal_button_hover, window->focused);
00993     }
00994     
00995     add_redrawwidget(window->focused);
00996 
00997     process_post(focus->window->owner, ctk_signal_widget_select, focus);
00998   }
00999 }
01000 /*---------------------------------------------------------------------------*/
01001 #define UP 0
01002 #define DOWN 1
01003 #define LEFT 2
01004 #define RIGHT 3
01005 static void CC_FASTCALL
01006 switch_focus_widget(unsigned char direction)
01007 {
01008 #if CTK_CONF_WINDOWS
01009   register struct ctk_window *window;
01010 #endif /* CTK_CONF_WINDOWS */
01011   register struct ctk_widget *focus;
01012   struct ctk_widget *widget;
01013   
01014 #if CTK_CONF_WINDOWS
01015   if(dialog != NULL) {
01016     window = dialog;
01017   } else {
01018     window = windows;
01019   }
01020 
01021   /* If there are no windows open, we move focus around between the
01022      icons on the root window instead. */
01023   if(window == NULL) {
01024     window = &desktop_window;
01025   }
01026 #else /* CTK_CONF_WINDOWS */
01027   if(window == NULL) {
01028     return;
01029   }
01030 #endif /* CTK_CONF_WINDOWS */
01031  
01032   focus = window->focused;
01033   if(focus == NULL) {
01034     focus = window->active;
01035     if(focus == NULL) {
01036       return;
01037     }
01038   }
01039   add_redrawwidget(focus);
01040   
01041   if((direction & 1) == 0) {
01042     /* Move focus "up" */
01043     focus = focus->next;
01044   } else {
01045     /* Move focus "down" */
01046     for(widget = window->active;
01047         widget != NULL; widget = widget->next) {
01048         if(widget->next == focus) {
01049           break;
01050         }
01051     }
01052     focus = widget;
01053     if(focus == NULL) {
01054       if(window->active != NULL) {
01055         for(focus = window->active;
01056             focus->next != NULL; focus = focus->next);
01057       }
01058     }
01059   }
01060   if(focus == NULL) {
01061     focus = window->active;
01062   }
01063 
01064   select_widget(focus);
01065 }
01066 /*---------------------------------------------------------------------------*/
01067 #if CTK_CONF_MENUS
01068 static void
01069 switch_open_menu(unsigned char rightleft)
01070 {
01071   struct ctk_menu *menu;
01072   
01073   if(rightleft == 0) {
01074     /* Move right */
01075     for(menu = menus.menus; menu != NULL; menu = menu->next) {
01076       if(menu->next == menus.open) {
01077         break;
01078       }
01079     }
01080     lastmenu = menus.open;
01081     menus.open = menu;
01082     if(menus.open == NULL) {
01083       for(menu = menus.menus;
01084           menu->next != NULL; menu = menu->next);
01085       menus.open = menu;
01086     }
01087   } else {
01088     /* Move to left */
01089     lastmenu = menus.open;
01090     menus.open = menus.open->next;
01091     if(menus.open == NULL) {
01092       menus.open = menus.menus;
01093     }
01094   }
01095 
01096   menus.open->active = 0;
01097 }
01098 /*---------------------------------------------------------------------------*/
01099 static void
01100 switch_menu_item(unsigned char updown)
01101 {
01102   register struct ctk_menu *m;
01103 
01104   m = menus.open;
01105   
01106   if(updown == 0) {
01107     /* Move up */
01108     if(m->active == 0) {
01109       m->active = m->nitems - 1;
01110     } else {
01111       --m->active;
01112       if(m->items[m->active].title[0] == '-') {
01113         --m->active;
01114       }
01115     }
01116   } else {
01117     /* Move down */
01118     if(m->active >= m->nitems - 1) {
01119       m->active = 0;
01120     } else {
01121       ++m->active;
01122       if(m->items[m->active].title[0] == '-') {
01123         ++m->active;
01124       }
01125     }
01126   }
01127 }
01128 #endif /* CTK_CONF_MENUS */
01129 /*---------------------------------------------------------------------------*/
01130 static unsigned char CC_FASTCALL
01131 activate(CC_REGISTER_ARG struct ctk_widget *w)
01132 {
01133   if(w->type == CTK_WIDGET_BUTTON) {
01134 #if CTK_CONF_WINDOWCLOSE
01135     if(w == (struct ctk_widget *)&windows->closebutton) {
01136       process_post(w->window->owner, ctk_signal_window_close, windows);
01137       ctk_window_close(windows);
01138       return REDRAW_ALL;
01139     } else
01140 #endif /* CTK_CONF_WINDOWCLOSE */
01141 #if CTK_CONF_WINDOWMOVE
01142     if(w == (struct ctk_widget *)&windows->titlebutton) {
01143       mode = CTK_MODE_WINDOWMOVE;
01144       return REDRAW_ALL;
01145     } else
01146 #endif /* CTK_CONF_WINDOWMOVE */
01147     {
01148       process_post(w->window->owner, ctk_signal_widget_activate, w);
01149     }
01150 #if CTK_CONF_ICONS
01151   } else if(w->type == CTK_WIDGET_ICON) {
01152     if(w->widget.icon.owner != PROCESS_NONE) {
01153       process_post(w->widget.icon.owner, ctk_signal_widget_activate, w);
01154     } else {
01155       process_post(w->window->owner, ctk_signal_widget_activate, w);
01156     }
01157 #endif /* CTK_CONF_ICONS */
01158   } else if(w->type == CTK_WIDGET_HYPERLINK) {
01159     process_post(PROCESS_BROADCAST, ctk_signal_hyperlink_activate, w);
01160   } else if(w->type == CTK_WIDGET_TEXTENTRY) {
01161     if(w->widget.textentry.state == CTK_TEXTENTRY_NORMAL) {
01162       w->widget.textentry.state = CTK_TEXTENTRY_EDIT;
01163       textentry_input(0, (struct ctk_textentry *)w);
01164     } else {
01165       w->widget.textentry.state = CTK_TEXTENTRY_NORMAL;
01166       process_post(w->window->owner, ctk_signal_widget_activate, w);
01167     }
01168     add_redrawwidget(w);
01169     return REDRAW_WIDGETS;
01170   } else {
01171     process_post(w->window->owner, ctk_signal_widget_activate, w);
01172   }
01173   return REDRAW_NONE;
01174 }
01175 /*---------------------------------------------------------------------------*/
01176 #ifdef SDCC
01177 /* Dummy function that we define to keep sdcc happy - with sdcc,
01178    function pointers cannot be NULL. ctk_textentry_input is typedef'd
01179    in ctk/ctk.h, hence the strange-looking function signature. */
01180 unsigned char
01181 ctk_textentry_input_null(ctk_arch_key_t c, struct ctk_textentry *t)
01182 {
01183   return 0;
01184 }
01185 #endif /* SDCC */
01186 /*---------------------------------------------------------------------------*/
01187 static void CC_FASTCALL
01188 textentry_input(ctk_arch_key_t c, CC_REGISTER_ARG struct ctk_textentry *t)
01189 {
01190   register char *cptr, *cptr2;
01191   static unsigned char len, txpos, typos, tlen;
01192 
01193   if(t->input != NULL && t->input(c, t)) {
01194     return;
01195   }
01196 
01197   txpos = t->xpos;
01198   typos = t->ypos;
01199   tlen = t->len;
01200 
01201   cptr = &t->text[txpos + typos * (tlen + 1)];
01202       
01203   switch(c) {
01204   case CH_CURS_LEFT:
01205     if(txpos > 0) {
01206       --txpos;
01207     }
01208     break;
01209     
01210   case CH_CURS_RIGHT:
01211     if(txpos < tlen - 1 && *cptr != 0) {
01212       ++txpos;
01213     }
01214     break;
01215 
01216   case CH_CURS_UP:
01217     txpos = 0;
01218     break;
01219     
01220   case 0:
01221   case CH_CURS_DOWN:
01222     txpos = (unsigned char)strlen(t->text);
01223     if(txpos == tlen) {
01224       --txpos;
01225     }
01226     break;
01227     
01228   case CH_ENTER:
01229     activate((struct ctk_widget *)t);
01230     switch_focus_widget(DOWN);
01231     break;
01232     
01233   case CTK_CONF_WIDGETDOWN_KEY:
01234     t->state = CTK_TEXTENTRY_NORMAL;
01235     switch_focus_widget(DOWN);
01236     break;
01237   case CTK_CONF_WIDGETUP_KEY:
01238     t->state = CTK_TEXTENTRY_NORMAL;
01239     switch_focus_widget(UP);
01240     break;
01241     
01242   default:
01243     len = tlen - txpos;
01244     if(c == CH_DEL) {
01245       if(len == 1 && *cptr != 0) {
01246         *cptr = 0;
01247       } else {
01248         if(txpos > 0) {
01249           --txpos;
01250           strcpy(cptr - 1, cptr);
01251         }
01252       }
01253     } else {
01254       if(ctk_arch_isprint(c)) {
01255         if(len > 1) {
01256           cptr2 = cptr + len - 1;
01257           while(cptr2 > cptr) {
01258             *cptr2 = *(cptr2 - 1);
01259             --cptr2;
01260           }
01261           ++txpos;
01262         }
01263         *cptr = c;
01264       }
01265     }
01266     break;
01267   }
01268 
01269   t->xpos = txpos;
01270   t->ypos = typos;
01271 }
01272 /*---------------------------------------------------------------------------*/
01273 #if CTK_CONF_MENUS
01274 static unsigned char
01275 activate_menu(void)
01276 {
01277   struct ctk_window *w;
01278   
01279   lastmenu = menus.open;
01280   if(menus.open == &desktopmenu) {
01281     for(w = windows; w != NULL; w = w->next) {
01282       if(w->title == desktopmenu.items[desktopmenu.active].title) {
01283         ctk_window_open(w);
01284         menus.open = NULL;
01285         return REDRAW_ALL;
01286       }
01287     }
01288   } else {
01289     process_post(PROCESS_BROADCAST, ctk_signal_menu_activate, menus.open);
01290   }
01291   menus.open = NULL;
01292   return REDRAW_MENUPART;
01293 }
01294 /*---------------------------------------------------------------------------*/
01295 static unsigned char
01296 menus_input(ctk_arch_key_t c)
01297 {
01298   if(menus.open->nitems > maxnitems) {
01299     maxnitems = menus.open->nitems;
01300   }
01301   
01302   switch(c) {
01303   case CH_CURS_RIGHT:
01304     switch_open_menu(1);
01305     return REDRAW_MENUPART;
01306 
01307   case CH_CURS_DOWN:
01308     switch_menu_item(1);
01309     return REDRAW_MENUS;
01310 
01311   case CH_CURS_LEFT:
01312     switch_open_menu(0);
01313     return REDRAW_MENUPART;
01314 
01315   case CH_CURS_UP:
01316     switch_menu_item(0);
01317     return REDRAW_MENUS;
01318     
01319   case CH_ENTER:
01320     return activate_menu();
01321 
01322   case CTK_CONF_MENU_KEY:
01323     lastmenu = menus.open;
01324     menus.open = NULL;
01325     return REDRAW_MENUPART;
01326   }
01327 
01328   return REDRAW_NONE;
01329 }
01330 #endif /* CTK_CONF_MENUS */
01331 /*---------------------------------------------------------------------------*/
01332 #if CTK_CONF_SCREENSAVER
01333 static void
01334 handle_timer(void)
01335 {
01336   if(mode == CTK_MODE_NORMAL) {
01337     ++screensaver_timer;
01338     if(screensaver_timer >= ctk_screensaver_timeout) {
01339       process_post(PROCESS_BROADCAST, ctk_signal_screensaver_start, NULL);
01340 #ifdef CTK_SCREENSAVER_INIT
01341       CTK_SCREENSAVER_INIT();
01342 #endif /* CTK_SCREENSAVER_INIT */
01343 
01344       screensaver_timer = 0;
01345     }
01346   }
01347 }
01348 #endif /* CTK_CONF_SCREENSAVER */
01349 /*---------------------------------------------------------------------------*/
01350 static void
01351 unfocus_widget(CC_REGISTER_ARG struct ctk_widget *w)
01352 {
01353   if(w != NULL) {
01354     redraw |= REDRAW_WIDGETS;
01355     add_redrawwidget(w);
01356     if(CTK_WIDGET_TYPE(w) == CTK_WIDGET_TEXTENTRY) {
01357       ((struct ctk_textentry *)w)->state =
01358         CTK_TEXTENTRY_NORMAL;
01359     }
01360     w->window->focused = NULL;
01361   }
01362 }
01363 /*---------------------------------------------------------------------------*/
01364 PROCESS_THREAD(ctk_process, ev, data)
01365 {
01366   static ctk_arch_key_t c;
01367   static unsigned char i;
01368 #if CTK_CONF_WINDOWS
01369   register struct ctk_window *window;
01370 #endif /* CTK_CONF_WINDOWS */
01371   register struct ctk_widget *widget;
01372   register struct ctk_widget **widgetptr;
01373 #if CTK_CONF_MOUSE_SUPPORT
01374   static unsigned char mxc, myc, mouse_button_changed, mouse_moved,
01375     mouse_clicked;
01376 #if CTK_CONF_MENUS
01377   static unsigned char menux;
01378   register struct ctk_menu *menu;
01379 #endif /* CTK_CONF_MENUS */
01380 #endif /* CTK_CONF_MOUSE_SUPPORT */
01381   
01382   PROCESS_BEGIN();
01383   
01384 #if CTK_CONF_MENUS
01385   ctk_menu_new(&desktopmenu, "Desktop");
01386   make_desktopmenu();
01387   menus.menus = menus.desktopmenu = &desktopmenu;
01388 #endif /* CTK_CONF_MENUS */
01389 
01390 #if CTK_CONF_MOUSE_SUPPORT
01391   ctk_mouse_init();
01392   ctk_mouse_show();
01393 #endif /* CTK_CONF_MOUSE_SUPPORT */
01394   
01395   ctk_restore();
01396 
01397 #if CTK_CONF_WINDOWS
01398   desktop_window.owner = &ctk_process;
01399 #endif /* CTK_CONF_WINDOWS */
01400 
01401   ctk_signal_keypress = process_alloc_event();
01402   
01403   ctk_signal_button_activate =
01404     ctk_signal_widget_activate = process_alloc_event();
01405   
01406   ctk_signal_button_hover =
01407     ctk_signal_hyperlink_hover =
01408     ctk_signal_widget_select = process_alloc_event();
01409   
01410   ctk_signal_hyperlink_activate = process_alloc_event();
01411 
01412   ctk_signal_menu_activate = process_alloc_event();
01413 
01414   ctk_signal_window_close = process_alloc_event();
01415 
01416 #if CTK_CONF_MOUSE_SUPPORT
01417   ctk_signal_pointer_move = process_alloc_event();
01418   ctk_signal_pointer_button = process_alloc_event();
01419 #endif /* CTK_CONF_MOUSE_SUPPORT */
01420 
01421 #if CTK_CONF_SCREENSAVER
01422   ctk_signal_screensaver_start = process_alloc_event();
01423   ctk_signal_screensaver_stop = process_alloc_event();
01424 #endif /* CTK_CONF_SCREENSAVER */
01425 
01426   mode = CTK_MODE_NORMAL;
01427 
01428 #if CTK_CONF_ICONS
01429   iconx = ICONX_START;
01430   icony = ICONY_START;
01431 #endif /* CTK_CONF_ICONS */
01432 
01433 #if CTK_CONF_SCREENSAVER
01434   timer_set(&timer, CLOCK_SECOND);
01435 #endif /* CTK_CONF_SCREENSAVER */
01436   
01437   while(1) {
01438     process_poll(&ctk_process);
01439     PROCESS_WAIT_EVENT();
01440     
01441 #if CTK_CONF_SCREENSAVER
01442     if(timer_expired(&timer)) {
01443       timer_reset(&timer);
01444       handle_timer();
01445     }
01446 #endif /* CTK_CONF_SCREENSAVER */
01447 
01448 #if CTK_CONF_MENUS
01449     if(menus.open != NULL) {
01450       maxnitems = menus.open->nitems;
01451     } else {
01452       maxnitems = 0;
01453     }
01454 #endif /* CTK_CONF_MENUS */
01455 
01456 #if CTK_CONF_MOUSE_SUPPORT
01457     mouse_button_changed = mouse_moved = mouse_clicked = 0;
01458 
01459     /* See if there is any change in the buttons. */
01460     if(ctk_mouse_button() != mouse_button) {
01461       mouse_button = ctk_mouse_button();
01462       mouse_button_changed = 1;
01463       if(mouse_button == 0) {
01464         mouse_clicked = 1;
01465       }
01466     }
01467   
01468     /* Check if the mouse pointer has moved. */
01469     if(ctk_mouse_x() != mouse_x ||
01470        ctk_mouse_y() != mouse_y) {
01471       mouse_x = ctk_mouse_x();
01472       mouse_y = ctk_mouse_y();
01473       mouse_moved = 1;
01474     }
01475 
01476     mxc = ctk_mouse_xtoc(mouse_x);
01477     myc = ctk_mouse_ytoc(mouse_y);
01478 #endif /* CTK_CONF_MOUSE_SUPPORT */
01479 
01480 #if CTK_CONF_SCREENSAVER
01481     if(mode == CTK_MODE_SCREENSAVER) {
01482       if(ctk_arch_keyavail()
01483 #if CTK_CONF_MOUSE_SUPPORT
01484          || mouse_moved || mouse_button_changed
01485 #endif /* CTK_CONF_MOUSE_SUPPORT */
01486          ) {
01487         process_post(PROCESS_BROADCAST, ctk_signal_screensaver_stop, NULL);
01488         mode = CTK_MODE_NORMAL;
01489       }
01490     } else
01491 #endif /* CTK_CONF_SCREENSAVER */
01492       if(mode == CTK_MODE_NORMAL) {
01493 #if CTK_CONF_MOUSE_SUPPORT
01494         /* If there is any change in the mouse conditions, find out in
01495            which window the mouse pointer currently is in order to send
01496            the correct signals, or bring a window to focus. */
01497         if(mouse_moved || mouse_button_changed) {
01498           ctk_mouse_show();
01499 #if CTK_CONF_SCREENSAVER
01500           screensaver_timer = 0;
01501 #endif /* CTK_CONF_SCREENSAVER */
01502       
01503 #if CTK_CONF_MENUS
01504           if(myc == 0) {
01505             /* Here we should do whatever needs to be done when the mouse
01506                moves around and clicks in the menubar. */
01507             if(mouse_clicked) {
01508               static unsigned char titlelen;
01509           
01510               /* Find out which menu that the mouse pointer is in. Start
01511                  with the ->next menu after the desktop menu. We assume
01512                  that the menus start one character from the left screen
01513                  side and that the desktop menu is farthest to the
01514                  right. */
01515               menux = 1;
01516               for(menu = menus.menus->next;
01517                   menu != NULL; menu = menu->next) {
01518                 titlelen = menu->titlelen;
01519                 if(mxc >= menux && mxc <= menux + titlelen) {
01520                   break;
01521                 }
01522                 menux += titlelen;
01523               }
01524           
01525               /* Also check desktop menu. */
01526               if(mxc >= width - 7 &&
01527                  mxc <= width - 1) {
01528                 menu = &desktopmenu;
01529               }
01530           
01531               menus.open = menu;
01532               redraw |= REDRAW_MENUPART;
01533             }
01534           } else {
01535             --myc;
01536 
01537             if(menus.open != NULL) {
01538               static unsigned char nitems;
01539           
01540               /* Do whatever needs to be done when a menu is open. */
01541 
01542               /* First check if the mouse pointer is in the currently open
01543                  menu. */
01544               if(menus.open == &desktopmenu) {
01545                 menux = width - CTK_CONF_MENUWIDTH;
01546               } else {
01547                 menux = 1;
01548                 for(menu = menus.menus->next; menu != menus.open;
01549                     menu = menu->next) {
01550                   menux += menu->titlelen;
01551                 }
01552               }
01553 
01554               nitems = menus.open->nitems;
01555               /* Find out which of the menu items the mouse is pointing
01556                  to. */
01557               if(mxc >= menux && mxc <= menux + CTK_CONF_MENUWIDTH) {
01558                 if(myc <= nitems) {
01559                   menus.open->active = myc;
01560                 } else {
01561                   menus.open->active = nitems - 1;
01562                 }
01563               }
01564           
01565               if(mouse_clicked) {
01566                 if(mxc >= menux && mxc <= menux + CTK_CONF_MENUWIDTH &&
01567                    myc <= nitems) {
01568                   redraw |= activate_menu();
01569                 } else {
01570                   lastmenu = menus.open;
01571                   menus.open = NULL;
01572                   redraw |= REDRAW_MENUPART;
01573                 }
01574               } else {
01575                 redraw |= REDRAW_MENUS;
01576               }
01577             } else {
01578 #endif /* CTK_CONF_MENUS */
01579 
01580 #if CTK_CONF_WINDOWS
01581               /* Walk through the windows from top to bottom to see in
01582                  which window the mouse pointer is. */
01583               if(dialog != NULL) {
01584                 window = dialog;
01585               } else {
01586                 for(window = windows; window != NULL;
01587                     window = window->next) {
01588               
01589                   /* Check if the mouse is within the window. */
01590                   if(mxc >= window->x &&
01591                      mxc <= window->x + window->w +
01592                      2 * ctk_draw_windowborder_width &&
01593                      myc >= window->y &&
01594                      myc <= window->y + window->h +
01595                      ctk_draw_windowtitle_height +
01596                      ctk_draw_windowborder_height) {
01597                     break;
01598                   }
01599                 }
01600               }
01601 
01602               /* If we didn't find any window, and there are no windows
01603                  open, the mouse pointer will definately be within the
01604                  background desktop window. */
01605               if(window == NULL) {
01606                 window = &desktop_window;
01607               }
01608 
01609               /* If the mouse pointer moves around outside of the
01610                  currently focused window (or dialog), we should not have
01611                  any focused widgets in the focused window so we make sure
01612                  that there are none. */
01613               if(windows != NULL &&
01614                  window != windows &&
01615                  windows->focused != NULL){
01616                 unfocus_widget(windows->focused);
01617               }
01618 #endif /* CTK_CONF_WINDOWS */
01619 
01620               if(window != NULL) {
01621 #if CTK_CONF_WINDOWS
01622                 /* If the mouse was clicked outside of the current window,
01623                    we bring the clicked window to front. */
01624                 if(dialog == NULL &&
01625                    window != &desktop_window &&
01626                    window != windows &&
01627                    mouse_clicked) {
01628                   /* Bring window to front. */
01629                   ctk_window_open(window);
01630                   redraw |= REDRAW_ALL;
01631                 } else {
01632 
01633                   /* Find out which widget currently is under the mouse
01634                      pointer and give it focus, unless it already has
01635                      focus. */
01636                   mxc = mxc - window->x - ctk_draw_windowborder_width;
01637                   myc = myc - window->y - ctk_draw_windowtitle_height;
01638 #endif /* CTK_CONF_WINDOWS */
01639             
01640                   /* See if the mouse pointer is on a widget. If so, it
01641                      should be selected and, if the button is clicked,
01642                      activated. */
01643                   for(widget = window->active; widget != NULL;
01644                       widget = widget->next) {
01645                 
01646                     if(mxc >= widget->x &&
01647                        mxc <= widget->x + widget->w + 1 &&
01648                        myc >= widget->y &&
01649                        myc <= widget->y + widget->h - 1) {
01650                       break;
01651                     }
01652                   }
01653             
01654                   /* if the mouse is moved in the focused window, we emit
01655                      a ctk_signal_pointer_move signal to the owner of the
01656                      window. */
01657                   if(mouse_moved
01658 #if CTK_CONF_WINDOWS
01659                      && (window != &desktop_window || windows == NULL)
01660 #endif /* CTK_CONF_WINDOWS */
01661                       ) {
01662 
01663                     process_post(window->owner, ctk_signal_pointer_move, NULL);
01664 
01665                     /* If there was a focused widget that is not below the
01666                        mouse pointer, we remove focus from the widget and
01667                        redraw it. */
01668                     if(window->focused != NULL &&
01669                        widget != window->focused) {
01670                       unfocus_widget(window->focused);
01671                     }
01672                     redraw |= REDRAW_WIDGETS;
01673                     if(widget != NULL) {
01674                       select_widget(widget);
01675                     }
01676                   }
01677             
01678                   if(mouse_button_changed) {
01679                     process_post(window->owner, ctk_signal_pointer_button,
01680                                  (process_data_t)(size_t)mouse_button);
01681                     if(mouse_clicked && widget != NULL) {
01682                       select_widget(widget);
01683                       redraw |= activate(widget);
01684                     }
01685                   }
01686 #if CTK_CONF_WINDOWS
01687                 }
01688 #endif /* CTK_CONF_WINDOWS */
01689               }
01690 #if CTK_CONF_MENUS
01691             }
01692           }
01693 #endif /* CTK_CONF_MENUS */
01694         }
01695 #endif /* CTK_CONF_MOUSE_SUPPORT */
01696     
01697         while(ctk_arch_keyavail()) {
01698 
01699           ctk_mouse_hide();
01700       
01701 #if CTK_CONF_SCREENSAVER
01702           screensaver_timer = 0;
01703 #endif /* CTK_CONF_SCREENSAVER */
01704       
01705           c = ctk_arch_getkey();
01706       
01707 #if CTK_CONF_WINDOWS
01708           if(dialog != NULL) {
01709             window = dialog;
01710           } else if(windows != NULL) {
01711             window = windows;
01712           } else {
01713             window = &desktop_window;
01714           }
01715 #else /* CTK_CONF_WINDOWS */
01716           if(window == NULL) {
01717             continue;
01718           }
01719 #endif /* CTK_CONF_WINDOWS */
01720 
01721           /* Allow to exit the process owning the foreground window by
01722              pressing ctrl-c. This is especially useful if there's no
01723              closebutton on the window frames (or no windows at all).
01724           */
01725           if(c == 3) {
01726             process_post(window->owner, PROCESS_EVENT_EXIT, NULL);
01727           }
01728 
01729           widget = window->focused;
01730           
01731           if(widget != NULL &&
01732              widget->type == CTK_WIDGET_TEXTENTRY &&
01733              widget->widget.textentry.state == CTK_TEXTENTRY_EDIT) {
01734             textentry_input(c, (struct ctk_textentry *)widget);
01735             add_redrawwidget(widget);
01736 #if CTK_CONF_MENUS
01737           } else if(menus.open != NULL) {
01738             redraw |= menus_input(c);
01739 #endif /* CTK_CONF_MENUS */
01740           } else {
01741             switch(c) {
01742             case CTK_CONF_WIDGETDOWN_KEY:
01743               switch_focus_widget(DOWN);
01744               break;
01745             case CTK_CONF_WIDGETUP_KEY:
01746               switch_focus_widget(UP);
01747               break;
01748 #if CTK_CONF_MENUS
01749             case CTK_CONF_MENU_KEY:
01750               if(dialog == NULL) {
01751                 if(lastmenu == NULL) {
01752                   menus.open = menus.menus;
01753                 } else {
01754                   menus.open = lastmenu;
01755                 }
01756                 menus.open->active = 0;
01757                 redraw |= REDRAW_MENUS;
01758               }
01759               break;
01760 #endif /* CTK_CONF_MENUS */
01761 #if CTK_CONF_WINDOWS
01762             case CTK_CONF_WINDOWSWITCH_KEY:
01763               if(windows != NULL) {
01764                 for(window = windows; window->next != NULL;
01765                     window = window->next);
01766                 ctk_window_open(window);
01767               }
01768               break;
01769 #endif /* CTK_CONF_WINDOWS */
01770             default:
01771 
01772               if(c == CH_ENTER &&
01773                  widget != NULL) {
01774                 redraw |= activate(widget);
01775               } else {
01776                 if(widget != NULL &&
01777                    widget->type == CTK_WIDGET_TEXTENTRY) {
01778                   if(widget->widget.textentry.state == CTK_TEXTENTRY_NORMAL) {
01779                     widget->widget.textentry.state = CTK_TEXTENTRY_EDIT;
01780                     textentry_input(0, (struct ctk_textentry *)widget);
01781                   }
01782                   textentry_input(c, (struct ctk_textentry *)widget);
01783                   add_redrawwidget(widget);
01784                 } else {
01785                   unfocus_widget(window->focused);
01786                   process_post_synch(window->owner, ctk_signal_keypress,
01787                                      (process_data_t)(size_t)c);
01788                 }
01789               }
01790               break;
01791             }
01792           }
01793 
01794 #if 0
01795           if(redraw & REDRAW_WIDGETS) {
01796             widgetptr = redraw_widgets;
01797             for(i = 0; i < MAX_REDRAWWIDGETS; ++i) {
01798               widget_redraw(*widgetptr);
01799               *widgetptr = NULL;
01800               ++widgetptr;
01801             }
01802             redraw &= ~REDRAW_WIDGETS;
01803             redraw_widgetptr = 0;
01804           }
01805 #endif /* 0 */
01806         }
01807 #if CTK_CONF_WINDOWMOVE
01808       } else if(mode == CTK_MODE_WINDOWMOVE) {
01809 
01810         redraw = 0;
01811 
01812         window = windows;
01813 
01814 #if CTK_CONF_MOUSE_SUPPORT
01815 
01816         /* If the mouse has moved, we move the window as well. */
01817         if(mouse_moved) {
01818 
01819           if(window->w + mxc + 2 >= width) {
01820             window->x = width - 2 - window->w;
01821           } else {
01822             window->x = mxc;
01823           }
01824 
01825           if(window->h + myc + ctk_draw_windowtitle_height +
01826              ctk_draw_windowborder_height >= height) {
01827             window->y = height - window->h -
01828               ctk_draw_windowtitle_height - ctk_draw_windowborder_height;
01829           } else {
01830             window->y = myc;
01831           }
01832 #if CTK_CONF_MENUS
01833           if(window->y > 0) {
01834             --window->y;
01835           }
01836 #endif /* CTK_CONF_MENUS */
01837 
01838           redraw = REDRAW_ALL;
01839         }
01840     
01841         /* Check if the mouse has been clicked, and stop moving the window
01842            if so. */
01843         if(mouse_button_changed &&
01844            mouse_button == 0) {
01845           mode = CTK_MODE_NORMAL;
01846           redraw = REDRAW_ALL;
01847         }
01848 #endif /* CTK_CONF_MOUSE_SUPPORT */
01849     
01850         while(mode == CTK_MODE_WINDOWMOVE && ctk_arch_keyavail()) {
01851     
01852 #if CTK_CONF_SCREENSAVER
01853           screensaver_timer = 0;
01854 #endif /* CTK_CONF_SCREENSAVER */
01855       
01856           c = ctk_arch_getkey();
01857       
01858           switch(c) {
01859           case CH_CURS_RIGHT:
01860             ++window->x;
01861             if(window->x + window->w + 1 >= width) {
01862               --window->x;
01863             }
01864             redraw = REDRAW_ALL;
01865             break;
01866           case CH_CURS_LEFT:
01867             if(window->x > 0) {
01868               --window->x;
01869             }
01870             redraw = REDRAW_ALL;
01871             break;
01872           case CH_CURS_DOWN:
01873             ++window->y;
01874             if(window->y + window->h + 1 + CTK_CONF_MENUS >= height) {
01875               --window->y;
01876             }
01877             redraw = REDRAW_ALL;
01878             break;
01879           case CH_CURS_UP:
01880             if(window->y > 0) {
01881               --window->y;
01882             }
01883             redraw = REDRAW_ALL;
01884             break;
01885           default:
01886             mode = CTK_MODE_NORMAL;
01887             redraw = REDRAW_ALL;
01888             break;
01889           }
01890         }
01891 #endif /* CTK_CONF_WINDOWMOVE */
01892       }
01893 
01894     if(redraw & REDRAW_ALL) {
01895       do_redraw_all(CTK_CONF_MENUS, height);
01896 #if CTK_CONF_MENUS
01897     } else if(redraw & REDRAW_MENUPART) {
01898       do_redraw_all(CTK_CONF_MENUS, maxnitems + 1);
01899     } else if(redraw & REDRAW_MENUS) {
01900       ctk_draw_menus(&menus);
01901 #endif /* CTK_CONF_MENUS */
01902     } else if(redraw & REDRAW_FOCUS) {
01903 #if CTK_CONF_WINDOWS
01904       if(dialog != NULL) {
01905         ctk_window_redraw(dialog);
01906       } else if(windows != NULL) {
01907         ctk_window_redraw(windows);
01908       } else {
01909         ctk_window_redraw(&desktop_window);
01910       }
01911 #else /* CTK_CONF_WINDOWS */
01912       if(window != NULL) {
01913         ctk_window_redraw(window);
01914       }
01915 #endif /* CTK_CONF_WINDOWS */
01916     } else if(redraw & REDRAW_WIDGETS) {
01917       widgetptr = redraw_widgets;
01918       for(i = 0; i < MAX_REDRAWWIDGETS; ++i) {
01919         widget_redraw(*widgetptr);
01920         *widgetptr = NULL;
01921         ++widgetptr;
01922       }
01923     }
01924     redraw = 0;
01925     redraw_widgetptr = 0;
01926   }
01927   
01928   PROCESS_END();
01929 }
01930 /*---------------------------------------------------------------------------*/
01931 /** @} */
01932 /** @} */