blob: 8b1f50915a37edc48d7f09f998ebe92996667f11 [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 *
adamdunkels88ed9c42003-03-28 12:10:09 +000035 * $Id: ctk.c,v 1.2 2003/03/28 12:10:09 adamdunkels Exp $
adamdunkelsca9ddcb2003-03-19 14:13:31 +000036 *
37 */
38
39#include "ek.h"
40#include "dispatcher.h"
41#include "ctk.h"
42#include "ctk-draw.h"
43#include "ctk-conf.h"
44
45static unsigned char height, width;
46
47static unsigned char mode;
48
49static struct ctk_window desktop_window;
50static struct ctk_window *windows;
51static struct ctk_window *dialog;
52
53#if CTK_CONF_MENUS
54static struct ctk_menus menus;
55static struct ctk_menu *lastmenu;
56static struct ctk_menu desktopmenu;
57#endif /* CTK_CONF_MENUS */
58
59#ifndef NULL
60#define NULL (void *)0
61#endif /* NULL */
62
63
64#define REDRAW_NONE 0
65#define REDRAW_ALL 1
66#define REDRAW_FOCUS 2
67#define REDRAW_WIDGETS 4
68#define REDRAW_MENUS 8
69#define REDRAW_MENUPART 16
70
71#define MAX_REDRAWWIDGETS 4
72static unsigned char redraw;
73static struct ctk_widget *redraw_widgets[MAX_REDRAWWIDGETS];
74static unsigned char redraw_widgetptr;
75static unsigned char maxnitems;
76
77static unsigned char iconx, icony;
78#define ICONX_START (width - 5)
79#define ICONY_START 0
80#define ICONX_DELTA -8
81#define ICONY_DELTA 5
82#define ICONY_MAX (height - 4)
83
84static void idle(void);
85static void sighandler(ek_signal_t s, ek_data_t data);
86static struct dispatcher_proc p =
87 {DISPATCHER_PROC("CTK Contiki GUI", idle, sighandler, NULL)};
88static ek_id_t ctkid;
89
90
91ek_signal_t ctk_signal_keypress,
92 ctk_signal_timer,
93 ctk_signal_button_activate,
94 ctk_signal_button_hover,
95 ctk_signal_hyperlink_activate,
96 ctk_signal_hyperlink_hover,
97 ctk_signal_menu_activate,
98 ctk_signal_window_close;
99
100
101static unsigned short screensaver_timer;
102#define SCREENSAVER_TIMEOUT (5*60)
103
104#if CTK_CONF_MENUS
105/*-----------------------------------------------------------------------------------*/
106/* make_desktopmenu(void)
107 *
108 * Creates the leftmost menu, "Desktop". Since the desktop menu
109 * contains the list of all open windows, this function will be called
110 * whenever a window is opened or closed.
111 */
112static void
113make_desktopmenu(void)
114{
115 struct ctk_window *w;
116
117 desktopmenu.nitems = 0;
118
119 if(windows == NULL) {
120 ctk_menuitem_add(&desktopmenu, "(No windows)");
121 } else {
122 for(w = windows; w != NULL; w = w->next) {
123 ctk_menuitem_add(&desktopmenu, w->title);
124 }
125 }
126}
127#endif /* CTK_CONF_MENUS */
128/*-----------------------------------------------------------------------------------*/
129/* ctk_init(void)
130 *
131 * Initializes CTK. Must be called before any other CTK function.
132 */
133void
134ctk_init(void)
135{
136 ctkid = dispatcher_start(&p);
137
138 windows = NULL;
139 dialog = NULL;
140
141#if CTK_CONF_MENUS
142 ctk_menu_new(&desktopmenu, "Desktop");
143 make_desktopmenu();
144 menus.menus = menus.desktopmenu = &desktopmenu;
145#endif /* CTK_CONF_MENUS */
146
147 ctk_draw_init();
148
149 height = ctk_draw_height();
150 width = ctk_draw_width();
151
152 desktop_window.active = NULL;
153
154
155 ctk_signal_keypress = dispatcher_sigalloc();
156 ctk_signal_timer = dispatcher_sigalloc();
157 ctk_signal_button_activate = dispatcher_sigalloc();
158 ctk_signal_button_hover = dispatcher_sigalloc();
159 ctk_signal_hyperlink_activate = dispatcher_sigalloc();
160 ctk_signal_hyperlink_hover = dispatcher_sigalloc();
161 ctk_signal_menu_activate = dispatcher_sigalloc();
162 ctk_signal_window_close = dispatcher_sigalloc();
163
164 dispatcher_listen(ctk_signal_timer);
165 dispatcher_timer(ctk_signal_timer, NULL, CLK_TCK);
166
167 mode = CTK_MODE_NORMAL;
168
169 iconx = ICONX_START;
170 icony = ICONY_START;
171
172}
173/*-----------------------------------------------------------------------------------*/
174/* void ctk_mode_set()
175 */
176void
177ctk_mode_set(unsigned char m) {
178 mode = m;
179}
180/*-----------------------------------------------------------------------------------*/
181unsigned char
182ctk_mode_get(void) {
183 return mode;
184}
185/*-----------------------------------------------------------------------------------*/
186void
187ctk_icon_add(register struct ctk_widget *icon,
188 ek_id_t id)
189{
190#if CTK_CONF_ICONS
191 icon->x = iconx;
192 icon->y = icony;
193 icon->widget.icon.owner = id;
194
195 icony += ICONY_DELTA;
196 if(icony >= ICONY_MAX) {
197 icony = ICONY_START;
198 iconx += ICONX_DELTA;
199 }
200
201 ctk_widget_add(&desktop_window, icon);
202#endif /* CTK_CONF_ICONS */
203}
204/*-----------------------------------------------------------------------------------*/
205void
206ctk_dialog_open(struct ctk_window *d)
207{
208 dialog = d;
209}
210/*-----------------------------------------------------------------------------------*/
211void
212ctk_dialog_close(void)
213{
214 dialog = NULL;
215}
216/*-----------------------------------------------------------------------------------*/
217void
218ctk_window_open(register struct ctk_window *w)
219{
220 struct ctk_window *w2;
221
222 /* Check if already open. */
223 for(w2 = windows; w2 != w && w2 != NULL; w2 = w2->next);
224 if(w2 == NULL) {
225 /* Not open, so we add it at the head of the list of open
226 windows. */
227 w->next = windows;
228 if(windows != NULL) {
229 windows->prev = w;
230 }
231 windows = w;
232 w->prev = NULL;
233 } else {
234 /* Window already open, so we move it to the front of the windows
235 list. */
236 if(w != windows) {
237 if(w->next != NULL) {
238 w->next->prev = w->prev;
239 }
240 if(w->prev != NULL) {
241 w->prev->next = w->next;
242 }
243 w->next = windows;
244 windows->prev = w;
245 windows = w;
246 w->prev = NULL;
247 }
248 }
249
250#if CTK_CONF_MENUS
251 /* Recreate the Desktop menu's window entries.*/
252 make_desktopmenu();
253#endif /* CTK_CONF_MENUS */
254}
255/*-----------------------------------------------------------------------------------*/
256void
257ctk_window_close(struct ctk_window *w)
258{
259 struct ctk_window *w2;
260
261 if(w == NULL) {
262 return;
263 }
264
265 /* Check if the window to be closed is the first window on the
266 list. */
267 if(w == windows) {
268 windows = w->next;
269 if(windows != NULL) {
270 windows->prev = NULL;
271 }
272 w->next = w->prev = NULL;
273 } else {
274 /* Otherwise we step through the list until we find the window
275 before the one to be closed. We then redirect its ->next
276 pointer and its ->next->prev. */
277 for(w2 = windows; w2->next != w; w2 = w2->next);
278
279 if(w->next != NULL) {
280 w->next->prev = w->prev;
281 }
282 w2->next = w->next;
283
284 w->next = w->prev = NULL;
285 }
286
287#if CTK_CONF_MENUS
288 /* Recreate the Desktop menu's window entries.*/
289 make_desktopmenu();
290#endif /* CTK_CONF_MENUS */
291}
292/*-----------------------------------------------------------------------------------*/
293static void
294make_windowbuttons(register struct ctk_window *window)
295{
296#if CTK_CONF_WINDOWMOVE
297 CTK_BUTTON_NEW(&window->titlebutton, 0, -1, window->titlelen, window->title);
298#else
299 CTK_LABEL_NEW(&window->titlebutton, 0, -1, window->titlelen, 1, window->title);
300#endif /* CTK_CONF_WINDOWMOVE */
301 CTK_WIDGET_ADD(window, &window->titlebutton);
302
303
304#if CTK_CONF_WINDOWCLOSE
305 CTK_BUTTON_NEW(&window->closebutton, window->w - 3, -1, 1, "x");
306#else
307 CTK_LABEL_NEW(&window->closebutton, window->w - 4, -1, 3, 1, " ");
308#endif /* CTK_CONF_WINDOWCLOSE */
309 CTK_WIDGET_ADD(window, &window->closebutton);
310}
311/*-----------------------------------------------------------------------------------*/
312void
313ctk_window_clear(struct ctk_window *window)
314{
315 window->active = window->inactive = window->focused = NULL;
316
317 make_windowbuttons(window);
318}
319/*-----------------------------------------------------------------------------------*/
320void
321ctk_menu_add(struct ctk_menu *menu)
322{
323#if CTK_CONF_MENUS
324 struct ctk_menu *m;
325
326 if(lastmenu == NULL) {
327 lastmenu = menu;
328 }
329
330 for(m = menus.menus; m->next != NULL; m = m->next) {
331 if(m == menu) {
332 return;
333 }
334 }
335 m->next = menu;
336 menu->next = NULL;
337#endif /* CTK_CONF_MENUS */
338}
339/*-----------------------------------------------------------------------------------*/
340void
341ctk_menu_remove(struct ctk_menu *menu)
342{
343#if CTK_CONF_MENUS
344 struct ctk_menu *m;
345
346 for(m = menus.menus; m->next != NULL; m = m->next) {
347 if(m->next == menu) {
348 m->next = menu->next;
349 return;
350 }
351 }
352#endif /* CTK_CONF_MENUS */
353}
354/*-----------------------------------------------------------------------------------*/
355static void
356do_redraw_all(unsigned char clipy1, unsigned char clipy2)
357{
358 struct ctk_window *w;
359 struct ctk_widget *widget;
360
361 if(mode != CTK_MODE_NORMAL &&
362 mode != CTK_MODE_WINDOWMOVE) {
363 return;
364 }
365
366 ctk_draw_clear(clipy1, clipy2);
367
368 /* Draw widgets in root window */
369 for(widget = desktop_window.active;
370 widget != NULL; widget = widget->next) {
371 ctk_draw_widget(widget, 0, clipy1, clipy2);
372 }
373
374 /* Draw windows */
375 if(windows != NULL) {
376 /* Find the last window.*/
377 for(w = windows; w->next != NULL; w = w->next);
378
379 /* Draw the windows from back to front. */
380 for(; w != windows; w = w->prev) {
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000381 ctk_draw_window(w, 0, clipy1, clipy2);
382 }
383 /* Draw focused window */
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000384 ctk_draw_window(windows, CTK_FOCUS_WINDOW, clipy1, clipy2);
385 }
386
387 /* Draw dialog (if any) */
388 if(dialog != NULL) {
389 ctk_draw_dialog(dialog);
390 }
391
392#if CTK_CONF_MENUS
393 ctk_draw_menus(&menus);
394#endif /* CTK_CONF_MENUS */
395}
396/*-----------------------------------------------------------------------------------*/
397void
398ctk_redraw(void)
399{
400 if(DISPATCHER_CURRENT() == ctkid) {
401 if(mode == CTK_MODE_NORMAL ||
402 mode == CTK_MODE_WINDOWMOVE) {
403 do_redraw_all(1, height);
404 }
405 } else {
406 redraw |= REDRAW_ALL;
407 }
408}
409/*-----------------------------------------------------------------------------------*/
410void
411ctk_window_redraw(struct ctk_window *w)
412{
413 /* Only redraw the window if it is a dialog or if it is the foremost
414 window. */
415 if(mode != CTK_MODE_NORMAL) {
416 return;
417 }
418
419 if(w == dialog) {
420 ctk_draw_dialog(w);
421 } else if(dialog == NULL &&
422#if CTK_CONF_MENUS
423 menus.open == NULL &&
424#endif /* CTK_CONF_MENUS */
425 windows == w) {
426 ctk_draw_window(w, CTK_FOCUS_WINDOW,
427 0, height);
428 }
429}
430/*-----------------------------------------------------------------------------------*/
431static void
432window_new(register struct ctk_window *window,
433 unsigned char w, unsigned char h,
434 char *title)
435{
436 window->x = window->y = 0;
437 window->w = w;
438 window->h = h;
439 window->title = title;
440 if(title != NULL) {
441 window->titlelen = strlen(title);
442 } else {
443 window->titlelen = 0;
444 }
445 window->next = window->prev = NULL;
446 window->owner = DISPATCHER_CURRENT();
447 window->active = window->inactive = window->focused = NULL;
448}
449/*-----------------------------------------------------------------------------------*/
450void
451ctk_window_new(struct ctk_window *window,
452 unsigned char w, unsigned char h,
453 char *title)
454{
455 window_new(window, w, h, title);
456
457 make_windowbuttons(window);
458}
459/*-----------------------------------------------------------------------------------*/
460void
461ctk_dialog_new(register struct ctk_window *window,
462 unsigned char w, unsigned char h)
463{
464 window_new(window, w, h, NULL);
465
466 window->x = (width - w) / 2;
467 window->y = (height - h - 1) / 2;
468}
469/*-----------------------------------------------------------------------------------*/
470void
471ctk_menu_new(register struct ctk_menu *menu,
472 char *title)
473{
474#if CTK_CONF_MENUS
475 menu->next = NULL;
476 menu->title = title;
477 menu->titlelen = strlen(title);
478 menu->active = 0;
479 menu->nitems = 0;
480#endif /* CTK_CONF_MENUS */
481}
482/*-----------------------------------------------------------------------------------*/
483unsigned char
484ctk_menuitem_add(register struct ctk_menu *menu,
485 char *name)
486{
487#if CTK_CONF_MENUS
488 if(menu->nitems == CTK_CONF_MAXMENUITEMS) {
489 return 0;
490 }
491 menu->items[menu->nitems].title = name;
492 menu->items[menu->nitems].titlelen = strlen(name);
493 return menu->nitems++;
494#else
495 return 0;
496#endif /* CTK_CONF_MENUS */
497}
498/*-----------------------------------------------------------------------------------*/
499static void
500add_redrawwidget(struct ctk_widget *w)
501{
502 static unsigned char i;
503
504 if(redraw_widgetptr == MAX_REDRAWWIDGETS) {
505 redraw |= REDRAW_FOCUS;
506 } else {
507 redraw |= REDRAW_WIDGETS;
508 /* Check if it is in the queue already. If so, we don't add it
509 again. */
510 for(i = 0; i < redraw_widgetptr; ++i) {
511 if(redraw_widgets[i] == w) {
512 return;
513 }
514 }
515 redraw_widgets[redraw_widgetptr++] = w;
516 }
517}
518/*-----------------------------------------------------------------------------------*/
519void
520ctk_widget_redraw(struct ctk_widget *widget)
521{
522 struct ctk_window *window;
523
524 if(mode != CTK_MODE_NORMAL) {
525 return;
526 }
527
528 /* If this function isn't called by CTK itself, we only queue the
529 redraw request. */
530 if(DISPATCHER_CURRENT() != ctkid) {
531 redraw |= REDRAW_WIDGETS;
532 add_redrawwidget(widget);
533 } else {
534
535 /* Only redraw widgets that are in the foremost window. If we
536 would allow redrawing widgets in non-focused windows, we would
537 have to redraw all the windows that cover the non-focused
538 window as well, which would lead to flickering.
539
540 Also, we avoid drawing any widgets when the menus are active.
541 */
542
543#if CTK_CONF_MENUS
544 if(menus.open == NULL)
545#endif /* CTK_CONF_MENUS */
546 {
547 window = widget->window;
548 if(window == dialog) {
549 ctk_draw_widget(widget, CTK_FOCUS_DIALOG, 0, height);
550 } else if(window == windows) {
adamdunkels88ed9c42003-03-28 12:10:09 +0000551 ctk_draw_widget(widget, CTK_FOCUS_WINDOW, 0, height);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000552 } else if(window == &desktop_window) {
553 ctk_draw_widget(widget, 0, 0, height);
554 }
555 }
556 }
557}
558/*-----------------------------------------------------------------------------------*/
559void
560ctk_widget_add(register struct ctk_window *window,
561 register struct ctk_widget *widget)
562{
563 if(widget->type == CTK_WIDGET_LABEL ||
564 widget->type == CTK_WIDGET_SEPARATOR) {
565 widget->next = window->inactive;
566 window->inactive = widget;
567 widget->window = window;
568 } else {
569 widget->next = window->active;
570 window->active = widget;
571 widget->window = window;
572 if(window->focused == NULL) {
573 window->focused = widget;
574 }
575 }
576}
577/*-----------------------------------------------------------------------------------*/
578#define UP 0
579#define DOWN 1
580#define LEFT 2
581#define RIGHT 3
582static void
583switch_focus_widget(unsigned char direction)
584{
585 register struct ctk_window *window;
586 register struct ctk_widget *focus;
587 struct ctk_widget *widget;
588
589
590 if(dialog != NULL) {
591 window = dialog;
592 } else {
593 window = windows;
594 }
595
596 /* If there are no windows open, we move focus around between the
597 icons on the root window instead. */
598 if(window == NULL) {
599 window = &desktop_window;
600 }
601
602 focus = window->focused;
603 add_redrawwidget(focus);
604
605 if((direction & 1) == 0) {
606 /* Move focus "up" */
607 focus = focus->next;
608 } else {
609 /* Move focus "down" */
610 for(widget = window->active;
611 widget != NULL; widget = widget->next) {
612 if(widget->next == focus) {
613 break;
614 }
615 }
616 focus = widget;
617 if(focus == NULL) {
618 if(window->active != NULL) {
619 for(focus = window->active;
620 focus->next != NULL; focus = focus->next);
621 }
622 }
623 }
624 if(focus == NULL) {
625 focus = window->active;
626 }
627
628 if(focus != window->focused) {
629 window->focused = focus;
630 /* The operation changed the focus, so we emit a "hover" signal
631 for those widgets that support it. */
632
633 if(window->focused->type == CTK_WIDGET_HYPERLINK) {
634 dispatcher_emit(ctk_signal_hyperlink_hover, window->focused,
635 window->owner);
636 } else if(window->focused->type == CTK_WIDGET_BUTTON) {
637 dispatcher_emit(ctk_signal_button_hover, window->focused,
638 window->owner);
639 }
640
641 add_redrawwidget(window->focused);
642 }
643}
644/*-----------------------------------------------------------------------------------*/
645#if CTK_CONF_MENUS
646static void
647switch_open_menu(unsigned char rightleft)
648{
649 struct ctk_menu *menu;
650
651 if(rightleft == 0) {
652 /* Move right */
653 for(menu = menus.menus; menu != NULL; menu = menu->next) {
654 if(menu->next == menus.open) {
655 break;
656 }
657 }
658 lastmenu = menus.open;
659 menus.open = menu;
660 if(menus.open == NULL) {
661 for(menu = menus.menus;
662 menu->next != NULL; menu = menu->next);
663 menus.open = menu;
664 }
665 } else {
666 /* Move to left */
667 lastmenu = menus.open;
668 menus.open = menus.open->next;
669 if(menus.open == NULL) {
670 menus.open = menus.menus;
671 }
672 }
673
674 menus.open->active = 0;
675 /* ctk_redraw();*/
676}
677/*-----------------------------------------------------------------------------------*/
678static void
679switch_menu_item(unsigned char updown)
680{
681 register struct ctk_menu *m;
682
683 m = menus.open;
684
685 if(updown == 0) {
686 /* Move up */
687 if(m->active == 0) {
688 m->active = m->nitems - 1;
689 } else {
690 --m->active;
691 if(m->items[m->active].title[0] == '-') {
692 --m->active;
693 }
694 }
695 } else {
696 /* Move down */
697 if(m->active >= m->nitems - 1) {
698 m->active = 0;
699 } else {
700 ++m->active;
701 if(m->items[m->active].title[0] == '-') {
702 ++m->active;
703 }
704 }
705 }
706
707}
708#endif /* CTK_CONF_MENUS */
709/*-----------------------------------------------------------------------------------*/
710static unsigned char
711activate(register struct ctk_widget *w)
712{
713 static unsigned char len;
714
715 if(w->type == CTK_WIDGET_BUTTON) {
716 if(w == (struct ctk_widget *)&windows->closebutton) {
717#if CTK_CONF_WINDOWCLOSE
718 /*dispatcher_emit(ctk_signal_window_close, windows, w->window->owner);*/
719 ctk_window_close(windows);
720 return REDRAW_ALL;
721#endif /* CTK_CONF_WINDOWCLOSE */
722 } else if(w == (struct ctk_widget *)&windows->titlebutton) {
723#if CTK_CONF_WINDOWCLOSE
724 mode = CTK_MODE_WINDOWMOVE;
725#endif /* CTK_CONF_WINDOWCLOSE */
726 } else {
727 dispatcher_emit(ctk_signal_button_activate, w,
728 w->window->owner);
729 }
730#if CTK_CONF_ICONS
731 } else if(w->type == CTK_WIDGET_ICON) {
732 dispatcher_emit(ctk_signal_button_activate, w,
733 w->widget.icon.owner);
734#endif /* CTK_CONF_ICONS */
735 } else if(w->type == CTK_WIDGET_HYPERLINK) {
736 dispatcher_emit(ctk_signal_hyperlink_activate, w,
737 DISPATCHER_BROADCAST);
738 } else if(w->type == CTK_WIDGET_TEXTENTRY) {
739 if(w->widget.textentry.state == CTK_TEXTENTRY_NORMAL) {
740 w->widget.textentry.state = CTK_TEXTENTRY_EDIT;
741 len = strlen(w->widget.textentry.text);
742 if(w->widget.textentry.xpos > len) {
743 w->widget.textentry.xpos = len;
744 }
745 } else if(w->widget.textentry.state == CTK_TEXTENTRY_EDIT) {
746 w->widget.textentry.state = CTK_TEXTENTRY_NORMAL;
747 }
748 add_redrawwidget(w);
749 return REDRAW_WIDGETS;
750 }
751 return REDRAW_NONE;
752}
753/*-----------------------------------------------------------------------------------*/
754static void
755textentry_input(ctk_arch_key_t c,
756 register struct ctk_textentry *t)
757{
758 static char *cptr, *cptr2;
759 static unsigned char len, txpos, typos, tlen;
760
761 txpos = t->xpos;
762 typos = t->ypos;
763 tlen = t->len;
764
765 cptr = &t->text[txpos + typos * tlen];
766
767 switch(c) {
768 case CH_CURS_LEFT:
769 if(txpos > 0) {
770 --txpos;
771 }
772 break;
773
774 case CH_CURS_RIGHT:
775 if(txpos < tlen &&
776 *cptr != 0) {
777 ++txpos;
778 }
779 break;
780
781 case CH_CURS_UP:
782#if CTK_CONF_TEXTENTRY_MULTILINE
783 if(t->h == 1) {
784 txpos = 0;
785 } else {
786 if(typos > 0) {
787 --typos;
788 } else {
789 t->state = CTK_TEXTENTRY_NORMAL;
790 }
791 }
792#else
793 txpos = 0;
794#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
795 break;
796
797 case CH_CURS_DOWN:
798#if CTK_CONF_TEXTENTRY_MULTILINE
799 if(t->h == 1) {
800 txpos = strlen(t->text);
801 } else {
802 if(typos < t->h - 1) {
803 ++typos;
804 } else {
805 t->state = CTK_TEXTENTRY_NORMAL;
806 }
807 }
808#else
809 txpos = strlen(t->text);
810#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
811 break;
812
813 case CH_ENTER:
814#if CTK_CONF_TEXTENTRY_MULTILINE
815 if(t->h == 1) {
816 t->state = CTK_TEXTENTRY_NORMAL;
817 } else {
818 if(typos < t->h - 1) {
819 ++typos;
820 txpos = 0;
821 } else {
822 t->state = CTK_TEXTENTRY_NORMAL;
823 }
824 }
825#else
826 t->state = CTK_TEXTENTRY_NORMAL;
827#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
828 break;
829
830 default:
831 len = tlen - txpos - 1;
832 if(c == CH_DEL) {
833 if(txpos > 0 && len > 0) {
834 strncpy(cptr - 1, cptr,
835 len);
836 *(cptr + len - 1) = 0;
837 --txpos;
838 }
839 } else {
840 if(len > 0) {
841 cptr2 = cptr + len - 1;
842 while(cptr2 + 1 > cptr) {
843 *(cptr2 + 1) = *cptr2;
844 --cptr2;
845 }
846
847 *cptr = c;
848 ++txpos;
849 }
850 }
851 break;
852 }
853
854 t->xpos = txpos;
855 t->ypos = typos;
856}
857/*-----------------------------------------------------------------------------------*/
858#if CTK_CONF_MENUS
859static unsigned char
860menus_input(ctk_arch_key_t c)
861{
862 struct ctk_window *w;
863
864 if(menus.open->nitems > maxnitems) {
865 maxnitems = menus.open->nitems;
866 }
867
868
869 switch(c) {
870 case CH_CURS_RIGHT:
871 switch_open_menu(1);
872
873 return REDRAW_MENUPART;
874
875 case CH_CURS_DOWN:
876 switch_menu_item(1);
877 return REDRAW_MENUS;
878
879 case CH_CURS_LEFT:
880 switch_open_menu(0);
881 return REDRAW_MENUPART;
882
883 case CH_CURS_UP:
884 switch_menu_item(0);
885 return REDRAW_MENUS;
886
887 case CH_ENTER:
888 lastmenu = menus.open;
889 if(menus.open == &desktopmenu) {
890 for(w = windows; w != NULL; w = w->next) {
891 if(w->title == desktopmenu.items[desktopmenu.active].title) {
892 ctk_window_open(w);
893 menus.open = NULL;
894 return REDRAW_ALL;
895 }
896 }
897 } else {
898 dispatcher_emit(ctk_signal_menu_activate, menus.open,
899 DISPATCHER_BROADCAST);
900 }
901 menus.open = NULL;
902 return REDRAW_MENUPART;
903
adamdunkels88ed9c42003-03-28 12:10:09 +0000904 case CTK_CONF_MENU_KEY:
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000905 lastmenu = menus.open;
906 menus.open = NULL;
907 return REDRAW_MENUPART;
908 }
909}
910#endif /* CTK_CONF_MENUS */
911/*-----------------------------------------------------------------------------------*/
912static void
913idle(void)
914{
915 static ctk_arch_key_t c;
916 static unsigned char i;
917 register struct ctk_window *window;
918 register struct ctk_widget *widget;
919
920#if CTK_CONF_MENUS
921 if(menus.open != NULL) {
922 maxnitems = menus.open->nitems;
923 } else {
924 maxnitems = 0;
925 }
926#endif /* CTK_CONF_MENUS */
927
928 if(mode == CTK_MODE_SCREENSAVER) {
929#ifdef CTK_SCREENSAVER_RUN
930 CTK_SCREENSAVER_RUN();
931#endif /* CTK_SCREENSAVER_RUN */
932 if(ctk_arch_keyavail()) {
933 mode = CTK_MODE_NORMAL;
934 ctk_draw_init();
935 ctk_redraw();
936 }
937 } else if(mode == CTK_MODE_NORMAL) {
938 while(ctk_arch_keyavail()) {
939
940 screensaver_timer = 0;
941
942 c = ctk_arch_getkey();
943
944
945 if(dialog != NULL) {
946 window = dialog;
947 } else if(windows != NULL) {
948 window = windows;
949 } else {
950 window = &desktop_window;
951 }
952 widget = window->focused;
953
954
955 if(widget != NULL &&
956 widget->type == CTK_WIDGET_TEXTENTRY &&
957 widget->widget.textentry.state == CTK_TEXTENTRY_EDIT) {
958 textentry_input(c, (struct ctk_textentry *)widget);
959 add_redrawwidget(widget);
960#if CTK_CONF_MENUS
961 } else if(menus.open != NULL) {
962 redraw |= menus_input(c);
963#endif /* CTK_CONF_MENUS */
964 } else {
965 switch(c) {
966 case CH_CURS_RIGHT:
967 switch_focus_widget(RIGHT);
968 break;
969 case CH_CURS_DOWN:
970 switch_focus_widget(DOWN);
971 break;
972 case CH_CURS_LEFT:
973 switch_focus_widget(LEFT);
974 break;
975 case CH_CURS_UP:
976 switch_focus_widget(UP);
977 break;
978 case CH_ENTER:
979 redraw |= activate(widget);
980 break;
981#if CTK_CONF_MENUS
982 case CTK_CONF_MENU_KEY:
983 if(dialog == NULL) {
984 if(lastmenu == NULL) {
985 menus.open = menus.menus;
986 } else {
987 menus.open = lastmenu;
988 }
989 menus.open->active = 0;
990 redraw |= REDRAW_MENUS;
991 }
992 break;
993#endif /* CTK_CONF_MENUS */
994 case CTK_CONF_WINDOWSWITCH_KEY:
995 if(windows != NULL) {
996 for(window = windows; window->next != NULL;
997 window = window->next);
998 ctk_window_open(window);
999 ctk_redraw();
1000 }
1001 break;
1002 default:
1003 if(widget->type == CTK_WIDGET_TEXTENTRY) {
1004 widget->widget.textentry.state = CTK_TEXTENTRY_EDIT;
1005 textentry_input(c, (struct ctk_textentry *)widget);
1006 add_redrawwidget(widget);
1007 } else {
1008 dispatcher_emit(ctk_signal_keypress, (void *)c,
1009 window->owner);
1010 }
1011 break;
1012 }
1013 }
1014
1015 if(redraw & REDRAW_WIDGETS) {
1016 for(i = 0; i < redraw_widgetptr; ++i) {
1017 ctk_widget_redraw(redraw_widgets[i]);
1018 }
1019 redraw &= ~REDRAW_WIDGETS;
1020 redraw_widgetptr = 0;
1021 }
1022 }
1023 if(redraw & REDRAW_ALL) {
1024 do_redraw_all(1, height);
1025#if CTK_CONF_MENUS
1026 } else if(redraw & REDRAW_MENUPART) {
1027 do_redraw_all(1, maxnitems + 1);
1028 } else if(redraw & REDRAW_MENUS) {
1029 ctk_draw_menus(&menus);
1030#endif /* CTK_CONF_MENUS */
1031 } else if(redraw & REDRAW_FOCUS) {
1032 if(dialog != NULL) {
1033 ctk_window_redraw(dialog);
1034 } else if(windows != NULL) {
1035 ctk_window_redraw(windows);
1036 } else {
1037 ctk_window_redraw(&desktop_window);
1038 }
1039 } else if(redraw & REDRAW_WIDGETS) {
1040 for(i = 0; i < redraw_widgetptr; ++i) {
1041 ctk_widget_redraw(redraw_widgets[i]);
1042 }
1043 }
1044 redraw = 0;
1045 redraw_widgetptr = 0;
1046#if CTK_CONF_WINDOWMOVE
1047 } else if(mode == CTK_MODE_WINDOWMOVE) {
1048
1049 redraw = 0;
1050
1051 window = windows;
1052
1053 while(mode == CTK_MODE_WINDOWMOVE && ctk_arch_keyavail()) {
1054
1055 screensaver_timer = 0;
1056
1057 c = ctk_arch_getkey();
1058
1059 switch(c) {
1060 case CH_CURS_RIGHT:
1061 ++window->x;
1062 if(window->x + window->w + 1 >= width) {
1063 --window->x;
1064 }
1065 redraw = REDRAW_ALL;
1066 break;
1067 case CH_CURS_LEFT:
1068 if(window->x > 0) {
1069 --window->x;
1070 }
1071 redraw = REDRAW_ALL;
1072 break;
1073 case CH_CURS_DOWN:
1074 ++window->y;
1075 if(window->y + window->h + 2 >= height) {
1076 --window->y;
1077 }
1078 redraw = REDRAW_ALL;
1079 break;
1080 case CH_CURS_UP:
1081 if(window->y > 0) {
1082 --window->y;
1083 }
1084 redraw = REDRAW_ALL;
1085 break;
1086 case CH_ENTER:
1087 case CH_ESC:
1088 mode = CTK_MODE_NORMAL;
1089 redraw = REDRAW_ALL;
1090 break;
1091 }
1092 }
1093 if(redraw & REDRAW_ALL) {
1094 do_redraw_all(1, height);
1095 }
1096 redraw = 0;
1097#endif /* CTK_CONF_WINDOWMOVE */
1098 }
1099}
1100/*-----------------------------------------------------------------------------------*/
1101static void
1102sighandler(ek_signal_t s, ek_data_t data)
1103{
1104 if(s == ctk_signal_timer) {
1105 if(mode == CTK_MODE_NORMAL) {
1106 ++screensaver_timer;
1107 if(screensaver_timer == SCREENSAVER_TIMEOUT) {
1108#ifdef CTK_SCREENSAVER_INIT
1109 CTK_SCREENSAVER_INIT();
1110#endif /* CTK_SCREENSAVER_INIT */
1111 mode = CTK_MODE_SCREENSAVER;
1112 screensaver_timer = 0;
1113 }
1114 }
1115 dispatcher_timer(ctk_signal_timer, data, CLK_TCK);
1116 }
1117}
1118/*-----------------------------------------------------------------------------------*/