blob: 2dc40b7f485f0b1dfba6bcf3e96516c76502e964 [file] [log] [blame]
adamdunkelsca9ddcb2003-03-19 14:13:31 +00001/*
2 * Copyright (c) 2002, 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. All advertising materials mentioning features or use of this
15 * software must display the following acknowledgement:
16 * This product includes software developed by Adam Dunkels.
17 * 4. The name of the author may not be used to endorse or promote
18 * products derived from this software without specific prior
19 * written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
22 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 * This file is part of the "ctk" console GUI toolkit for cc65
34 *
adamdunkels4b7a7ab2003-04-09 19:22:19 +000035 * $Id: ctk.c,v 1.11 2003/04/09 19:22:19 adamdunkels Exp $
adamdunkelsca9ddcb2003-03-19 14:13:31 +000036 *
37 */
38
adamdunkelsd07a5422003-04-05 12:22:35 +000039#include "cc.h"
adamdunkelsca9ddcb2003-03-19 14:13:31 +000040#include "ek.h"
41#include "dispatcher.h"
42#include "ctk.h"
43#include "ctk-draw.h"
44#include "ctk-conf.h"
adamdunkelsc4902862003-04-09 00:30:45 +000045#include "ctk-mouse.h"
adamdunkelsca9ddcb2003-03-19 14:13:31 +000046
47static unsigned char height, width;
48
49static unsigned char mode;
50
51static struct ctk_window desktop_window;
52static struct ctk_window *windows;
53static struct ctk_window *dialog;
54
55#if CTK_CONF_MENUS
56static struct ctk_menus menus;
57static struct ctk_menu *lastmenu;
58static struct ctk_menu desktopmenu;
59#endif /* CTK_CONF_MENUS */
60
61#ifndef NULL
62#define NULL (void *)0
63#endif /* NULL */
64
65
66#define REDRAW_NONE 0
67#define REDRAW_ALL 1
68#define REDRAW_FOCUS 2
69#define REDRAW_WIDGETS 4
70#define REDRAW_MENUS 8
71#define REDRAW_MENUPART 16
72
73#define MAX_REDRAWWIDGETS 4
74static unsigned char redraw;
75static struct ctk_widget *redraw_widgets[MAX_REDRAWWIDGETS];
76static unsigned char redraw_widgetptr;
77static unsigned char maxnitems;
78
79static unsigned char iconx, icony;
80#define ICONX_START (width - 5)
81#define ICONY_START 0
82#define ICONX_DELTA -8
83#define ICONY_DELTA 5
84#define ICONY_MAX (height - 4)
85
adamdunkelsc4902862003-04-09 00:30:45 +000086static void ctk_idle(void);
adamdunkels78c03dc2003-04-09 13:45:05 +000087static DISPATCHER_SIGHANDLER(ctk_sighandler, s, data);
adamdunkelsca9ddcb2003-03-19 14:13:31 +000088static struct dispatcher_proc p =
adamdunkelsc4902862003-04-09 00:30:45 +000089 {DISPATCHER_PROC("CTK Contiki GUI", ctk_idle, ctk_sighandler, NULL)};
adamdunkelsca9ddcb2003-03-19 14:13:31 +000090static ek_id_t ctkid;
91
adamdunkelsca9ddcb2003-03-19 14:13:31 +000092ek_signal_t ctk_signal_keypress,
93 ctk_signal_timer,
94 ctk_signal_button_activate,
95 ctk_signal_button_hover,
96 ctk_signal_hyperlink_activate,
97 ctk_signal_hyperlink_hover,
98 ctk_signal_menu_activate,
adamdunkelsc4902862003-04-09 00:30:45 +000099 ctk_signal_window_close,
100 ctk_signal_pointer_move,
101 ctk_signal_pointer_down,
102 ctk_signal_pointer_up;
103
adamdunkels19a787c2003-04-09 09:22:24 +0000104#if CTK_CONF_MOUSE_SUPPORT
adamdunkelse0683312003-04-09 09:02:52 +0000105unsigned short mouse_last_x, mouse_last_y, mouse_last_button;
adamdunkels19a787c2003-04-09 09:22:24 +0000106#endif /* CTK_CONF_MOUSE_SUPPORT */
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000107
108static unsigned short screensaver_timer;
109#define SCREENSAVER_TIMEOUT (5*60)
110
111#if CTK_CONF_MENUS
112/*-----------------------------------------------------------------------------------*/
113/* make_desktopmenu(void)
114 *
115 * Creates the leftmost menu, "Desktop". Since the desktop menu
116 * contains the list of all open windows, this function will be called
117 * whenever a window is opened or closed.
118 */
119static void
120make_desktopmenu(void)
121{
122 struct ctk_window *w;
123
124 desktopmenu.nitems = 0;
125
126 if(windows == NULL) {
127 ctk_menuitem_add(&desktopmenu, "(No windows)");
128 } else {
129 for(w = windows; w != NULL; w = w->next) {
130 ctk_menuitem_add(&desktopmenu, w->title);
131 }
132 }
133}
134#endif /* CTK_CONF_MENUS */
135/*-----------------------------------------------------------------------------------*/
136/* ctk_init(void)
137 *
138 * Initializes CTK. Must be called before any other CTK function.
139 */
140void
141ctk_init(void)
142{
143 ctkid = dispatcher_start(&p);
144
145 windows = NULL;
146 dialog = NULL;
147
148#if CTK_CONF_MENUS
149 ctk_menu_new(&desktopmenu, "Desktop");
150 make_desktopmenu();
151 menus.menus = menus.desktopmenu = &desktopmenu;
152#endif /* CTK_CONF_MENUS */
153
adamdunkelsc4902862003-04-09 00:30:45 +0000154 ctk_mouse_init();
155
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000156 ctk_draw_init();
157
158 height = ctk_draw_height();
159 width = ctk_draw_width();
160
161 desktop_window.active = NULL;
162
163
164 ctk_signal_keypress = dispatcher_sigalloc();
165 ctk_signal_timer = dispatcher_sigalloc();
166 ctk_signal_button_activate = dispatcher_sigalloc();
167 ctk_signal_button_hover = dispatcher_sigalloc();
168 ctk_signal_hyperlink_activate = dispatcher_sigalloc();
169 ctk_signal_hyperlink_hover = dispatcher_sigalloc();
170 ctk_signal_menu_activate = dispatcher_sigalloc();
171 ctk_signal_window_close = dispatcher_sigalloc();
adamdunkelsc4902862003-04-09 00:30:45 +0000172
173 ctk_signal_pointer_move = dispatcher_sigalloc();
174 ctk_signal_pointer_down = dispatcher_sigalloc();
175 ctk_signal_pointer_up = dispatcher_sigalloc();
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000176
177 dispatcher_listen(ctk_signal_timer);
178 dispatcher_timer(ctk_signal_timer, NULL, CLK_TCK);
179
180 mode = CTK_MODE_NORMAL;
181
182 iconx = ICONX_START;
183 icony = ICONY_START;
184
185}
186/*-----------------------------------------------------------------------------------*/
187/* void ctk_mode_set()
188 */
189void
190ctk_mode_set(unsigned char m) {
191 mode = m;
192}
193/*-----------------------------------------------------------------------------------*/
194unsigned char
195ctk_mode_get(void) {
196 return mode;
197}
198/*-----------------------------------------------------------------------------------*/
199void
adamdunkelsd07a5422003-04-05 12:22:35 +0000200ctk_icon_add(CC_REGISTER_ARG struct ctk_widget *icon,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000201 ek_id_t id)
202{
203#if CTK_CONF_ICONS
204 icon->x = iconx;
205 icon->y = icony;
206 icon->widget.icon.owner = id;
207
208 icony += ICONY_DELTA;
209 if(icony >= ICONY_MAX) {
210 icony = ICONY_START;
211 iconx += ICONX_DELTA;
212 }
213
214 ctk_widget_add(&desktop_window, icon);
215#endif /* CTK_CONF_ICONS */
216}
217/*-----------------------------------------------------------------------------------*/
218void
219ctk_dialog_open(struct ctk_window *d)
220{
221 dialog = d;
222}
223/*-----------------------------------------------------------------------------------*/
224void
225ctk_dialog_close(void)
226{
227 dialog = NULL;
228}
229/*-----------------------------------------------------------------------------------*/
230void
adamdunkelsd07a5422003-04-05 12:22:35 +0000231ctk_window_open(CC_REGISTER_ARG struct ctk_window *w)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000232{
233 struct ctk_window *w2;
234
235 /* Check if already open. */
236 for(w2 = windows; w2 != w && w2 != NULL; w2 = w2->next);
237 if(w2 == NULL) {
238 /* Not open, so we add it at the head of the list of open
239 windows. */
240 w->next = windows;
241 if(windows != NULL) {
242 windows->prev = w;
243 }
244 windows = w;
245 w->prev = NULL;
246 } else {
247 /* Window already open, so we move it to the front of the windows
248 list. */
249 if(w != windows) {
250 if(w->next != NULL) {
251 w->next->prev = w->prev;
252 }
253 if(w->prev != NULL) {
254 w->prev->next = w->next;
255 }
256 w->next = windows;
257 windows->prev = w;
258 windows = w;
259 w->prev = NULL;
260 }
261 }
262
263#if CTK_CONF_MENUS
264 /* Recreate the Desktop menu's window entries.*/
265 make_desktopmenu();
266#endif /* CTK_CONF_MENUS */
267}
268/*-----------------------------------------------------------------------------------*/
269void
270ctk_window_close(struct ctk_window *w)
271{
272 struct ctk_window *w2;
273
274 if(w == NULL) {
275 return;
276 }
277
278 /* Check if the window to be closed is the first window on the
279 list. */
280 if(w == windows) {
281 windows = w->next;
282 if(windows != NULL) {
283 windows->prev = NULL;
284 }
285 w->next = w->prev = NULL;
286 } else {
287 /* Otherwise we step through the list until we find the window
288 before the one to be closed. We then redirect its ->next
289 pointer and its ->next->prev. */
adamdunkels3cf116a2003-04-08 19:28:15 +0000290 for(w2 = windows; w2 != NULL && w2->next != w; w2 = w2->next);
291
292 if(w2 == NULL) {
293 /* The window wasn't open, so there is nothing more for us to
294 do. */
295 return;
296 }
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000297
298 if(w->next != NULL) {
299 w->next->prev = w->prev;
300 }
301 w2->next = w->next;
302
303 w->next = w->prev = NULL;
304 }
305
306#if CTK_CONF_MENUS
307 /* Recreate the Desktop menu's window entries.*/
308 make_desktopmenu();
309#endif /* CTK_CONF_MENUS */
310}
311/*-----------------------------------------------------------------------------------*/
adamdunkelsd07a5422003-04-05 12:22:35 +0000312static void
313make_windowbuttons(CC_REGISTER_ARG struct ctk_window *window)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000314{
315#if CTK_CONF_WINDOWMOVE
316 CTK_BUTTON_NEW(&window->titlebutton, 0, -1, window->titlelen, window->title);
317#else
318 CTK_LABEL_NEW(&window->titlebutton, 0, -1, window->titlelen, 1, window->title);
319#endif /* CTK_CONF_WINDOWMOVE */
320 CTK_WIDGET_ADD(window, &window->titlebutton);
321
322
323#if CTK_CONF_WINDOWCLOSE
324 CTK_BUTTON_NEW(&window->closebutton, window->w - 3, -1, 1, "x");
325#else
326 CTK_LABEL_NEW(&window->closebutton, window->w - 4, -1, 3, 1, " ");
327#endif /* CTK_CONF_WINDOWCLOSE */
328 CTK_WIDGET_ADD(window, &window->closebutton);
329}
330/*-----------------------------------------------------------------------------------*/
331void
332ctk_window_clear(struct ctk_window *window)
333{
334 window->active = window->inactive = window->focused = NULL;
335
336 make_windowbuttons(window);
337}
338/*-----------------------------------------------------------------------------------*/
339void
340ctk_menu_add(struct ctk_menu *menu)
341{
342#if CTK_CONF_MENUS
343 struct ctk_menu *m;
344
345 if(lastmenu == NULL) {
346 lastmenu = menu;
347 }
348
349 for(m = menus.menus; m->next != NULL; m = m->next) {
350 if(m == menu) {
351 return;
352 }
353 }
354 m->next = menu;
355 menu->next = NULL;
356#endif /* CTK_CONF_MENUS */
357}
358/*-----------------------------------------------------------------------------------*/
359void
360ctk_menu_remove(struct ctk_menu *menu)
361{
362#if CTK_CONF_MENUS
363 struct ctk_menu *m;
364
365 for(m = menus.menus; m->next != NULL; m = m->next) {
366 if(m->next == menu) {
367 m->next = menu->next;
368 return;
369 }
370 }
371#endif /* CTK_CONF_MENUS */
372}
373/*-----------------------------------------------------------------------------------*/
374static void
375do_redraw_all(unsigned char clipy1, unsigned char clipy2)
376{
377 struct ctk_window *w;
378 struct ctk_widget *widget;
379
380 if(mode != CTK_MODE_NORMAL &&
381 mode != CTK_MODE_WINDOWMOVE) {
382 return;
383 }
384
385 ctk_draw_clear(clipy1, clipy2);
386
387 /* Draw widgets in root window */
388 for(widget = desktop_window.active;
389 widget != NULL; widget = widget->next) {
390 ctk_draw_widget(widget, 0, clipy1, clipy2);
391 }
392
393 /* Draw windows */
394 if(windows != NULL) {
395 /* Find the last window.*/
396 for(w = windows; w->next != NULL; w = w->next);
397
398 /* Draw the windows from back to front. */
399 for(; w != windows; w = w->prev) {
adamdunkels9d3a0e52003-04-02 09:53:59 +0000400 ctk_draw_clear_window(w, 0, clipy1, clipy2);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000401 ctk_draw_window(w, 0, clipy1, clipy2);
402 }
403 /* Draw focused window */
adamdunkels9d3a0e52003-04-02 09:53:59 +0000404 ctk_draw_clear_window(windows, CTK_FOCUS_WINDOW, clipy1, clipy2);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000405 ctk_draw_window(windows, CTK_FOCUS_WINDOW, clipy1, clipy2);
406 }
407
408 /* Draw dialog (if any) */
409 if(dialog != NULL) {
410 ctk_draw_dialog(dialog);
411 }
412
413#if CTK_CONF_MENUS
414 ctk_draw_menus(&menus);
415#endif /* CTK_CONF_MENUS */
416}
417/*-----------------------------------------------------------------------------------*/
418void
419ctk_redraw(void)
420{
421 if(DISPATCHER_CURRENT() == ctkid) {
422 if(mode == CTK_MODE_NORMAL ||
423 mode == CTK_MODE_WINDOWMOVE) {
424 do_redraw_all(1, height);
425 }
426 } else {
427 redraw |= REDRAW_ALL;
428 }
429}
430/*-----------------------------------------------------------------------------------*/
431void
432ctk_window_redraw(struct ctk_window *w)
433{
434 /* Only redraw the window if it is a dialog or if it is the foremost
435 window. */
436 if(mode != CTK_MODE_NORMAL) {
437 return;
438 }
439
440 if(w == dialog) {
441 ctk_draw_dialog(w);
442 } else if(dialog == NULL &&
443#if CTK_CONF_MENUS
444 menus.open == NULL &&
445#endif /* CTK_CONF_MENUS */
446 windows == w) {
447 ctk_draw_window(w, CTK_FOCUS_WINDOW,
448 0, height);
449 }
450}
451/*-----------------------------------------------------------------------------------*/
452static void
adamdunkelsd07a5422003-04-05 12:22:35 +0000453window_new(CC_REGISTER_ARG struct ctk_window *window,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000454 unsigned char w, unsigned char h,
455 char *title)
456{
adamdunkels4b7a7ab2003-04-09 19:22:19 +0000457
458 if(w >= width - 2) {
459 window->x = 0;
460 } else {
461 window->x = (width - w - 2) / 2;
462 }
463 if(h >= height - 3) {
464 window->y = 0;
465 } else {
466 window->y = (height - h - 1) / 2;
467 }
468
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000469 window->w = w;
470 window->h = h;
471 window->title = title;
472 if(title != NULL) {
473 window->titlelen = strlen(title);
474 } else {
475 window->titlelen = 0;
476 }
477 window->next = window->prev = NULL;
478 window->owner = DISPATCHER_CURRENT();
479 window->active = window->inactive = window->focused = NULL;
480}
481/*-----------------------------------------------------------------------------------*/
482void
483ctk_window_new(struct ctk_window *window,
484 unsigned char w, unsigned char h,
485 char *title)
486{
487 window_new(window, w, h, title);
488
489 make_windowbuttons(window);
490}
491/*-----------------------------------------------------------------------------------*/
492void
adamdunkelsd07a5422003-04-05 12:22:35 +0000493ctk_dialog_new(CC_REGISTER_ARG struct ctk_window *window,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000494 unsigned char w, unsigned char h)
495{
496 window_new(window, w, h, NULL);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000497}
498/*-----------------------------------------------------------------------------------*/
499void
adamdunkelsd07a5422003-04-05 12:22:35 +0000500ctk_menu_new(CC_REGISTER_ARG struct ctk_menu *menu,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000501 char *title)
502{
503#if CTK_CONF_MENUS
504 menu->next = NULL;
505 menu->title = title;
506 menu->titlelen = strlen(title);
507 menu->active = 0;
508 menu->nitems = 0;
509#endif /* CTK_CONF_MENUS */
510}
511/*-----------------------------------------------------------------------------------*/
512unsigned char
adamdunkelsd07a5422003-04-05 12:22:35 +0000513ctk_menuitem_add(CC_REGISTER_ARG struct ctk_menu *menu,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000514 char *name)
515{
516#if CTK_CONF_MENUS
517 if(menu->nitems == CTK_CONF_MAXMENUITEMS) {
518 return 0;
519 }
520 menu->items[menu->nitems].title = name;
521 menu->items[menu->nitems].titlelen = strlen(name);
522 return menu->nitems++;
523#else
524 return 0;
525#endif /* CTK_CONF_MENUS */
526}
527/*-----------------------------------------------------------------------------------*/
528static void
529add_redrawwidget(struct ctk_widget *w)
530{
531 static unsigned char i;
532
533 if(redraw_widgetptr == MAX_REDRAWWIDGETS) {
534 redraw |= REDRAW_FOCUS;
535 } else {
536 redraw |= REDRAW_WIDGETS;
537 /* Check if it is in the queue already. If so, we don't add it
538 again. */
539 for(i = 0; i < redraw_widgetptr; ++i) {
540 if(redraw_widgets[i] == w) {
541 return;
542 }
543 }
544 redraw_widgets[redraw_widgetptr++] = w;
545 }
546}
547/*-----------------------------------------------------------------------------------*/
548void
549ctk_widget_redraw(struct ctk_widget *widget)
550{
551 struct ctk_window *window;
552
553 if(mode != CTK_MODE_NORMAL) {
554 return;
555 }
556
557 /* If this function isn't called by CTK itself, we only queue the
558 redraw request. */
559 if(DISPATCHER_CURRENT() != ctkid) {
560 redraw |= REDRAW_WIDGETS;
561 add_redrawwidget(widget);
562 } else {
563
564 /* Only redraw widgets that are in the foremost window. If we
565 would allow redrawing widgets in non-focused windows, we would
566 have to redraw all the windows that cover the non-focused
567 window as well, which would lead to flickering.
568
569 Also, we avoid drawing any widgets when the menus are active.
570 */
571
572#if CTK_CONF_MENUS
573 if(menus.open == NULL)
574#endif /* CTK_CONF_MENUS */
575 {
576 window = widget->window;
577 if(window == dialog) {
578 ctk_draw_widget(widget, CTK_FOCUS_DIALOG, 0, height);
579 } else if(window == windows) {
adamdunkels9d3a0e52003-04-02 09:53:59 +0000580 ctk_draw_widget(widget, CTK_FOCUS_WINDOW, 0, height);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000581 } else if(window == &desktop_window) {
582 ctk_draw_widget(widget, 0, 0, height);
583 }
584 }
585 }
586}
587/*-----------------------------------------------------------------------------------*/
588void
adamdunkelsd07a5422003-04-05 12:22:35 +0000589ctk_widget_add(CC_REGISTER_ARG struct ctk_window *window,
590 CC_REGISTER_ARG struct ctk_widget *widget)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000591{
592 if(widget->type == CTK_WIDGET_LABEL ||
593 widget->type == CTK_WIDGET_SEPARATOR) {
594 widget->next = window->inactive;
595 window->inactive = widget;
596 widget->window = window;
597 } else {
598 widget->next = window->active;
599 window->active = widget;
600 widget->window = window;
601 if(window->focused == NULL) {
602 window->focused = widget;
603 }
604 }
605}
606/*-----------------------------------------------------------------------------------*/
adamdunkelse0683312003-04-09 09:02:52 +0000607static void
608focus_widget(struct ctk_widget *focus)
609{
610 struct ctk_window *window;
611
612 window = focus->window;
613
614 if(focus != window->focused) {
615 window->focused = focus;
616 /* The operation changed the focus, so we emit a "hover" signal
617 for those widgets that support it. */
618
619 if(window->focused->type == CTK_WIDGET_HYPERLINK) {
620 dispatcher_emit(ctk_signal_hyperlink_hover, window->focused,
621 window->owner);
622 } else if(window->focused->type == CTK_WIDGET_BUTTON) {
623 dispatcher_emit(ctk_signal_button_hover, window->focused,
624 window->owner);
625 }
626
627 add_redrawwidget(window->focused);
628 }
629
630}
631/*-----------------------------------------------------------------------------------*/
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000632#define UP 0
633#define DOWN 1
634#define LEFT 2
635#define RIGHT 3
636static void
637switch_focus_widget(unsigned char direction)
638{
639 register struct ctk_window *window;
640 register struct ctk_widget *focus;
641 struct ctk_widget *widget;
642
643
644 if(dialog != NULL) {
645 window = dialog;
646 } else {
647 window = windows;
648 }
649
650 /* If there are no windows open, we move focus around between the
651 icons on the root window instead. */
652 if(window == NULL) {
653 window = &desktop_window;
654 }
655
656 focus = window->focused;
657 add_redrawwidget(focus);
658
659 if((direction & 1) == 0) {
660 /* Move focus "up" */
661 focus = focus->next;
662 } else {
663 /* Move focus "down" */
664 for(widget = window->active;
665 widget != NULL; widget = widget->next) {
666 if(widget->next == focus) {
667 break;
668 }
669 }
670 focus = widget;
671 if(focus == NULL) {
672 if(window->active != NULL) {
673 for(focus = window->active;
674 focus->next != NULL; focus = focus->next);
675 }
676 }
677 }
678 if(focus == NULL) {
679 focus = window->active;
680 }
adamdunkelse0683312003-04-09 09:02:52 +0000681
682 focus_widget(focus);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000683}
684/*-----------------------------------------------------------------------------------*/
685#if CTK_CONF_MENUS
686static void
687switch_open_menu(unsigned char rightleft)
688{
689 struct ctk_menu *menu;
690
691 if(rightleft == 0) {
692 /* Move right */
693 for(menu = menus.menus; menu != NULL; menu = menu->next) {
694 if(menu->next == menus.open) {
695 break;
696 }
697 }
698 lastmenu = menus.open;
699 menus.open = menu;
700 if(menus.open == NULL) {
701 for(menu = menus.menus;
702 menu->next != NULL; menu = menu->next);
703 menus.open = menu;
704 }
705 } else {
706 /* Move to left */
707 lastmenu = menus.open;
708 menus.open = menus.open->next;
709 if(menus.open == NULL) {
710 menus.open = menus.menus;
711 }
712 }
713
714 menus.open->active = 0;
715 /* ctk_redraw();*/
716}
717/*-----------------------------------------------------------------------------------*/
718static void
719switch_menu_item(unsigned char updown)
720{
721 register struct ctk_menu *m;
722
723 m = menus.open;
724
725 if(updown == 0) {
726 /* Move up */
727 if(m->active == 0) {
728 m->active = m->nitems - 1;
729 } else {
730 --m->active;
731 if(m->items[m->active].title[0] == '-') {
732 --m->active;
733 }
734 }
735 } else {
736 /* Move down */
737 if(m->active >= m->nitems - 1) {
738 m->active = 0;
739 } else {
740 ++m->active;
741 if(m->items[m->active].title[0] == '-') {
742 ++m->active;
743 }
744 }
745 }
746
747}
748#endif /* CTK_CONF_MENUS */
749/*-----------------------------------------------------------------------------------*/
750static unsigned char
adamdunkelsd07a5422003-04-05 12:22:35 +0000751activate(CC_REGISTER_ARG struct ctk_widget *w)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000752{
753 static unsigned char len;
754
755 if(w->type == CTK_WIDGET_BUTTON) {
756 if(w == (struct ctk_widget *)&windows->closebutton) {
757#if CTK_CONF_WINDOWCLOSE
adamdunkels5cb690c2003-04-02 11:36:21 +0000758 dispatcher_emit(ctk_signal_window_close, windows, w->window->owner);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000759 ctk_window_close(windows);
760 return REDRAW_ALL;
761#endif /* CTK_CONF_WINDOWCLOSE */
762 } else if(w == (struct ctk_widget *)&windows->titlebutton) {
763#if CTK_CONF_WINDOWCLOSE
764 mode = CTK_MODE_WINDOWMOVE;
765#endif /* CTK_CONF_WINDOWCLOSE */
766 } else {
767 dispatcher_emit(ctk_signal_button_activate, w,
768 w->window->owner);
769 }
770#if CTK_CONF_ICONS
771 } else if(w->type == CTK_WIDGET_ICON) {
772 dispatcher_emit(ctk_signal_button_activate, w,
773 w->widget.icon.owner);
774#endif /* CTK_CONF_ICONS */
775 } else if(w->type == CTK_WIDGET_HYPERLINK) {
776 dispatcher_emit(ctk_signal_hyperlink_activate, w,
777 DISPATCHER_BROADCAST);
778 } else if(w->type == CTK_WIDGET_TEXTENTRY) {
779 if(w->widget.textentry.state == CTK_TEXTENTRY_NORMAL) {
780 w->widget.textentry.state = CTK_TEXTENTRY_EDIT;
781 len = strlen(w->widget.textentry.text);
782 if(w->widget.textentry.xpos > len) {
783 w->widget.textentry.xpos = len;
784 }
785 } else if(w->widget.textentry.state == CTK_TEXTENTRY_EDIT) {
786 w->widget.textentry.state = CTK_TEXTENTRY_NORMAL;
787 }
788 add_redrawwidget(w);
789 return REDRAW_WIDGETS;
790 }
791 return REDRAW_NONE;
792}
793/*-----------------------------------------------------------------------------------*/
794static void
795textentry_input(ctk_arch_key_t c,
adamdunkelsd07a5422003-04-05 12:22:35 +0000796 CC_REGISTER_ARG struct ctk_textentry *t)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000797{
798 static char *cptr, *cptr2;
799 static unsigned char len, txpos, typos, tlen;
800
801 txpos = t->xpos;
802 typos = t->ypos;
803 tlen = t->len;
804
805 cptr = &t->text[txpos + typos * tlen];
806
807 switch(c) {
808 case CH_CURS_LEFT:
809 if(txpos > 0) {
810 --txpos;
811 }
812 break;
813
814 case CH_CURS_RIGHT:
815 if(txpos < tlen &&
816 *cptr != 0) {
817 ++txpos;
818 }
819 break;
820
821 case CH_CURS_UP:
822#if CTK_CONF_TEXTENTRY_MULTILINE
823 if(t->h == 1) {
824 txpos = 0;
825 } else {
826 if(typos > 0) {
827 --typos;
828 } else {
829 t->state = CTK_TEXTENTRY_NORMAL;
830 }
831 }
832#else
833 txpos = 0;
834#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
835 break;
836
837 case CH_CURS_DOWN:
838#if CTK_CONF_TEXTENTRY_MULTILINE
839 if(t->h == 1) {
840 txpos = strlen(t->text);
841 } else {
842 if(typos < t->h - 1) {
843 ++typos;
844 } else {
845 t->state = CTK_TEXTENTRY_NORMAL;
846 }
847 }
848#else
849 txpos = strlen(t->text);
850#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
851 break;
852
853 case CH_ENTER:
854#if CTK_CONF_TEXTENTRY_MULTILINE
855 if(t->h == 1) {
856 t->state = CTK_TEXTENTRY_NORMAL;
857 } else {
858 if(typos < t->h - 1) {
859 ++typos;
860 txpos = 0;
861 } else {
862 t->state = CTK_TEXTENTRY_NORMAL;
863 }
864 }
865#else
866 t->state = CTK_TEXTENTRY_NORMAL;
867#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
868 break;
869
870 default:
871 len = tlen - txpos - 1;
872 if(c == CH_DEL) {
873 if(txpos > 0 && len > 0) {
874 strncpy(cptr - 1, cptr,
875 len);
876 *(cptr + len - 1) = 0;
877 --txpos;
878 }
879 } else {
880 if(len > 0) {
881 cptr2 = cptr + len - 1;
882 while(cptr2 + 1 > cptr) {
883 *(cptr2 + 1) = *cptr2;
884 --cptr2;
885 }
886
887 *cptr = c;
888 ++txpos;
889 }
890 }
891 break;
892 }
893
894 t->xpos = txpos;
895 t->ypos = typos;
896}
897/*-----------------------------------------------------------------------------------*/
898#if CTK_CONF_MENUS
899static unsigned char
900menus_input(ctk_arch_key_t c)
901{
902 struct ctk_window *w;
903
904 if(menus.open->nitems > maxnitems) {
905 maxnitems = menus.open->nitems;
906 }
907
908
909 switch(c) {
910 case CH_CURS_RIGHT:
911 switch_open_menu(1);
912
913 return REDRAW_MENUPART;
914
915 case CH_CURS_DOWN:
916 switch_menu_item(1);
917 return REDRAW_MENUS;
918
919 case CH_CURS_LEFT:
920 switch_open_menu(0);
921 return REDRAW_MENUPART;
922
923 case CH_CURS_UP:
924 switch_menu_item(0);
925 return REDRAW_MENUS;
926
927 case CH_ENTER:
928 lastmenu = menus.open;
929 if(menus.open == &desktopmenu) {
930 for(w = windows; w != NULL; w = w->next) {
931 if(w->title == desktopmenu.items[desktopmenu.active].title) {
932 ctk_window_open(w);
933 menus.open = NULL;
934 return REDRAW_ALL;
935 }
936 }
937 } else {
938 dispatcher_emit(ctk_signal_menu_activate, menus.open,
939 DISPATCHER_BROADCAST);
940 }
941 menus.open = NULL;
942 return REDRAW_MENUPART;
943
adamdunkels88ed9c42003-03-28 12:10:09 +0000944 case CTK_CONF_MENU_KEY:
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000945 lastmenu = menus.open;
946 menus.open = NULL;
947 return REDRAW_MENUPART;
948 }
949}
950#endif /* CTK_CONF_MENUS */
951/*-----------------------------------------------------------------------------------*/
952static void
adamdunkelsc4902862003-04-09 00:30:45 +0000953ctk_idle(void)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000954{
955 static ctk_arch_key_t c;
adamdunkels19a787c2003-04-09 09:22:24 +0000956 static unsigned char i;
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000957 register struct ctk_window *window;
adamdunkels19a787c2003-04-09 09:22:24 +0000958 register struct ctk_widget *widget;
959#if CTK_CONF_MOUSE_SUPPORT
960 static unsigned char mxc, myc, mouse_clicked;
961#endif /* CTK_CONF_MOUSE_SUPPORT */
adamdunkelsc4902862003-04-09 00:30:45 +0000962
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000963#if CTK_CONF_MENUS
964 if(menus.open != NULL) {
965 maxnitems = menus.open->nitems;
966 } else {
967 maxnitems = 0;
968 }
969#endif /* CTK_CONF_MENUS */
970
971 if(mode == CTK_MODE_SCREENSAVER) {
972#ifdef CTK_SCREENSAVER_RUN
973 CTK_SCREENSAVER_RUN();
974#endif /* CTK_SCREENSAVER_RUN */
975 if(ctk_arch_keyavail()) {
976 mode = CTK_MODE_NORMAL;
977 ctk_draw_init();
978 ctk_redraw();
979 }
adamdunkelsc4902862003-04-09 00:30:45 +0000980 } else if(mode == CTK_MODE_NORMAL) {
adamdunkels19a787c2003-04-09 09:22:24 +0000981#if CTK_CONF_MOUSE_SUPPORT
adamdunkelse0683312003-04-09 09:02:52 +0000982 if(dialog != NULL) {
983 window = dialog;
984 } else {
985 window = windows;
986 }
adamdunkelsc4902862003-04-09 00:30:45 +0000987
adamdunkelse0683312003-04-09 09:02:52 +0000988 /* If there has been a change in the mouse button(s), we send out
989 a signal. */
990 mouse_clicked = 0;
991 if(ctk_mouse_button() != mouse_last_button) {
992 mouse_last_button = ctk_mouse_button();
993 if(mouse_last_button == 0) {
994 dispatcher_emit(ctk_signal_pointer_up, NULL, window->owner);
995 } else {
996 dispatcher_emit(ctk_signal_pointer_down, (ek_data_t)mouse_last_button,
997 window->owner);
998 mouse_clicked = 1;
999 }
1000 }
1001
adamdunkelsc4902862003-04-09 00:30:45 +00001002 /* Check if the mouse pointer has moved, and if so we emit a
1003 signal. */
1004 if(ctk_mouse_x() != mouse_last_x ||
adamdunkelse0683312003-04-09 09:02:52 +00001005 ctk_mouse_y() != mouse_last_y ||
1006 mouse_clicked != 0) {
adamdunkelsc4902862003-04-09 00:30:45 +00001007 mouse_last_x = ctk_mouse_x();
1008 mouse_last_y = ctk_mouse_y();
1009 dispatcher_emit(ctk_signal_pointer_move, NULL, window->owner);
1010
1011 /* Find out which widget currently is under the mouse pointer
1012 and give it focus, unless it already has focus. */
1013 mxc = ctk_mouse_xtoc(mouse_last_x);
adamdunkelse0683312003-04-09 09:02:52 +00001014 myc = ctk_mouse_ytoc(mouse_last_y) - 1;
adamdunkelsc4902862003-04-09 00:30:45 +00001015
adamdunkelse0683312003-04-09 09:02:52 +00001016 /* Check if the mouse is in the current window. */
adamdunkelsc4902862003-04-09 00:30:45 +00001017 if(mxc >= window->x &&
1018 mxc <= window->x + window->w &&
1019 myc >= window->y &&
1020 myc <= window->y + window->h) {
1021
adamdunkelse0683312003-04-09 09:02:52 +00001022 mxc = mxc - window->x - 1;
1023 myc = myc - window->y - 1;
1024
adamdunkelsc4902862003-04-09 00:30:45 +00001025
1026 redraw |= REDRAW_WIDGETS;
1027 add_redrawwidget(window->focused);
1028 window->focused = NULL;
1029
1030 /* Now find the appropriate widget to assign focus to. */
1031 for(widget = window->active; widget != NULL; widget = widget->next) {
1032 if(mxc >= widget->x &&
1033 mxc <= widget->x + widget->w &&
adamdunkelse0683312003-04-09 09:02:52 +00001034 myc == widget->y /* &&
adamdunkelsc4902862003-04-09 00:30:45 +00001035 ((widget->type == CTK_WIDGET_BITMAP ||
1036 widget->type == CTK_WIDGET_TEXTENTRY ||
1037 widget->type == CTK_WIDGET_ICON) &&
1038 (myc <= widget->y + ((struct ctk_bitmap *)widget)->h))*/) {
adamdunkelse0683312003-04-09 09:02:52 +00001039 focus_widget(widget);
1040 if(mouse_clicked != 0) {
1041 redraw |= activate(widget);
1042 }
adamdunkelsc4902862003-04-09 00:30:45 +00001043 break;
1044 }
1045 }
adamdunkelse0683312003-04-09 09:02:52 +00001046 } else {
1047 redraw |= REDRAW_WIDGETS;
1048 add_redrawwidget(window->focused);
1049 window->focused = NULL;
adamdunkelsc4902862003-04-09 00:30:45 +00001050 }
1051 }
adamdunkels19a787c2003-04-09 09:22:24 +00001052#endif /* CTK_CONF_MOUSE_SUPPORT */
adamdunkelsc4902862003-04-09 00:30:45 +00001053
adamdunkelsca9ddcb2003-03-19 14:13:31 +00001054 while(ctk_arch_keyavail()) {
1055
1056 screensaver_timer = 0;
1057
1058 c = ctk_arch_getkey();
1059
1060
1061 if(dialog != NULL) {
1062 window = dialog;
1063 } else if(windows != NULL) {
1064 window = windows;
1065 } else {
1066 window = &desktop_window;
1067 }
1068 widget = window->focused;
1069
1070
1071 if(widget != NULL &&
1072 widget->type == CTK_WIDGET_TEXTENTRY &&
1073 widget->widget.textentry.state == CTK_TEXTENTRY_EDIT) {
1074 textentry_input(c, (struct ctk_textentry *)widget);
1075 add_redrawwidget(widget);
1076#if CTK_CONF_MENUS
1077 } else if(menus.open != NULL) {
1078 redraw |= menus_input(c);
1079#endif /* CTK_CONF_MENUS */
1080 } else {
1081 switch(c) {
1082 case CH_CURS_RIGHT:
1083 switch_focus_widget(RIGHT);
1084 break;
1085 case CH_CURS_DOWN:
1086 switch_focus_widget(DOWN);
1087 break;
1088 case CH_CURS_LEFT:
1089 switch_focus_widget(LEFT);
1090 break;
1091 case CH_CURS_UP:
1092 switch_focus_widget(UP);
1093 break;
1094 case CH_ENTER:
1095 redraw |= activate(widget);
1096 break;
1097#if CTK_CONF_MENUS
1098 case CTK_CONF_MENU_KEY:
1099 if(dialog == NULL) {
1100 if(lastmenu == NULL) {
1101 menus.open = menus.menus;
1102 } else {
1103 menus.open = lastmenu;
1104 }
1105 menus.open->active = 0;
1106 redraw |= REDRAW_MENUS;
1107 }
1108 break;
1109#endif /* CTK_CONF_MENUS */
1110 case CTK_CONF_WINDOWSWITCH_KEY:
1111 if(windows != NULL) {
1112 for(window = windows; window->next != NULL;
1113 window = window->next);
1114 ctk_window_open(window);
1115 ctk_redraw();
1116 }
1117 break;
1118 default:
1119 if(widget->type == CTK_WIDGET_TEXTENTRY) {
1120 widget->widget.textentry.state = CTK_TEXTENTRY_EDIT;
1121 textentry_input(c, (struct ctk_textentry *)widget);
1122 add_redrawwidget(widget);
1123 } else {
1124 dispatcher_emit(ctk_signal_keypress, (void *)c,
1125 window->owner);
1126 }
1127 break;
1128 }
1129 }
1130
1131 if(redraw & REDRAW_WIDGETS) {
1132 for(i = 0; i < redraw_widgetptr; ++i) {
1133 ctk_widget_redraw(redraw_widgets[i]);
1134 }
1135 redraw &= ~REDRAW_WIDGETS;
1136 redraw_widgetptr = 0;
1137 }
1138 }
1139 if(redraw & REDRAW_ALL) {
1140 do_redraw_all(1, height);
1141#if CTK_CONF_MENUS
1142 } else if(redraw & REDRAW_MENUPART) {
1143 do_redraw_all(1, maxnitems + 1);
1144 } else if(redraw & REDRAW_MENUS) {
1145 ctk_draw_menus(&menus);
1146#endif /* CTK_CONF_MENUS */
1147 } else if(redraw & REDRAW_FOCUS) {
1148 if(dialog != NULL) {
1149 ctk_window_redraw(dialog);
1150 } else if(windows != NULL) {
1151 ctk_window_redraw(windows);
1152 } else {
1153 ctk_window_redraw(&desktop_window);
1154 }
1155 } else if(redraw & REDRAW_WIDGETS) {
1156 for(i = 0; i < redraw_widgetptr; ++i) {
1157 ctk_widget_redraw(redraw_widgets[i]);
1158 }
1159 }
1160 redraw = 0;
1161 redraw_widgetptr = 0;
1162#if CTK_CONF_WINDOWMOVE
1163 } else if(mode == CTK_MODE_WINDOWMOVE) {
1164
1165 redraw = 0;
1166
1167 window = windows;
1168
1169 while(mode == CTK_MODE_WINDOWMOVE && ctk_arch_keyavail()) {
1170
1171 screensaver_timer = 0;
1172
1173 c = ctk_arch_getkey();
1174
1175 switch(c) {
1176 case CH_CURS_RIGHT:
1177 ++window->x;
1178 if(window->x + window->w + 1 >= width) {
1179 --window->x;
1180 }
1181 redraw = REDRAW_ALL;
1182 break;
1183 case CH_CURS_LEFT:
1184 if(window->x > 0) {
1185 --window->x;
1186 }
1187 redraw = REDRAW_ALL;
1188 break;
1189 case CH_CURS_DOWN:
1190 ++window->y;
1191 if(window->y + window->h + 2 >= height) {
1192 --window->y;
1193 }
1194 redraw = REDRAW_ALL;
1195 break;
1196 case CH_CURS_UP:
1197 if(window->y > 0) {
1198 --window->y;
1199 }
1200 redraw = REDRAW_ALL;
1201 break;
1202 case CH_ENTER:
1203 case CH_ESC:
1204 mode = CTK_MODE_NORMAL;
1205 redraw = REDRAW_ALL;
1206 break;
1207 }
1208 }
1209 if(redraw & REDRAW_ALL) {
1210 do_redraw_all(1, height);
1211 }
1212 redraw = 0;
1213#endif /* CTK_CONF_WINDOWMOVE */
1214 }
1215}
1216/*-----------------------------------------------------------------------------------*/
adamdunkels78c03dc2003-04-09 13:45:05 +00001217static
1218DISPATCHER_SIGHANDLER(ctk_sighandler, s, data)
adamdunkelsca9ddcb2003-03-19 14:13:31 +00001219{
adamdunkels78c03dc2003-04-09 13:45:05 +00001220 DISPATCHER_SIGHANDLER_ARGS(s, data);
1221
adamdunkelsca9ddcb2003-03-19 14:13:31 +00001222 if(s == ctk_signal_timer) {
1223 if(mode == CTK_MODE_NORMAL) {
1224 ++screensaver_timer;
1225 if(screensaver_timer == SCREENSAVER_TIMEOUT) {
1226#ifdef CTK_SCREENSAVER_INIT
1227 CTK_SCREENSAVER_INIT();
1228#endif /* CTK_SCREENSAVER_INIT */
1229 mode = CTK_MODE_SCREENSAVER;
1230 screensaver_timer = 0;
1231 }
1232 }
1233 dispatcher_timer(ctk_signal_timer, data, CLK_TCK);
1234 }
1235}
1236/*-----------------------------------------------------------------------------------*/
adamdunkelsd07a5422003-04-05 12:22:35 +00001237