Contiki 2.6
|
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 /** @} */