Contiki 2.5
ctk.c
Go to the documentation of this file.
1 /**
2  * \defgroup ctk CTK graphical user interface
3  *
4  * The Contiki Toolkit (CTK) provides the graphical user interface for
5  * the Contiki system.
6  *
7  * @{
8  */
9 
10 /**
11  * \file
12  * The Contiki Toolkit CTK, the Contiki GUI.
13  * \author Adam Dunkels <adam@dunkels.com>
14  */
15 
16 /*
17  * Copyright (c) 2002-2003, Adam Dunkels.
18  * All rights reserved.
19  *
20  * Redistribution and use in source and binary forms, with or without
21  * modification, are permitted provided that the following conditions
22  * are met:
23  * 1. Redistributions of source code must retain the above copyright
24  * notice, this list of conditions and the following disclaimer.
25  * 2. Redistributions in binary form must reproduce the above
26  * copyright notice, this list of conditions and the following
27  * disclaimer in the documentation and/or other materials provided
28  * with the distribution.
29  * 3. The name of the author may not be used to endorse or promote
30  * products derived from this software without specific prior
31  * written permission.
32  *
33  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
34  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
35  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
37  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
38  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
39  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
40  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
41  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
42  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
43  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44  *
45  * This file is part of the Contiki operating system.
46  *
47  * $Id: ctk.c,v 1.26 2010/09/09 20:21:26 oliverschmidt Exp $
48  *
49  */
50 
51 #include <string.h>
52 
53 #include "contiki.h"
54 
55 #include "ctk/ctk.h"
56 #include "ctk/ctk-draw.h"
57 #include "ctk/ctk-mouse.h"
58 
59 static unsigned char height, width;
60 
61 static unsigned char mode;
62 
63 #if CTK_CONF_WINDOWS
64 static struct ctk_window desktop_window;
65 static struct ctk_window *windows;
66 static struct ctk_window *dialog;
67 #else /* CTK_CONF_WINDOWS */
68 static struct ctk_window *window;
69 #endif /* CTK_CONF_WINDOWS */
70 
71 #if CTK_CONF_MENUS
72 static struct ctk_menus menus;
73 static struct ctk_menu *lastmenu;
74 static struct ctk_menu desktopmenu;
75 static unsigned char maxnitems;
76 #endif /* CTK_CONF_MENUS */
77 
78 #ifndef NULL
79 #define NULL (void *)0
80 #endif /* NULL */
81 
82 #define REDRAW_NONE 0
83 #define REDRAW_ALL 1
84 #define REDRAW_FOCUS 2
85 #define REDRAW_WIDGETS 4
86 #define REDRAW_MENUS 8
87 #define REDRAW_MENUPART 16
88 
89 #define MAX_REDRAWWIDGETS 4
90 static unsigned char redraw;
91 static struct ctk_widget *redraw_widgets[MAX_REDRAWWIDGETS];
92 static unsigned char redraw_widgetptr;
93 
94 #if CTK_CONF_ICONS
95 static unsigned char iconx, icony;
96 #define ICONX_START (width - 6)
97 #define ICONY_START (height - 6 - CTK_CONF_MENUS)
98 #define ICONX_DELTA -16
99 #define ICONY_DELTA -5
100 #define ICONY_MAX height
101 #endif /* CTK_CONF_ICONS */
102 
103 #ifndef ctk_arch_keyavail
104 unsigned char ctk_arch_keyavail(void);
105 #endif /* ctk_arch_keyavail */
106 
107 #ifndef ctk_arch_getkey
109 #endif /* ctk_arch_getkey */
110 
111 #ifndef ctk_arch_isprint
112 unsigned char ctk_arch_isprint(ctk_arch_key_t key);
113 #endif /* ctk_arch_isprint */
114 
115 PROCESS(ctk_process, "CTK Contiki GUI");
116 
117 /**
118  * \defgroup ctkevents CTK events
119  * @{
120  */
121 process_event_t
122 
123  /**
124  * Emitted for every key being pressed.
125  *
126  * The key is passed as signal data.*/
128 
129  /** Emitted when a widget is activated (pressed). A pointer to the
130  widget is passed as signal data. */
132 
133  /** Same as ctk_signal_widget_activate. */
135 
136  /** Emitted when a widget is selected. A pointer to the widget is
137  passed as signal data. */
139 
140  /** Same as ctk_signal_widget_select. */
142 
143  /** Emitted when a hyperlink is activated. The signal is broadcast
144  to all listeners. */
146 
147  /** Same as ctk_signal_widget_select. */
149 
150  /** Emitted when a menu item is activated. The number of the menu
151  item is passed as signal data. */
152 process_event_t ctk_signal_menu_activate;
153 
154  /** Emitted when a window is closed. A pointer to the window is
155  passed as signal data. */
156 process_event_t ctk_signal_window_close;
157 
158 #if CTK_CONF_MOUSE_SUPPORT
159  /** Emitted when the mouse pointer is moved. A NULL pointer is
160  passed as signal data and it is up to the listening process to
161  check the position of the mouse using the CTK mouse API.*/
162 process_event_t ctk_signal_pointer_move,
163  /** Emitted when a mouse button is pressed. The button is passed as
164  signal data to the listening process. */
165  ctk_signal_pointer_button;
166 #endif /* CTK_CONF_MOUSE_SUPPORT */
167 
168 #if CTK_CONF_SCREENSAVER
169 /** Emitted when the user has been idle long enough for the
170  screensaver to start. */
171 process_event_t ctk_signal_screensaver_stop,
172  /** Emitted when the user presses a key or moves the mouse when the
173  screensaver is active. */
174  ctk_signal_screensaver_start;
175 #endif /* CTK_CONF_SCREENSAVER */
176 
177 /** @} */
178 
179 #if CTK_CONF_MOUSE_SUPPORT
180 unsigned short mouse_x, mouse_y, mouse_button;
181 #endif /* CTK_CONF_MOUSE_SUPPORT */
182 
183 #if CTK_CONF_SCREENSAVER
184 static unsigned short screensaver_timer = 0;
185 unsigned short ctk_screensaver_timeout = (5*60);
186 static struct timer timer;
187 #endif /* CTK_CONF_SCREENSAVER */
188 
189 static void CC_FASTCALL
190 textentry_input(ctk_arch_key_t c,
191  CC_REGISTER_ARG struct ctk_textentry *t);
192 
193 #if CTK_CONF_MENUS
194 /*---------------------------------------------------------------------------*/
195 /**
196  * \internal Creates the Desktop menu.
197  *
198  * Creates the leftmost menu, "Desktop". Since the desktop menu
199  * contains the list of all open windows, this function will be called
200  * whenever a window is opened or closed.
201  */
202 /*---------------------------------------------------------------------------*/
203 static void
204 make_desktopmenu(void)
205 {
206  struct ctk_window *w;
207 
208  desktopmenu.nitems = 0;
209 
210  if(windows == NULL) {
211  ctk_menuitem_add(&desktopmenu, "(No windows)");
212  } else {
213  for(w = windows; w != NULL; w = w->next) {
214  ctk_menuitem_add(&desktopmenu, w->title);
215  }
216  }
217 }
218 #endif /* CTK_CONF_MENUS */
219 /*---------------------------------------------------------------------------*/
220 #if CTK_CONF_ICONS
221 static void
222 arrange_icons(void)
223 {
224  struct ctk_widget *icon;
225 
226  iconx = ICONX_START;
227  icony = ICONY_START;
228 
229  for(icon = desktop_window.active; icon != NULL; icon = icon->next) {
230 
231  icon->x = iconx;
232  icon->y = icony;
233 
234  icony += ICONY_DELTA;
235  if(icony >= ICONY_MAX) {
236  icony = ICONY_START;
237  iconx += ICONX_DELTA;
238  }
239  }
240 }
241 #endif /* CTK_CONF_ICONS */
242 /*---------------------------------------------------------------------------*/
243 void
244 ctk_restore(void)
245 {
246  ctk_draw_init();
247 
248  height = ctk_draw_height();
249  width = ctk_draw_width();
250 
251 #if CTK_CONF_ICONS
252  arrange_icons();
253 #endif /* CTK_CONF_ICONS */
254 
255  redraw = REDRAW_ALL;
256 }
257 /*---------------------------------------------------------------------------*/
258 
259 /**
260  * \addtogroup ctkappfunc
261  * @{
262  */
263 
264 /*---------------------------------------------------------------------------*/
265 /**
266  * Sets the current CTK mode.
267  *
268  * The CTK mode can be either CTK_MODE_NORMAL, CTK_MODE_SCREENSAVER or
269  * CTK_MODE_EXTERNAL. CTK_MODE_NORMAL is the normal mode, in which
270  * keypresses and mouse pointer movements are processed and the screen
271  * is redrawn. In CTK_MODE_SCREENSAVER, no screen redraws are
272  * performed and the first key press or pointer movement will cause
273  * the ctk_signal_screensaver_stop to be emitted. In the
274  * CTK_MODE_EXTERNAL mode, key presses and pointer movements are
275  * ignored and no screen redraws are made.
276  *
277  * \param m The mode.
278  */
279 /*---------------------------------------------------------------------------*/
280 void
281 ctk_mode_set(unsigned char m) {
282  mode = m;
283 }
284 /*---------------------------------------------------------------------------*/
285 /**
286  * Retrieves the current CTK mode.
287  *
288  * \return The current CTK mode.
289  */
290 /*---------------------------------------------------------------------------*/
291 unsigned char
293  return mode;
294 }
295 /*---------------------------------------------------------------------------*/
296 /**
297  * Add an icon to the desktop.
298  *
299  * \param icon The icon to be added.
300  *
301  * \param p The process that owns the icon.
302  */
303 /*---------------------------------------------------------------------------*/
304 void
305 ctk_icon_add(CC_REGISTER_ARG struct ctk_widget *icon, struct process *p)
306 {
307 #if CTK_CONF_ICONS
308  icon->widget.icon.owner = p;
309  ctk_widget_add(&desktop_window, icon);
310  arrange_icons();
311 #endif /* CTK_CONF_ICONS */
312 }
313 #if CTK_CONF_WINDOWS
314 /*---------------------------------------------------------------------------*/
315 /**
316  * Open a dialog box.
317  *
318  * \param d The dialog to be opened.
319  */
320 /*---------------------------------------------------------------------------*/
321 void
322 ctk_dialog_open(struct ctk_window *d)
323 {
324  dialog = d;
325  redraw |= REDRAW_FOCUS;
326 }
327 /*---------------------------------------------------------------------------*/
328 /**
329  * Close the dialog box, if one is open.
330  *
331  */
332 /*---------------------------------------------------------------------------*/
333 void
334 ctk_dialog_close(void)
335 {
336  dialog = NULL;
337  redraw |= REDRAW_ALL;
338 }
339 #endif /* CTK_CONF_WINDOWS */
340 /*---------------------------------------------------------------------------*/
341 /**
342  * Open a window, or bring window to front if already open.
343  *
344  * \param w The window to be opened.
345  */
346 /*---------------------------------------------------------------------------*/
347 void
349 {
350 #if CTK_CONF_WINDOWS
351  struct ctk_window *w2;
352 
353  /* Check if already open. */
354  for(w2 = windows; w2 != w && w2 != NULL; w2 = w2->next);
355  if(w2 == NULL) {
356  /* Not open, so we add it at the head of the list of open
357  windows. */
358  w->next = windows;
359  if(windows != NULL) {
360  windows->prev = w;
361  }
362  windows = w;
363  w->prev = NULL;
364  } else {
365  /* Window already open, so we move it to the front of the windows
366  list. */
367  if(w != windows) {
368  if(w->next != NULL) {
369  w->next->prev = w->prev;
370  }
371  if(w->prev != NULL) {
372  w->prev->next = w->next;
373  }
374  w->next = windows;
375  windows->prev = w;
376  windows = w;
377  w->prev = NULL;
378  }
379  }
380 #else /* CTK_CONF_WINDOWS */
381  window = w;
382 #endif /* CTK_CONF_WINDOWS */
383 
384 #if CTK_CONF_MENUS
385  /* Recreate the Desktop menu's window entries.*/
386  make_desktopmenu();
387 #endif /* CTK_CONF_MENUS */
388 
389  redraw |= REDRAW_ALL;
390 }
391 /*---------------------------------------------------------------------------*/
392 /**
393  * Close a window if it is open.
394  *
395  * If the window is not open, this function does nothing.
396  *
397  * \param w The window to be closed.
398  */
399 /*---------------------------------------------------------------------------*/
400 void
402 {
403 #if CTK_CONF_WINDOWCLOSE
404  static struct ctk_window *w2;
405 
406  if(w == NULL) {
407  return;
408  }
409 
410  /* Check if the window to be closed is the first window on the list. */
411  if(w == windows) {
412  windows = w->next;
413  if(windows != NULL) {
414  windows->prev = NULL;
415  }
416  w->next = w->prev = NULL;
417  } else {
418  /* Otherwise we step through the list until we find the window
419  before the one to be closed. We then redirect its ->next
420  pointer and its ->next->prev. */
421  for(w2 = windows; w2 != NULL && w2->next != w; w2 = w2->next);
422 
423  if(w2 == NULL) {
424  /* The window wasn't open, so there is nothing more for us to do. */
425  return;
426  }
427 
428  if(w->next != NULL) {
429  w->next->prev = w->prev;
430  }
431  w2->next = w->next;
432 
433  w->next = w->prev = NULL;
434  }
435 
436 #if CTK_CONF_MENUS
437  /* Recreate the Desktop menu's window entries.*/
438  make_desktopmenu();
439 #endif /* CTK_CONF_MENUS */
440  redraw |= REDRAW_ALL;
441 #endif /* CTK_CONF_WINDOWCLOSE */
442 }
443 #if CTK_CONF_WINDOWS
444 /*---------------------------------------------------------------------------*/
445 /**
446  * \internal Create the move and close buttons on the window titlebar.
447  */
448 /*---------------------------------------------------------------------------*/
449 static void
450 make_windowbuttons(CC_REGISTER_ARG struct ctk_window *window)
451 {
452  unsigned char placement;
453 
454  if(ctk_draw_windowtitle_height >= 2) {
455  placement = -1 - ctk_draw_windowtitle_height/2;
456  } else {
457  placement = -1;
458  }
459 #if CTK_CONF_WINDOWMOVE
460  CTK_BUTTON_NEW(&window->titlebutton, 0, placement,
461  window->titlelen, window->title);
462 #else
463  CTK_LABEL_NEW(&window->titlebutton, 0, placement,
464  window->titlelen, 1, window->title);
465 #endif /* CTK_CONF_WINDOWMOVE */
466  CTK_WIDGET_ADD(window, &window->titlebutton);
467 
468 #if CTK_CONF_WINDOWCLOSE
469  CTK_BUTTON_NEW(&window->closebutton, window->w - 3, placement,
470  1, "x");
471 #else
472  CTK_LABEL_NEW(&window->closebutton, window->w - 4, placement,
473  3, 1, " ");
474 #endif /* CTK_CONF_WINDOWCLOSE */
475  CTK_WIDGET_ADD(window, &window->closebutton);
476 }
477 #endif /* CTK_CONF_WINDOWS */
478 /*---------------------------------------------------------------------------*/
479 /**
480  * Remove all widgets from a window.
481  *
482  * \param w The window to be cleared.
483  */
484 /*---------------------------------------------------------------------------*/
485 void
487 {
488  w->active = w->inactive = w->focused = NULL;
489 
490 #if CTK_CONF_WINDOWS
491  make_windowbuttons(w);
492 #endif /* CTK_CONF_WINDOWS */
493 }
494 /*---------------------------------------------------------------------------*/
495 /**
496  * Add a menu to the menu bar.
497  *
498  * \param menu The menu to be added.
499  *
500  * \note Do not call this function multiple times for the same menu,
501  * as no check is made to see if the menu already is in the menu bar.
502  */
503 /*---------------------------------------------------------------------------*/
504 void
505 ctk_menu_add(struct ctk_menu *menu)
506 {
507 #if CTK_CONF_MENUS
508  struct ctk_menu *m;
509 
510  if(lastmenu == NULL) {
511  lastmenu = menu;
512  }
513 
514  for(m = menus.menus; m->next != NULL; m = m->next) {
515  if(m == menu) {
516  return;
517  }
518  }
519  m->next = menu;
520  menu->next = NULL;
521 
522  redraw |= REDRAW_MENUPART;
523 #endif /* CTK_CONF_MENUS */
524 }
525 /*---------------------------------------------------------------------------*/
526 /**
527  * Remove a menu from the menu bar.
528  *
529  * \param menu The menu to be removed.
530  */
531 /*---------------------------------------------------------------------------*/
532 void
534 {
535 #if CTK_CONF_MENUS
536  struct ctk_menu *m;
537 
538  for(m = menus.menus; m->next != NULL; m = m->next) {
539  if(m->next == menu) {
540  m->next = menu->next;
541  if(menu == lastmenu) {
542  lastmenu = NULL;
543  }
544  redraw |= REDRAW_MENUPART;
545  return;
546  }
547  }
548 #endif /* CTK_CONF_MENUS */
549 }
550 /*---------------------------------------------------------------------------*/
551 /**
552  * \internal Redraws everything on the screen within the clip
553  * interval.
554  *
555  * \param clipy1 The upper bound of the clip interval
556  * \param clipy2 The lower bound of the clip interval
557  */
558 /*---------------------------------------------------------------------------*/
559 static void CC_FASTCALL
560 do_redraw_all(unsigned char clipy1, unsigned char clipy2)
561 {
562 #if CTK_CONF_WINDOWS
563  static struct ctk_widget *widget;
564  struct ctk_window *w;
565  unsigned char focus;
566 #endif /* CTK_CONF_WINDOWS */
567 
568  if(mode != CTK_MODE_NORMAL && mode != CTK_MODE_WINDOWMOVE) {
569  return;
570  }
571 
572  ctk_draw_clear(clipy1, clipy2);
573 
574 #if CTK_CONF_WINDOWS
575  /* Draw widgets in root window */
576  for(widget = desktop_window.active;
577  widget != NULL; widget = widget->next) {
578  ctk_draw_widget(widget, windows != NULL? 0: CTK_FOCUS_WINDOW, clipy1, clipy2);
579  }
580 
581  /* Draw windows */
582  if(windows != NULL) {
583  /* Find the last window.*/
584  for(w = windows; w->next != NULL; w = w->next);
585 
586  /* Draw the windows from back to front. */
587  for(; w != windows; w = w->prev) {
588  ctk_draw_clear_window(w, 0, clipy1, clipy2);
589  ctk_draw_window(w, 0, clipy1, clipy2, 1);
590  }
591 
592  /* Draw focused window */
593  focus = mode == CTK_MODE_WINDOWMOVE?
596  ctk_draw_clear_window(windows, focus, clipy1, clipy2);
597  ctk_draw_window(windows, focus, clipy1, clipy2, 1);
598  }
599 
600  /* Draw dialog (if any) */
601  if(dialog != NULL) {
602  ctk_draw_dialog(dialog);
603  }
604 #else /* CTK_CONF_WINDOWS */
605  if(window != NULL) {
606  ctk_draw_clear_window(window, CTK_FOCUS_WINDOW, clipy1, clipy2);
607  ctk_draw_window(window, CTK_FOCUS_WINDOW, clipy1, clipy2, 0);
608  }
609 #endif /* CTK_CONF_WINDOWS */
610 
611 #if CTK_CONF_MENUS
612  ctk_draw_menus(&menus);
613 #endif /* CTK_CONF_MENUS */
614 }
615 #if CTK_CONF_WINDOWS
616 /*---------------------------------------------------------------------------*/
617 /**
618  * Redraw the entire desktop.
619  *
620  * \param d The desktop to be redrawn.
621  *
622  * \note Currently the parameter d is not used, but must be set to
623  * NULL.
624  *
625  */
626 /*---------------------------------------------------------------------------*/
627 void
628 ctk_desktop_redraw(struct ctk_desktop *d)
629 {
630  if(PROCESS_CURRENT() == &ctk_process) {
631  if(mode == CTK_MODE_NORMAL || mode == CTK_MODE_WINDOWMOVE) {
632  do_redraw_all(CTK_CONF_MENUS, height);
633  }
634  } else {
635  height = ctk_draw_height();
636  width = ctk_draw_width();
637 
638  redraw |= REDRAW_ALL;
639  }
640 }
641 #endif /* CTK_CONF_WINDOWS */
642 /*---------------------------------------------------------------------------*/
643 /**
644  * Redraw a window.
645  *
646  * This function redraws the window, but only if it is the foremost
647  * one on the desktop.
648  *
649  * \param w The window to be redrawn.
650  */
651 /*---------------------------------------------------------------------------*/
652 void
654 {
655  /* Only redraw the window if it is a dialog or if it is the foremost
656  window. */
657  if(mode != CTK_MODE_NORMAL) {
658  return;
659  }
660 
661 #if CTK_CONF_WINDOWS
662  if(w == dialog) {
663  ctk_draw_dialog(w);
664  } else if(dialog == NULL &&
665 #if CTK_CONF_MENUS
666  menus.open == NULL &&
667 #endif /* CTK_CONF_MENUS */
668  windows == w)
669 #endif /* CTK_CONF_WINDOWS */
670  {
671  ctk_draw_window(w, CTK_FOCUS_WINDOW, 0, height, 0);
672  }
673 }
674 /*---------------------------------------------------------------------------*/
675 /**
676  * \internal Creates a new window.
677  *
678  * \param window The window to be created.
679  * \param w The width of the window.
680  * \param h The height of the window.
681  * \param title The title of the window.
682  */
683 /*---------------------------------------------------------------------------*/
684 static void
685 window_new(CC_REGISTER_ARG struct ctk_window *window,
686  unsigned char w, unsigned char h, char *title)
687 {
688 #if CTK_CONF_WINDOWS
689  if(w >= width - 2) {
690  window->x = 0;
691  } else {
692  window->x = (width - w - 2) / 2;
693  }
694  if(h >= height - 2 - ctk_draw_windowtitle_height) {
695  window->y = 0;
696  } else {
697  window->y = (height - h - 2 - ctk_draw_windowtitle_height) / 2;
698  }
699 #endif /* CTK_CONF_WINDOWS */
700 
701  window->w = w;
702  window->h = h;
703  window->title = title;
704  if(title != NULL) {
705  window->titlelen = (unsigned char)strlen(title);
706  } else {
707  window->titlelen = 0;
708  }
709  window->next = window->prev = NULL;
710  window->owner = PROCESS_CURRENT();
711  window->active = window->inactive = window->focused = NULL;
712 }
713 /*---------------------------------------------------------------------------*/
714 /**
715  * Create a new window.
716  *
717  * Creates a new window. The memory for the window structure must
718  * already be allocated by the caller, and is usually done with a
719  * static declaration.
720  *
721  * This function sets up the internal structure of the ctk_window
722  * struct and creates the move and close buttons, but it does not open
723  * the window. The window must be explicitly opened by calling the
724  * ctk_window_open() function.
725  *
726  * \param window The window to be created.
727  * \param w The width of the new window.
728  * \param h The height of the new window.
729  * \param title The title of the new window.
730  */
731 /*---------------------------------------------------------------------------*/
732 void
733 ctk_window_new(struct ctk_window *window,
734  unsigned char w, unsigned char h, char *title)
735 {
736  window_new(window, w, h, title);
737 
738 #if CTK_CONF_WINDOWS
739  make_windowbuttons(window);
740 #endif /* CTK_CONF_WINDOWS */
741 }
742 #if CTK_CONF_WINDOWS
743 /*---------------------------------------------------------------------------*/
744 /**
745  * Creates a new dialog.
746  *
747  * This function only sets up the internal structure of the ctk_window
748  * struct but does not open the dialog. The dialog must be explicitly
749  * opened by calling the ctk_dialog_open() function.
750  *
751  * \param dialog The dialog to be created.
752  * \param w The width of the dialog.
753  * \param h The height of the dialog.
754  */
755 /*---------------------------------------------------------------------------*/
756 void
757 ctk_dialog_new(CC_REGISTER_ARG struct ctk_window *dialog,
758  unsigned char w, unsigned char h)
759 {
760  window_new(dialog, w, h, NULL);
761 }
762 #endif /* CTK_CONF_WINDOWS */
763 /*---------------------------------------------------------------------------*/
764 /**
765  * Creates a new menu.
766  *
767  * This function sets up the internal structure of the menu, but does
768  * not add it to the menubar. Use the function ctk_menu_add() for that
769  * purpose.
770  *
771  * \param menu The menu to be created.
772  * \param title The title of the menu.
773  */
774 /*---------------------------------------------------------------------------*/
775 void
776 ctk_menu_new(CC_REGISTER_ARG struct ctk_menu *menu, char *title)
777 {
778 #if CTK_CONF_MENUS
779  menu->next = NULL;
780  menu->title = title;
781  menu->titlelen = (unsigned char)strlen(title);
782  menu->active = 0;
783  menu->nitems = 0;
784 #endif /* CTK_CONF_MENUS */
785 }
786 /*---------------------------------------------------------------------------*/
787 /**
788  * Adds a menu item to a menu.
789  *
790  * In CTK, each menu item is identified by a number which is unique
791  * within each menu. When a menu item is selected, a
792  * ctk_menuitem_activated signal is emitted and the menu item number
793  * is passed as signal data with the signal.
794  *
795  * \param menu The menu to which the menu item should be added.
796  * \param name The name of the menu item.
797  * \return The number of the menu item.
798  */
799 /*---------------------------------------------------------------------------*/
800 unsigned char
801 ctk_menuitem_add(CC_REGISTER_ARG struct ctk_menu *menu, char *name)
802 {
803 #if CTK_CONF_MENUS
804  if(menu->nitems == CTK_MAXMENUITEMS) {
805  return 0;
806  }
807  menu->items[menu->nitems].title = name;
808  menu->items[menu->nitems].titlelen = (unsigned char)strlen(name);
809  return menu->nitems++;
810 #else
811  return 0;
812 #endif /* CTK_CONF_MENUS */
813 }
814 /*---------------------------------------------------------------------------*/
815 /**
816  * \internal Adds a widget to the list of widgets that should be
817  * redrawn.
818  *
819  * \param w The widget that should be redrawn.
820  */
821 /*---------------------------------------------------------------------------*/
822 static void CC_FASTCALL
823 add_redrawwidget(struct ctk_widget *w)
824 {
825  static unsigned char i;
826 
827  if(redraw_widgetptr == MAX_REDRAWWIDGETS) {
828  redraw |= REDRAW_FOCUS;
829  } else {
830  redraw |= REDRAW_WIDGETS;
831  /* Check if it is in the queue already. If so, we don't add it
832  again. */
833  for(i = 0; i < redraw_widgetptr; ++i) {
834  if(redraw_widgets[i] == w) {
835  return;
836  }
837  }
838  redraw_widgets[redraw_widgetptr++] = w;
839  }
840 }
841 /*---------------------------------------------------------------------------*/
842 /**
843  * \internal Checks if a widget redrawn and adds it to the list of
844  * widgets to be redrawn.
845  *
846  * A widget can be redrawn only if the current CTK mode is
847  * CTK_MODE_NORMAL, if no menu is open, and the widget is in the
848  * foremost window.
849  *
850  * \param widget The widget that should be redrawn.
851  */
852 /*---------------------------------------------------------------------------*/
853 static void
854 widget_redraw(struct ctk_widget *widget)
855 {
856  struct ctk_window *window;
857 
858  if(mode != CTK_MODE_NORMAL || widget == NULL) {
859  return;
860  }
861 
862  /* Only redraw widgets that are in the foremost window. If we would
863  allow redrawing widgets in non-focused windows, we would have to
864  redraw all the windows that cover the non-focused window as well,
865  which would lead to flickering.
866 
867  Also, we avoid drawing any widgets when the menus are active.
868  */
869 
870 #if CTK_CONF_MENUS
871  if(menus.open == NULL)
872 #endif /* CTK_CONF_MENUS */
873  {
874  window = widget->window;
875 #if CTK_CONF_WINDOWS
876  if(window == dialog) {
877  ctk_draw_widget(widget, CTK_FOCUS_DIALOG, 0, height);
878  } else if(dialog == NULL &&
879  (window == windows ||
880  window == &desktop_window))
881 #endif /* CTK_CONF_WINDOWS */
882  {
883  ctk_draw_widget(widget, CTK_FOCUS_WINDOW, 0, height);
884  }
885  }
886 }
887 /*---------------------------------------------------------------------------*/
888 /**
889  * Redraws a widget.
890  *
891  * This function will set a flag which causes the widget to be redrawn
892  * next time the CTK process is scheduled.
893  *
894  * \param widget The widget that is to be redrawn.
895  *
896  * \note This function should usually not be called directly since it
897  * requires typecasting of the widget parameter. The wrapper macro
898  * CTK_WIDGET_REDRAW() does the required typecast and should be used
899  * instead.
900  */
901 /*---------------------------------------------------------------------------*/
902 void
904 {
905  if(mode != CTK_MODE_NORMAL || widget == NULL) {
906  return;
907  }
908 
909  /* Since this function isn't called by CTK itself, we only queue the
910  redraw request. */
911  add_redrawwidget(widget);
912 }
913 /*---------------------------------------------------------------------------*/
914 /**
915  * Adds a widget to a window.
916  *
917  * This function adds a widget to a window. The order of which the
918  * widgets are added is important, as it sets the order to which
919  * widgets are cycled with the widget selection keys.
920  *
921  * \param window The window to which the widhet should be added.
922  * \param widget The widget to be added.
923  */
924 /*---------------------------------------------------------------------------*/
925 void CC_FASTCALL
927  CC_REGISTER_ARG struct ctk_widget *widget)
928 {
929  if(widget->type == CTK_WIDGET_LABEL ||
930  widget->type == CTK_WIDGET_SEPARATOR) {
931  widget->next = window->inactive;
932  window->inactive = widget;
933  widget->window = window;
934  } else {
935  widget->next = window->active;
936  window->active = widget;
937  widget->window = window;
938  }
939 }
940 /*---------------------------------------------------------------------------*/
941 /**
942  * Gets the width of the desktop.
943  *
944  * \param d The desktop.
945  * \return The width of the desktop, in characters.
946  *
947  * \note The d parameter is currently unused and must be set to NULL.
948  */
949 /*---------------------------------------------------------------------------*/
950 unsigned char
951 ctk_desktop_width(struct ctk_desktop *d)
952 {
953  return ctk_draw_width();
954 }
955 /*---------------------------------------------------------------------------*/
956 /**
957  * Gets the height of the desktop.
958  *
959  * \param d The desktop.
960  * \return The height of the desktop, in characters.
961  *
962  * \note The d parameter is currently unused and must be set to NULL.
963  */
964 /*---------------------------------------------------------------------------*/
965 unsigned char
966 ctk_desktop_height(struct ctk_desktop *d)
967 {
968  return ctk_draw_height();
969 }
970 /*---------------------------------------------------------------------------*/
971 /**
972  * \internal Selects a widget in the window of the widget.
973  *
974  * \param focus The widget to be focused.
975  */
976 /*---------------------------------------------------------------------------*/
977 static void CC_FASTCALL
978 select_widget(struct ctk_widget *focus)
979 {
980  struct ctk_window *window;
981 
982  window = focus->window;
983 
984  if(focus != window->focused) {
985  window->focused = focus;
986  /* The operation changed the focus, so we emit a "hover" signal
987  for those widgets that support it. */
988 
989  if(window->focused->type == CTK_WIDGET_HYPERLINK) {
991  } else if(window->focused->type == CTK_WIDGET_BUTTON) {
993  }
994 
995  add_redrawwidget(window->focused);
996 
998  }
999 }
1000 /*---------------------------------------------------------------------------*/
1001 #define UP 0
1002 #define DOWN 1
1003 #define LEFT 2
1004 #define RIGHT 3
1005 static void CC_FASTCALL
1006 switch_focus_widget(unsigned char direction)
1007 {
1008 #if CTK_CONF_WINDOWS
1009  register struct ctk_window *window;
1010 #endif /* CTK_CONF_WINDOWS */
1011  register struct ctk_widget *focus;
1012  struct ctk_widget *widget;
1013 
1014 #if CTK_CONF_WINDOWS
1015  if(dialog != NULL) {
1016  window = dialog;
1017  } else {
1018  window = windows;
1019  }
1020 
1021  /* If there are no windows open, we move focus around between the
1022  icons on the root window instead. */
1023  if(window == NULL) {
1024  window = &desktop_window;
1025  }
1026 #else /* CTK_CONF_WINDOWS */
1027  if(window == NULL) {
1028  return;
1029  }
1030 #endif /* CTK_CONF_WINDOWS */
1031 
1032  focus = window->focused;
1033  if(focus == NULL) {
1034  focus = window->active;
1035  if(focus == NULL) {
1036  return;
1037  }
1038  }
1039  add_redrawwidget(focus);
1040 
1041  if((direction & 1) == 0) {
1042  /* Move focus "up" */
1043  focus = focus->next;
1044  } else {
1045  /* Move focus "down" */
1046  for(widget = window->active;
1047  widget != NULL; widget = widget->next) {
1048  if(widget->next == focus) {
1049  break;
1050  }
1051  }
1052  focus = widget;
1053  if(focus == NULL) {
1054  if(window->active != NULL) {
1055  for(focus = window->active;
1056  focus->next != NULL; focus = focus->next);
1057  }
1058  }
1059  }
1060  if(focus == NULL) {
1061  focus = window->active;
1062  }
1063 
1064  select_widget(focus);
1065 }
1066 /*---------------------------------------------------------------------------*/
1067 #if CTK_CONF_MENUS
1068 static void
1069 switch_open_menu(unsigned char rightleft)
1070 {
1071  struct ctk_menu *menu;
1072 
1073  if(rightleft == 0) {
1074  /* Move right */
1075  for(menu = menus.menus; menu != NULL; menu = menu->next) {
1076  if(menu->next == menus.open) {
1077  break;
1078  }
1079  }
1080  lastmenu = menus.open;
1081  menus.open = menu;
1082  if(menus.open == NULL) {
1083  for(menu = menus.menus;
1084  menu->next != NULL; menu = menu->next);
1085  menus.open = menu;
1086  }
1087  } else {
1088  /* Move to left */
1089  lastmenu = menus.open;
1090  menus.open = menus.open->next;
1091  if(menus.open == NULL) {
1092  menus.open = menus.menus;
1093  }
1094  }
1095 
1096  menus.open->active = 0;
1097 }
1098 /*---------------------------------------------------------------------------*/
1099 static void
1100 switch_menu_item(unsigned char updown)
1101 {
1102  register struct ctk_menu *m;
1103 
1104  m = menus.open;
1105 
1106  if(updown == 0) {
1107  /* Move up */
1108  if(m->active == 0) {
1109  m->active = m->nitems - 1;
1110  } else {
1111  --m->active;
1112  if(m->items[m->active].title[0] == '-') {
1113  --m->active;
1114  }
1115  }
1116  } else {
1117  /* Move down */
1118  if(m->active >= m->nitems - 1) {
1119  m->active = 0;
1120  } else {
1121  ++m->active;
1122  if(m->items[m->active].title[0] == '-') {
1123  ++m->active;
1124  }
1125  }
1126  }
1127 }
1128 #endif /* CTK_CONF_MENUS */
1129 /*---------------------------------------------------------------------------*/
1130 static unsigned char CC_FASTCALL
1131 activate(CC_REGISTER_ARG struct ctk_widget *w)
1132 {
1133  if(w->type == CTK_WIDGET_BUTTON) {
1134 #if CTK_CONF_WINDOWCLOSE
1135  if(w == (struct ctk_widget *)&windows->closebutton) {
1136  process_post(w->window->owner, ctk_signal_window_close, windows);
1137  ctk_window_close(windows);
1138  return REDRAW_ALL;
1139  } else
1140 #endif /* CTK_CONF_WINDOWCLOSE */
1141 #if CTK_CONF_WINDOWMOVE
1142  if(w == (struct ctk_widget *)&windows->titlebutton) {
1143  mode = CTK_MODE_WINDOWMOVE;
1144  return REDRAW_ALL;
1145  } else
1146 #endif /* CTK_CONF_WINDOWMOVE */
1147  {
1148  process_post(w->window->owner, ctk_signal_widget_activate, w);
1149  }
1150 #if CTK_CONF_ICONS
1151  } else if(w->type == CTK_WIDGET_ICON) {
1152  if(w->widget.icon.owner != PROCESS_NONE) {
1153  process_post(w->widget.icon.owner, ctk_signal_widget_activate, w);
1154  } else {
1155  process_post(w->window->owner, ctk_signal_widget_activate, w);
1156  }
1157 #endif /* CTK_CONF_ICONS */
1158  } else if(w->type == CTK_WIDGET_HYPERLINK) {
1159  process_post(PROCESS_BROADCAST, ctk_signal_hyperlink_activate, w);
1160  } else if(w->type == CTK_WIDGET_TEXTENTRY) {
1161  if(w->widget.textentry.state == CTK_TEXTENTRY_NORMAL) {
1162  w->widget.textentry.state = CTK_TEXTENTRY_EDIT;
1163  textentry_input(0, (struct ctk_textentry *)w);
1164  } else {
1165  w->widget.textentry.state = CTK_TEXTENTRY_NORMAL;
1166  process_post(w->window->owner, ctk_signal_widget_activate, w);
1167  }
1168  add_redrawwidget(w);
1169  return REDRAW_WIDGETS;
1170  } else {
1171  process_post(w->window->owner, ctk_signal_widget_activate, w);
1172  }
1173  return REDRAW_NONE;
1174 }
1175 /*---------------------------------------------------------------------------*/
1176 #ifdef SDCC
1177 /* Dummy function that we define to keep sdcc happy - with sdcc,
1178  function pointers cannot be NULL. ctk_textentry_input is typedef'd
1179  in ctk/ctk.h, hence the strange-looking function signature. */
1180 unsigned char
1181 ctk_textentry_input_null(ctk_arch_key_t c, struct ctk_textentry *t)
1182 {
1183  return 0;
1184 }
1185 #endif /* SDCC */
1186 /*---------------------------------------------------------------------------*/
1187 static void CC_FASTCALL
1188 textentry_input(ctk_arch_key_t c, CC_REGISTER_ARG struct ctk_textentry *t)
1189 {
1190  register char *cptr, *cptr2;
1191  static unsigned char len, txpos, typos, tlen;
1192 
1193  if(t->input != NULL && t->input(c, t)) {
1194  return;
1195  }
1196 
1197  txpos = t->xpos;
1198  typos = t->ypos;
1199  tlen = t->len;
1200 
1201  cptr = &t->text[txpos + typos * (tlen + 1)];
1202 
1203  switch(c) {
1204  case CH_CURS_LEFT:
1205  if(txpos > 0) {
1206  --txpos;
1207  }
1208  break;
1209 
1210  case CH_CURS_RIGHT:
1211  if(txpos < tlen - 1 && *cptr != 0) {
1212  ++txpos;
1213  }
1214  break;
1215 
1216  case CH_CURS_UP:
1217  txpos = 0;
1218  break;
1219 
1220  case 0:
1221  case CH_CURS_DOWN:
1222  txpos = (unsigned char)strlen(t->text);
1223  if(txpos == tlen) {
1224  --txpos;
1225  }
1226  break;
1227 
1228  case CH_ENTER:
1229  activate((struct ctk_widget *)t);
1230  switch_focus_widget(DOWN);
1231  break;
1232 
1233  case CTK_CONF_WIDGETDOWN_KEY:
1234  t->state = CTK_TEXTENTRY_NORMAL;
1235  switch_focus_widget(DOWN);
1236  break;
1237  case CTK_CONF_WIDGETUP_KEY:
1238  t->state = CTK_TEXTENTRY_NORMAL;
1239  switch_focus_widget(UP);
1240  break;
1241 
1242  default:
1243  len = tlen - txpos;
1244  if(c == CH_DEL) {
1245  if(len == 1 && *cptr != 0) {
1246  *cptr = 0;
1247  } else {
1248  if(txpos > 0) {
1249  --txpos;
1250  strcpy(cptr - 1, cptr);
1251  }
1252  }
1253  } else {
1254  if(ctk_arch_isprint(c)) {
1255  if(len > 1) {
1256  cptr2 = cptr + len - 1;
1257  while(cptr2 > cptr) {
1258  *cptr2 = *(cptr2 - 1);
1259  --cptr2;
1260  }
1261  ++txpos;
1262  }
1263  *cptr = c;
1264  }
1265  }
1266  break;
1267  }
1268 
1269  t->xpos = txpos;
1270  t->ypos = typos;
1271 }
1272 /*---------------------------------------------------------------------------*/
1273 #if CTK_CONF_MENUS
1274 static unsigned char
1275 activate_menu(void)
1276 {
1277  struct ctk_window *w;
1278 
1279  lastmenu = menus.open;
1280  if(menus.open == &desktopmenu) {
1281  for(w = windows; w != NULL; w = w->next) {
1282  if(w->title == desktopmenu.items[desktopmenu.active].title) {
1283  ctk_window_open(w);
1284  menus.open = NULL;
1285  return REDRAW_ALL;
1286  }
1287  }
1288  } else {
1289  process_post(PROCESS_BROADCAST, ctk_signal_menu_activate, menus.open);
1290  }
1291  menus.open = NULL;
1292  return REDRAW_MENUPART;
1293 }
1294 /*---------------------------------------------------------------------------*/
1295 static unsigned char
1296 menus_input(ctk_arch_key_t c)
1297 {
1298  if(menus.open->nitems > maxnitems) {
1299  maxnitems = menus.open->nitems;
1300  }
1301 
1302  switch(c) {
1303  case CH_CURS_RIGHT:
1304  switch_open_menu(1);
1305  return REDRAW_MENUPART;
1306 
1307  case CH_CURS_DOWN:
1308  switch_menu_item(1);
1309  return REDRAW_MENUS;
1310 
1311  case CH_CURS_LEFT:
1312  switch_open_menu(0);
1313  return REDRAW_MENUPART;
1314 
1315  case CH_CURS_UP:
1316  switch_menu_item(0);
1317  return REDRAW_MENUS;
1318 
1319  case CH_ENTER:
1320  return activate_menu();
1321 
1322  case CTK_CONF_MENU_KEY:
1323  lastmenu = menus.open;
1324  menus.open = NULL;
1325  return REDRAW_MENUPART;
1326  }
1327 
1328  return REDRAW_NONE;
1329 }
1330 #endif /* CTK_CONF_MENUS */
1331 /*---------------------------------------------------------------------------*/
1332 #if CTK_CONF_SCREENSAVER
1333 static void
1334 handle_timer(void)
1335 {
1336  if(mode == CTK_MODE_NORMAL) {
1337  ++screensaver_timer;
1338  if(screensaver_timer >= ctk_screensaver_timeout) {
1339  process_post(PROCESS_BROADCAST, ctk_signal_screensaver_start, NULL);
1340 #ifdef CTK_SCREENSAVER_INIT
1341  CTK_SCREENSAVER_INIT();
1342 #endif /* CTK_SCREENSAVER_INIT */
1343 
1344  screensaver_timer = 0;
1345  }
1346  }
1347 }
1348 #endif /* CTK_CONF_SCREENSAVER */
1349 /*---------------------------------------------------------------------------*/
1350 static void
1351 unfocus_widget(CC_REGISTER_ARG struct ctk_widget *w)
1352 {
1353  if(w != NULL) {
1354  redraw |= REDRAW_WIDGETS;
1355  add_redrawwidget(w);
1357  ((struct ctk_textentry *)w)->state =
1358  CTK_TEXTENTRY_NORMAL;
1359  }
1360  w->window->focused = NULL;
1361  }
1362 }
1363 /*---------------------------------------------------------------------------*/
1364 PROCESS_THREAD(ctk_process, ev, data)
1365 {
1366  static ctk_arch_key_t c;
1367  static unsigned char i;
1368 #if CTK_CONF_WINDOWS
1369  register struct ctk_window *window;
1370 #endif /* CTK_CONF_WINDOWS */
1371  register struct ctk_widget *widget;
1372  register struct ctk_widget **widgetptr;
1373 #if CTK_CONF_MOUSE_SUPPORT
1374  static unsigned char mxc, myc, mouse_button_changed, mouse_moved,
1375  mouse_clicked;
1376 #if CTK_CONF_MENUS
1377  static unsigned char menux;
1378  register struct ctk_menu *menu;
1379 #endif /* CTK_CONF_MENUS */
1380 #endif /* CTK_CONF_MOUSE_SUPPORT */
1381 
1382  PROCESS_BEGIN();
1383 
1384 #if CTK_CONF_MENUS
1385  ctk_menu_new(&desktopmenu, "Desktop");
1386  make_desktopmenu();
1387  menus.menus = menus.desktopmenu = &desktopmenu;
1388 #endif /* CTK_CONF_MENUS */
1389 
1390 #if CTK_CONF_MOUSE_SUPPORT
1391  ctk_mouse_init();
1392  ctk_mouse_show();
1393 #endif /* CTK_CONF_MOUSE_SUPPORT */
1394 
1395  ctk_restore();
1396 
1397 #if CTK_CONF_WINDOWS
1398  desktop_window.owner = &ctk_process;
1399 #endif /* CTK_CONF_WINDOWS */
1400 
1402 
1405 
1409 
1411 
1413 
1415 
1416 #if CTK_CONF_MOUSE_SUPPORT
1417  ctk_signal_pointer_move = process_alloc_event();
1418  ctk_signal_pointer_button = process_alloc_event();
1419 #endif /* CTK_CONF_MOUSE_SUPPORT */
1420 
1421 #if CTK_CONF_SCREENSAVER
1422  ctk_signal_screensaver_start = process_alloc_event();
1423  ctk_signal_screensaver_stop = process_alloc_event();
1424 #endif /* CTK_CONF_SCREENSAVER */
1425 
1426  mode = CTK_MODE_NORMAL;
1427 
1428 #if CTK_CONF_ICONS
1429  iconx = ICONX_START;
1430  icony = ICONY_START;
1431 #endif /* CTK_CONF_ICONS */
1432 
1433 #if CTK_CONF_SCREENSAVER
1435 #endif /* CTK_CONF_SCREENSAVER */
1436 
1437  while(1) {
1438  process_poll(&ctk_process);
1440 
1441 #if CTK_CONF_SCREENSAVER
1442  if(timer_expired(&timer)) {
1443  timer_reset(&timer);
1444  handle_timer();
1445  }
1446 #endif /* CTK_CONF_SCREENSAVER */
1447 
1448 #if CTK_CONF_MENUS
1449  if(menus.open != NULL) {
1450  maxnitems = menus.open->nitems;
1451  } else {
1452  maxnitems = 0;
1453  }
1454 #endif /* CTK_CONF_MENUS */
1455 
1456 #if CTK_CONF_MOUSE_SUPPORT
1457  mouse_button_changed = mouse_moved = mouse_clicked = 0;
1458 
1459  /* See if there is any change in the buttons. */
1460  if(ctk_mouse_button() != mouse_button) {
1461  mouse_button = ctk_mouse_button();
1462  mouse_button_changed = 1;
1463  if(mouse_button == 0) {
1464  mouse_clicked = 1;
1465  }
1466  }
1467 
1468  /* Check if the mouse pointer has moved. */
1469  if(ctk_mouse_x() != mouse_x ||
1470  ctk_mouse_y() != mouse_y) {
1471  mouse_x = ctk_mouse_x();
1472  mouse_y = ctk_mouse_y();
1473  mouse_moved = 1;
1474  }
1475 
1476  mxc = ctk_mouse_xtoc(mouse_x);
1477  myc = ctk_mouse_ytoc(mouse_y);
1478 #endif /* CTK_CONF_MOUSE_SUPPORT */
1479 
1480 #if CTK_CONF_SCREENSAVER
1481  if(mode == CTK_MODE_SCREENSAVER) {
1482  if(ctk_arch_keyavail()
1483 #if CTK_CONF_MOUSE_SUPPORT
1484  || mouse_moved || mouse_button_changed
1485 #endif /* CTK_CONF_MOUSE_SUPPORT */
1486  ) {
1487  process_post(PROCESS_BROADCAST, ctk_signal_screensaver_stop, NULL);
1488  mode = CTK_MODE_NORMAL;
1489  }
1490  } else
1491 #endif /* CTK_CONF_SCREENSAVER */
1492  if(mode == CTK_MODE_NORMAL) {
1493 #if CTK_CONF_MOUSE_SUPPORT
1494  /* If there is any change in the mouse conditions, find out in
1495  which window the mouse pointer currently is in order to send
1496  the correct signals, or bring a window to focus. */
1497  if(mouse_moved || mouse_button_changed) {
1498  ctk_mouse_show();
1499 #if CTK_CONF_SCREENSAVER
1500  screensaver_timer = 0;
1501 #endif /* CTK_CONF_SCREENSAVER */
1502 
1503 #if CTK_CONF_MENUS
1504  if(myc == 0) {
1505  /* Here we should do whatever needs to be done when the mouse
1506  moves around and clicks in the menubar. */
1507  if(mouse_clicked) {
1508  static unsigned char titlelen;
1509 
1510  /* Find out which menu that the mouse pointer is in. Start
1511  with the ->next menu after the desktop menu. We assume
1512  that the menus start one character from the left screen
1513  side and that the desktop menu is farthest to the
1514  right. */
1515  menux = 1;
1516  for(menu = menus.menus->next;
1517  menu != NULL; menu = menu->next) {
1518  titlelen = menu->titlelen;
1519  if(mxc >= menux && mxc <= menux + titlelen) {
1520  break;
1521  }
1522  menux += titlelen;
1523  }
1524 
1525  /* Also check desktop menu. */
1526  if(mxc >= width - 7 &&
1527  mxc <= width - 1) {
1528  menu = &desktopmenu;
1529  }
1530 
1531  menus.open = menu;
1532  redraw |= REDRAW_MENUPART;
1533  }
1534  } else {
1535  --myc;
1536 
1537  if(menus.open != NULL) {
1538  static unsigned char nitems;
1539 
1540  /* Do whatever needs to be done when a menu is open. */
1541 
1542  /* First check if the mouse pointer is in the currently open
1543  menu. */
1544  if(menus.open == &desktopmenu) {
1545  menux = width - CTK_CONF_MENUWIDTH;
1546  } else {
1547  menux = 1;
1548  for(menu = menus.menus->next; menu != menus.open;
1549  menu = menu->next) {
1550  menux += menu->titlelen;
1551  }
1552  }
1553 
1554  nitems = menus.open->nitems;
1555  /* Find out which of the menu items the mouse is pointing
1556  to. */
1557  if(mxc >= menux && mxc <= menux + CTK_CONF_MENUWIDTH) {
1558  if(myc <= nitems) {
1559  menus.open->active = myc;
1560  } else {
1561  menus.open->active = nitems - 1;
1562  }
1563  }
1564 
1565  if(mouse_clicked) {
1566  if(mxc >= menux && mxc <= menux + CTK_CONF_MENUWIDTH &&
1567  myc <= nitems) {
1568  redraw |= activate_menu();
1569  } else {
1570  lastmenu = menus.open;
1571  menus.open = NULL;
1572  redraw |= REDRAW_MENUPART;
1573  }
1574  } else {
1575  redraw |= REDRAW_MENUS;
1576  }
1577  } else {
1578 #endif /* CTK_CONF_MENUS */
1579 
1580 #if CTK_CONF_WINDOWS
1581  /* Walk through the windows from top to bottom to see in
1582  which window the mouse pointer is. */
1583  if(dialog != NULL) {
1584  window = dialog;
1585  } else {
1586  for(window = windows; window != NULL;
1587  window = window->next) {
1588 
1589  /* Check if the mouse is within the window. */
1590  if(mxc >= window->x &&
1591  mxc <= window->x + window->w +
1592  2 * ctk_draw_windowborder_width &&
1593  myc >= window->y &&
1594  myc <= window->y + window->h +
1595  ctk_draw_windowtitle_height +
1596  ctk_draw_windowborder_height) {
1597  break;
1598  }
1599  }
1600  }
1601 
1602  /* If we didn't find any window, and there are no windows
1603  open, the mouse pointer will definately be within the
1604  background desktop window. */
1605  if(window == NULL) {
1606  window = &desktop_window;
1607  }
1608 
1609  /* If the mouse pointer moves around outside of the
1610  currently focused window (or dialog), we should not have
1611  any focused widgets in the focused window so we make sure
1612  that there are none. */
1613  if(windows != NULL &&
1614  window != windows &&
1615  windows->focused != NULL){
1616  unfocus_widget(windows->focused);
1617  }
1618 #endif /* CTK_CONF_WINDOWS */
1619 
1620  if(window != NULL) {
1621 #if CTK_CONF_WINDOWS
1622  /* If the mouse was clicked outside of the current window,
1623  we bring the clicked window to front. */
1624  if(dialog == NULL &&
1625  window != &desktop_window &&
1626  window != windows &&
1627  mouse_clicked) {
1628  /* Bring window to front. */
1629  ctk_window_open(window);
1630  redraw |= REDRAW_ALL;
1631  } else {
1632 
1633  /* Find out which widget currently is under the mouse
1634  pointer and give it focus, unless it already has
1635  focus. */
1636  mxc = mxc - window->x - ctk_draw_windowborder_width;
1637  myc = myc - window->y - ctk_draw_windowtitle_height;
1638 #endif /* CTK_CONF_WINDOWS */
1639 
1640  /* See if the mouse pointer is on a widget. If so, it
1641  should be selected and, if the button is clicked,
1642  activated. */
1643  for(widget = window->active; widget != NULL;
1644  widget = widget->next) {
1645 
1646  if(mxc >= widget->x &&
1647  mxc <= widget->x + widget->w + 1 &&
1648  myc >= widget->y &&
1649  myc <= widget->y + widget->h - 1) {
1650  break;
1651  }
1652  }
1653 
1654  /* if the mouse is moved in the focused window, we emit
1655  a ctk_signal_pointer_move signal to the owner of the
1656  window. */
1657  if(mouse_moved
1658 #if CTK_CONF_WINDOWS
1659  && (window != &desktop_window || windows == NULL)
1660 #endif /* CTK_CONF_WINDOWS */
1661  ) {
1662 
1663  process_post(window->owner, ctk_signal_pointer_move, NULL);
1664 
1665  /* If there was a focused widget that is not below the
1666  mouse pointer, we remove focus from the widget and
1667  redraw it. */
1668  if(window->focused != NULL &&
1669  widget != window->focused) {
1670  unfocus_widget(window->focused);
1671  }
1672  redraw |= REDRAW_WIDGETS;
1673  if(widget != NULL) {
1674  select_widget(widget);
1675  }
1676  }
1677 
1678  if(mouse_button_changed) {
1679  process_post(window->owner, ctk_signal_pointer_button,
1680  (process_data_t)(size_t)mouse_button);
1681  if(mouse_clicked && widget != NULL) {
1682  select_widget(widget);
1683  redraw |= activate(widget);
1684  }
1685  }
1686 #if CTK_CONF_WINDOWS
1687  }
1688 #endif /* CTK_CONF_WINDOWS */
1689  }
1690 #if CTK_CONF_MENUS
1691  }
1692  }
1693 #endif /* CTK_CONF_MENUS */
1694  }
1695 #endif /* CTK_CONF_MOUSE_SUPPORT */
1696 
1697  while(ctk_arch_keyavail()) {
1698 
1699  ctk_mouse_hide();
1700 
1701 #if CTK_CONF_SCREENSAVER
1702  screensaver_timer = 0;
1703 #endif /* CTK_CONF_SCREENSAVER */
1704 
1705  c = ctk_arch_getkey();
1706 
1707 #if CTK_CONF_WINDOWS
1708  if(dialog != NULL) {
1709  window = dialog;
1710  } else if(windows != NULL) {
1711  window = windows;
1712  } else {
1713  window = &desktop_window;
1714  }
1715 #else /* CTK_CONF_WINDOWS */
1716  if(window == NULL) {
1717  continue;
1718  }
1719 #endif /* CTK_CONF_WINDOWS */
1720 
1721  /* Allow to exit the process owning the foreground window by
1722  pressing ctrl-c. This is especially useful if there's no
1723  closebutton on the window frames (or no windows at all).
1724  */
1725  if(c == 3) {
1726  process_post(window->owner, PROCESS_EVENT_EXIT, NULL);
1727  }
1728 
1729  widget = window->focused;
1730 
1731  if(widget != NULL &&
1732  widget->type == CTK_WIDGET_TEXTENTRY &&
1733  widget->widget.textentry.state == CTK_TEXTENTRY_EDIT) {
1734  textentry_input(c, (struct ctk_textentry *)widget);
1735  add_redrawwidget(widget);
1736 #if CTK_CONF_MENUS
1737  } else if(menus.open != NULL) {
1738  redraw |= menus_input(c);
1739 #endif /* CTK_CONF_MENUS */
1740  } else {
1741  switch(c) {
1742  case CTK_CONF_WIDGETDOWN_KEY:
1743  switch_focus_widget(DOWN);
1744  break;
1745  case CTK_CONF_WIDGETUP_KEY:
1746  switch_focus_widget(UP);
1747  break;
1748 #if CTK_CONF_MENUS
1749  case CTK_CONF_MENU_KEY:
1750  if(dialog == NULL) {
1751  if(lastmenu == NULL) {
1752  menus.open = menus.menus;
1753  } else {
1754  menus.open = lastmenu;
1755  }
1756  menus.open->active = 0;
1757  redraw |= REDRAW_MENUS;
1758  }
1759  break;
1760 #endif /* CTK_CONF_MENUS */
1761 #if CTK_CONF_WINDOWS
1762  case CTK_CONF_WINDOWSWITCH_KEY:
1763  if(windows != NULL) {
1764  for(window = windows; window->next != NULL;
1765  window = window->next);
1766  ctk_window_open(window);
1767  }
1768  break;
1769 #endif /* CTK_CONF_WINDOWS */
1770  default:
1771 
1772  if(c == CH_ENTER &&
1773  widget != NULL) {
1774  redraw |= activate(widget);
1775  } else {
1776  if(widget != NULL &&
1777  widget->type == CTK_WIDGET_TEXTENTRY) {
1778  if(widget->widget.textentry.state == CTK_TEXTENTRY_NORMAL) {
1779  widget->widget.textentry.state = CTK_TEXTENTRY_EDIT;
1780  textentry_input(0, (struct ctk_textentry *)widget);
1781  }
1782  textentry_input(c, (struct ctk_textentry *)widget);
1783  add_redrawwidget(widget);
1784  } else {
1785  unfocus_widget(window->focused);
1787  (process_data_t)(size_t)c);
1788  }
1789  }
1790  break;
1791  }
1792  }
1793 
1794 #if 0
1795  if(redraw & REDRAW_WIDGETS) {
1796  widgetptr = redraw_widgets;
1797  for(i = 0; i < MAX_REDRAWWIDGETS; ++i) {
1798  widget_redraw(*widgetptr);
1799  *widgetptr = NULL;
1800  ++widgetptr;
1801  }
1802  redraw &= ~REDRAW_WIDGETS;
1803  redraw_widgetptr = 0;
1804  }
1805 #endif /* 0 */
1806  }
1807 #if CTK_CONF_WINDOWMOVE
1808  } else if(mode == CTK_MODE_WINDOWMOVE) {
1809 
1810  redraw = 0;
1811 
1812  window = windows;
1813 
1814 #if CTK_CONF_MOUSE_SUPPORT
1815 
1816  /* If the mouse has moved, we move the window as well. */
1817  if(mouse_moved) {
1818 
1819  if(window->w + mxc + 2 >= width) {
1820  window->x = width - 2 - window->w;
1821  } else {
1822  window->x = mxc;
1823  }
1824 
1825  if(window->h + myc + ctk_draw_windowtitle_height +
1826  ctk_draw_windowborder_height >= height) {
1827  window->y = height - window->h -
1828  ctk_draw_windowtitle_height - ctk_draw_windowborder_height;
1829  } else {
1830  window->y = myc;
1831  }
1832 #if CTK_CONF_MENUS
1833  if(window->y > 0) {
1834  --window->y;
1835  }
1836 #endif /* CTK_CONF_MENUS */
1837 
1838  redraw = REDRAW_ALL;
1839  }
1840 
1841  /* Check if the mouse has been clicked, and stop moving the window
1842  if so. */
1843  if(mouse_button_changed &&
1844  mouse_button == 0) {
1845  mode = CTK_MODE_NORMAL;
1846  redraw = REDRAW_ALL;
1847  }
1848 #endif /* CTK_CONF_MOUSE_SUPPORT */
1849 
1850  while(mode == CTK_MODE_WINDOWMOVE && ctk_arch_keyavail()) {
1851 
1852 #if CTK_CONF_SCREENSAVER
1853  screensaver_timer = 0;
1854 #endif /* CTK_CONF_SCREENSAVER */
1855 
1856  c = ctk_arch_getkey();
1857 
1858  switch(c) {
1859  case CH_CURS_RIGHT:
1860  ++window->x;
1861  if(window->x + window->w + 1 >= width) {
1862  --window->x;
1863  }
1864  redraw = REDRAW_ALL;
1865  break;
1866  case CH_CURS_LEFT:
1867  if(window->x > 0) {
1868  --window->x;
1869  }
1870  redraw = REDRAW_ALL;
1871  break;
1872  case CH_CURS_DOWN:
1873  ++window->y;
1874  if(window->y + window->h + 1 + CTK_CONF_MENUS >= height) {
1875  --window->y;
1876  }
1877  redraw = REDRAW_ALL;
1878  break;
1879  case CH_CURS_UP:
1880  if(window->y > 0) {
1881  --window->y;
1882  }
1883  redraw = REDRAW_ALL;
1884  break;
1885  default:
1886  mode = CTK_MODE_NORMAL;
1887  redraw = REDRAW_ALL;
1888  break;
1889  }
1890  }
1891 #endif /* CTK_CONF_WINDOWMOVE */
1892  }
1893 
1894  if(redraw & REDRAW_ALL) {
1895  do_redraw_all(CTK_CONF_MENUS, height);
1896 #if CTK_CONF_MENUS
1897  } else if(redraw & REDRAW_MENUPART) {
1898  do_redraw_all(CTK_CONF_MENUS, maxnitems + 1);
1899  } else if(redraw & REDRAW_MENUS) {
1900  ctk_draw_menus(&menus);
1901 #endif /* CTK_CONF_MENUS */
1902  } else if(redraw & REDRAW_FOCUS) {
1903 #if CTK_CONF_WINDOWS
1904  if(dialog != NULL) {
1905  ctk_window_redraw(dialog);
1906  } else if(windows != NULL) {
1907  ctk_window_redraw(windows);
1908  } else {
1909  ctk_window_redraw(&desktop_window);
1910  }
1911 #else /* CTK_CONF_WINDOWS */
1912  if(window != NULL) {
1913  ctk_window_redraw(window);
1914  }
1915 #endif /* CTK_CONF_WINDOWS */
1916  } else if(redraw & REDRAW_WIDGETS) {
1917  widgetptr = redraw_widgets;
1918  for(i = 0; i < MAX_REDRAWWIDGETS; ++i) {
1919  widget_redraw(*widgetptr);
1920  *widgetptr = NULL;
1921  ++widgetptr;
1922  }
1923  }
1924  redraw = 0;
1925  redraw_widgetptr = 0;
1926  }
1927 
1928  PROCESS_END();
1929 }
1930 /*---------------------------------------------------------------------------*/
1931 /** @} */
1932 /** @} */