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