blob: 2c700853304ba2f73f53849215c2bca951a26173 [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 *
35 * $Id: ctk.c,v 1.1 2003/03/19 14:13:34 adamdunkels Exp $
36 *
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) {
381 ctk_draw_clear_window(w, 0, clipy1, clipy2);
382 ctk_draw_window(w, 0, clipy1, clipy2);
383 }
384 /* Draw focused window */
385 ctk_draw_clear_window(windows, CTK_FOCUS_WINDOW, clipy1, clipy2);
386 ctk_draw_window(windows, CTK_FOCUS_WINDOW, clipy1, clipy2);
387 }
388
389 /* Draw dialog (if any) */
390 if(dialog != NULL) {
391 ctk_draw_dialog(dialog);
392 }
393
394#if CTK_CONF_MENUS
395 ctk_draw_menus(&menus);
396#endif /* CTK_CONF_MENUS */
397}
398/*-----------------------------------------------------------------------------------*/
399void
400ctk_redraw(void)
401{
402 if(DISPATCHER_CURRENT() == ctkid) {
403 if(mode == CTK_MODE_NORMAL ||
404 mode == CTK_MODE_WINDOWMOVE) {
405 do_redraw_all(1, height);
406 }
407 } else {
408 redraw |= REDRAW_ALL;
409 }
410}
411/*-----------------------------------------------------------------------------------*/
412void
413ctk_window_redraw(struct ctk_window *w)
414{
415 /* Only redraw the window if it is a dialog or if it is the foremost
416 window. */
417 if(mode != CTK_MODE_NORMAL) {
418 return;
419 }
420
421 if(w == dialog) {
422 ctk_draw_dialog(w);
423 } else if(dialog == NULL &&
424#if CTK_CONF_MENUS
425 menus.open == NULL &&
426#endif /* CTK_CONF_MENUS */
427 windows == w) {
428 ctk_draw_window(w, CTK_FOCUS_WINDOW,
429 0, height);
430 }
431}
432/*-----------------------------------------------------------------------------------*/
433static void
434window_new(register struct ctk_window *window,
435 unsigned char w, unsigned char h,
436 char *title)
437{
438 window->x = window->y = 0;
439 window->w = w;
440 window->h = h;
441 window->title = title;
442 if(title != NULL) {
443 window->titlelen = strlen(title);
444 } else {
445 window->titlelen = 0;
446 }
447 window->next = window->prev = NULL;
448 window->owner = DISPATCHER_CURRENT();
449 window->active = window->inactive = window->focused = NULL;
450}
451/*-----------------------------------------------------------------------------------*/
452void
453ctk_window_new(struct ctk_window *window,
454 unsigned char w, unsigned char h,
455 char *title)
456{
457 window_new(window, w, h, title);
458
459 make_windowbuttons(window);
460}
461/*-----------------------------------------------------------------------------------*/
462void
463ctk_dialog_new(register struct ctk_window *window,
464 unsigned char w, unsigned char h)
465{
466 window_new(window, w, h, NULL);
467
468 window->x = (width - w) / 2;
469 window->y = (height - h - 1) / 2;
470}
471/*-----------------------------------------------------------------------------------*/
472void
473ctk_menu_new(register struct ctk_menu *menu,
474 char *title)
475{
476#if CTK_CONF_MENUS
477 menu->next = NULL;
478 menu->title = title;
479 menu->titlelen = strlen(title);
480 menu->active = 0;
481 menu->nitems = 0;
482#endif /* CTK_CONF_MENUS */
483}
484/*-----------------------------------------------------------------------------------*/
485unsigned char
486ctk_menuitem_add(register struct ctk_menu *menu,
487 char *name)
488{
489#if CTK_CONF_MENUS
490 if(menu->nitems == CTK_CONF_MAXMENUITEMS) {
491 return 0;
492 }
493 menu->items[menu->nitems].title = name;
494 menu->items[menu->nitems].titlelen = strlen(name);
495 return menu->nitems++;
496#else
497 return 0;
498#endif /* CTK_CONF_MENUS */
499}
500/*-----------------------------------------------------------------------------------*/
501static void
502add_redrawwidget(struct ctk_widget *w)
503{
504 static unsigned char i;
505
506 if(redraw_widgetptr == MAX_REDRAWWIDGETS) {
507 redraw |= REDRAW_FOCUS;
508 } else {
509 redraw |= REDRAW_WIDGETS;
510 /* Check if it is in the queue already. If so, we don't add it
511 again. */
512 for(i = 0; i < redraw_widgetptr; ++i) {
513 if(redraw_widgets[i] == w) {
514 return;
515 }
516 }
517 redraw_widgets[redraw_widgetptr++] = w;
518 }
519}
520/*-----------------------------------------------------------------------------------*/
521void
522ctk_widget_redraw(struct ctk_widget *widget)
523{
524 struct ctk_window *window;
525
526 if(mode != CTK_MODE_NORMAL) {
527 return;
528 }
529
530 /* If this function isn't called by CTK itself, we only queue the
531 redraw request. */
532 if(DISPATCHER_CURRENT() != ctkid) {
533 redraw |= REDRAW_WIDGETS;
534 add_redrawwidget(widget);
535 } else {
536
537 /* Only redraw widgets that are in the foremost window. If we
538 would allow redrawing widgets in non-focused windows, we would
539 have to redraw all the windows that cover the non-focused
540 window as well, which would lead to flickering.
541
542 Also, we avoid drawing any widgets when the menus are active.
543 */
544
545#if CTK_CONF_MENUS
546 if(menus.open == NULL)
547#endif /* CTK_CONF_MENUS */
548 {
549 window = widget->window;
550 if(window == dialog) {
551 ctk_draw_widget(widget, CTK_FOCUS_DIALOG, 0, height);
552 } else if(window == windows) {
553 ctk_draw_widget(widget, CTK_FOCUS_WINDOW, 0, height);
554 } else if(window == &desktop_window) {
555 ctk_draw_widget(widget, 0, 0, height);
556 }
557 }
558 }
559}
560/*-----------------------------------------------------------------------------------*/
561void
562ctk_widget_add(register struct ctk_window *window,
563 register struct ctk_widget *widget)
564{
565 if(widget->type == CTK_WIDGET_LABEL ||
566 widget->type == CTK_WIDGET_SEPARATOR) {
567 widget->next = window->inactive;
568 window->inactive = widget;
569 widget->window = window;
570 } else {
571 widget->next = window->active;
572 window->active = widget;
573 widget->window = window;
574 if(window->focused == NULL) {
575 window->focused = widget;
576 }
577 }
578}
579/*-----------------------------------------------------------------------------------*/
580#define UP 0
581#define DOWN 1
582#define LEFT 2
583#define RIGHT 3
584static void
585switch_focus_widget(unsigned char direction)
586{
587 register struct ctk_window *window;
588 register struct ctk_widget *focus;
589 struct ctk_widget *widget;
590
591
592 if(dialog != NULL) {
593 window = dialog;
594 } else {
595 window = windows;
596 }
597
598 /* If there are no windows open, we move focus around between the
599 icons on the root window instead. */
600 if(window == NULL) {
601 window = &desktop_window;
602 }
603
604 focus = window->focused;
605 add_redrawwidget(focus);
606
607 if((direction & 1) == 0) {
608 /* Move focus "up" */
609 focus = focus->next;
610 } else {
611 /* Move focus "down" */
612 for(widget = window->active;
613 widget != NULL; widget = widget->next) {
614 if(widget->next == focus) {
615 break;
616 }
617 }
618 focus = widget;
619 if(focus == NULL) {
620 if(window->active != NULL) {
621 for(focus = window->active;
622 focus->next != NULL; focus = focus->next);
623 }
624 }
625 }
626 if(focus == NULL) {
627 focus = window->active;
628 }
629
630 if(focus != window->focused) {
631 window->focused = focus;
632 /* The operation changed the focus, so we emit a "hover" signal
633 for those widgets that support it. */
634
635 if(window->focused->type == CTK_WIDGET_HYPERLINK) {
636 dispatcher_emit(ctk_signal_hyperlink_hover, window->focused,
637 window->owner);
638 } else if(window->focused->type == CTK_WIDGET_BUTTON) {
639 dispatcher_emit(ctk_signal_button_hover, window->focused,
640 window->owner);
641 }
642
643 add_redrawwidget(window->focused);
644 }
645}
646/*-----------------------------------------------------------------------------------*/
647#if CTK_CONF_MENUS
648static void
649switch_open_menu(unsigned char rightleft)
650{
651 struct ctk_menu *menu;
652
653 if(rightleft == 0) {
654 /* Move right */
655 for(menu = menus.menus; menu != NULL; menu = menu->next) {
656 if(menu->next == menus.open) {
657 break;
658 }
659 }
660 lastmenu = menus.open;
661 menus.open = menu;
662 if(menus.open == NULL) {
663 for(menu = menus.menus;
664 menu->next != NULL; menu = menu->next);
665 menus.open = menu;
666 }
667 } else {
668 /* Move to left */
669 lastmenu = menus.open;
670 menus.open = menus.open->next;
671 if(menus.open == NULL) {
672 menus.open = menus.menus;
673 }
674 }
675
676 menus.open->active = 0;
677 /* ctk_redraw();*/
678}
679/*-----------------------------------------------------------------------------------*/
680static void
681switch_menu_item(unsigned char updown)
682{
683 register struct ctk_menu *m;
684
685 m = menus.open;
686
687 if(updown == 0) {
688 /* Move up */
689 if(m->active == 0) {
690 m->active = m->nitems - 1;
691 } else {
692 --m->active;
693 if(m->items[m->active].title[0] == '-') {
694 --m->active;
695 }
696 }
697 } else {
698 /* Move down */
699 if(m->active >= m->nitems - 1) {
700 m->active = 0;
701 } else {
702 ++m->active;
703 if(m->items[m->active].title[0] == '-') {
704 ++m->active;
705 }
706 }
707 }
708
709}
710#endif /* CTK_CONF_MENUS */
711/*-----------------------------------------------------------------------------------*/
712static unsigned char
713activate(register struct ctk_widget *w)
714{
715 static unsigned char len;
716
717 if(w->type == CTK_WIDGET_BUTTON) {
718 if(w == (struct ctk_widget *)&windows->closebutton) {
719#if CTK_CONF_WINDOWCLOSE
720 /*dispatcher_emit(ctk_signal_window_close, windows, w->window->owner);*/
721 ctk_window_close(windows);
722 return REDRAW_ALL;
723#endif /* CTK_CONF_WINDOWCLOSE */
724 } else if(w == (struct ctk_widget *)&windows->titlebutton) {
725#if CTK_CONF_WINDOWCLOSE
726 mode = CTK_MODE_WINDOWMOVE;
727#endif /* CTK_CONF_WINDOWCLOSE */
728 } else {
729 dispatcher_emit(ctk_signal_button_activate, w,
730 w->window->owner);
731 }
732#if CTK_CONF_ICONS
733 } else if(w->type == CTK_WIDGET_ICON) {
734 dispatcher_emit(ctk_signal_button_activate, w,
735 w->widget.icon.owner);
736#endif /* CTK_CONF_ICONS */
737 } else if(w->type == CTK_WIDGET_HYPERLINK) {
738 dispatcher_emit(ctk_signal_hyperlink_activate, w,
739 DISPATCHER_BROADCAST);
740 } else if(w->type == CTK_WIDGET_TEXTENTRY) {
741 if(w->widget.textentry.state == CTK_TEXTENTRY_NORMAL) {
742 w->widget.textentry.state = CTK_TEXTENTRY_EDIT;
743 len = strlen(w->widget.textentry.text);
744 if(w->widget.textentry.xpos > len) {
745 w->widget.textentry.xpos = len;
746 }
747 } else if(w->widget.textentry.state == CTK_TEXTENTRY_EDIT) {
748 w->widget.textentry.state = CTK_TEXTENTRY_NORMAL;
749 }
750 add_redrawwidget(w);
751 return REDRAW_WIDGETS;
752 }
753 return REDRAW_NONE;
754}
755/*-----------------------------------------------------------------------------------*/
756static void
757textentry_input(ctk_arch_key_t c,
758 register struct ctk_textentry *t)
759{
760 static char *cptr, *cptr2;
761 static unsigned char len, txpos, typos, tlen;
762
763 txpos = t->xpos;
764 typos = t->ypos;
765 tlen = t->len;
766
767 cptr = &t->text[txpos + typos * tlen];
768
769 switch(c) {
770 case CH_CURS_LEFT:
771 if(txpos > 0) {
772 --txpos;
773 }
774 break;
775
776 case CH_CURS_RIGHT:
777 if(txpos < tlen &&
778 *cptr != 0) {
779 ++txpos;
780 }
781 break;
782
783 case CH_CURS_UP:
784#if CTK_CONF_TEXTENTRY_MULTILINE
785 if(t->h == 1) {
786 txpos = 0;
787 } else {
788 if(typos > 0) {
789 --typos;
790 } else {
791 t->state = CTK_TEXTENTRY_NORMAL;
792 }
793 }
794#else
795 txpos = 0;
796#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
797 break;
798
799 case CH_CURS_DOWN:
800#if CTK_CONF_TEXTENTRY_MULTILINE
801 if(t->h == 1) {
802 txpos = strlen(t->text);
803 } else {
804 if(typos < t->h - 1) {
805 ++typos;
806 } else {
807 t->state = CTK_TEXTENTRY_NORMAL;
808 }
809 }
810#else
811 txpos = strlen(t->text);
812#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
813 break;
814
815 case CH_ENTER:
816#if CTK_CONF_TEXTENTRY_MULTILINE
817 if(t->h == 1) {
818 t->state = CTK_TEXTENTRY_NORMAL;
819 } else {
820 if(typos < t->h - 1) {
821 ++typos;
822 txpos = 0;
823 } else {
824 t->state = CTK_TEXTENTRY_NORMAL;
825 }
826 }
827#else
828 t->state = CTK_TEXTENTRY_NORMAL;
829#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
830 break;
831
832 default:
833 len = tlen - txpos - 1;
834 if(c == CH_DEL) {
835 if(txpos > 0 && len > 0) {
836 strncpy(cptr - 1, cptr,
837 len);
838 *(cptr + len - 1) = 0;
839 --txpos;
840 }
841 } else {
842 if(len > 0) {
843 cptr2 = cptr + len - 1;
844 while(cptr2 + 1 > cptr) {
845 *(cptr2 + 1) = *cptr2;
846 --cptr2;
847 }
848
849 *cptr = c;
850 ++txpos;
851 }
852 }
853 break;
854 }
855
856 t->xpos = txpos;
857 t->ypos = typos;
858}
859/*-----------------------------------------------------------------------------------*/
860#if CTK_CONF_MENUS
861static unsigned char
862menus_input(ctk_arch_key_t c)
863{
864 struct ctk_window *w;
865
866 if(menus.open->nitems > maxnitems) {
867 maxnitems = menus.open->nitems;
868 }
869
870
871 switch(c) {
872 case CH_CURS_RIGHT:
873 switch_open_menu(1);
874
875 return REDRAW_MENUPART;
876
877 case CH_CURS_DOWN:
878 switch_menu_item(1);
879 return REDRAW_MENUS;
880
881 case CH_CURS_LEFT:
882 switch_open_menu(0);
883 return REDRAW_MENUPART;
884
885 case CH_CURS_UP:
886 switch_menu_item(0);
887 return REDRAW_MENUS;
888
889 case CH_ENTER:
890 lastmenu = menus.open;
891 if(menus.open == &desktopmenu) {
892 for(w = windows; w != NULL; w = w->next) {
893 if(w->title == desktopmenu.items[desktopmenu.active].title) {
894 ctk_window_open(w);
895 menus.open = NULL;
896 return REDRAW_ALL;
897 }
898 }
899 } else {
900 dispatcher_emit(ctk_signal_menu_activate, menus.open,
901 DISPATCHER_BROADCAST);
902 }
903 menus.open = NULL;
904 return REDRAW_MENUPART;
905
906 case CH_F1:
907 lastmenu = menus.open;
908 menus.open = NULL;
909 return REDRAW_MENUPART;
910 }
911}
912#endif /* CTK_CONF_MENUS */
913/*-----------------------------------------------------------------------------------*/
914static void
915idle(void)
916{
917 static ctk_arch_key_t c;
918 static unsigned char i;
919 register struct ctk_window *window;
920 register struct ctk_widget *widget;
921
922#if CTK_CONF_MENUS
923 if(menus.open != NULL) {
924 maxnitems = menus.open->nitems;
925 } else {
926 maxnitems = 0;
927 }
928#endif /* CTK_CONF_MENUS */
929
930 if(mode == CTK_MODE_SCREENSAVER) {
931#ifdef CTK_SCREENSAVER_RUN
932 CTK_SCREENSAVER_RUN();
933#endif /* CTK_SCREENSAVER_RUN */
934 if(ctk_arch_keyavail()) {
935 mode = CTK_MODE_NORMAL;
936 ctk_draw_init();
937 ctk_redraw();
938 }
939 } else if(mode == CTK_MODE_NORMAL) {
940 while(ctk_arch_keyavail()) {
941
942 screensaver_timer = 0;
943
944 c = ctk_arch_getkey();
945
946
947 if(dialog != NULL) {
948 window = dialog;
949 } else if(windows != NULL) {
950 window = windows;
951 } else {
952 window = &desktop_window;
953 }
954 widget = window->focused;
955
956
957 if(widget != NULL &&
958 widget->type == CTK_WIDGET_TEXTENTRY &&
959 widget->widget.textentry.state == CTK_TEXTENTRY_EDIT) {
960 textentry_input(c, (struct ctk_textentry *)widget);
961 add_redrawwidget(widget);
962#if CTK_CONF_MENUS
963 } else if(menus.open != NULL) {
964 redraw |= menus_input(c);
965#endif /* CTK_CONF_MENUS */
966 } else {
967 switch(c) {
968 case CH_CURS_RIGHT:
969 switch_focus_widget(RIGHT);
970 break;
971 case CH_CURS_DOWN:
972 switch_focus_widget(DOWN);
973 break;
974 case CH_CURS_LEFT:
975 switch_focus_widget(LEFT);
976 break;
977 case CH_CURS_UP:
978 switch_focus_widget(UP);
979 break;
980 case CH_ENTER:
981 redraw |= activate(widget);
982 break;
983#if CTK_CONF_MENUS
984 case CTK_CONF_MENU_KEY:
985 if(dialog == NULL) {
986 if(lastmenu == NULL) {
987 menus.open = menus.menus;
988 } else {
989 menus.open = lastmenu;
990 }
991 menus.open->active = 0;
992 redraw |= REDRAW_MENUS;
993 }
994 break;
995#endif /* CTK_CONF_MENUS */
996 case CTK_CONF_WINDOWSWITCH_KEY:
997 if(windows != NULL) {
998 for(window = windows; window->next != NULL;
999 window = window->next);
1000 ctk_window_open(window);
1001 ctk_redraw();
1002 }
1003 break;
1004 default:
1005 if(widget->type == CTK_WIDGET_TEXTENTRY) {
1006 widget->widget.textentry.state = CTK_TEXTENTRY_EDIT;
1007 textentry_input(c, (struct ctk_textentry *)widget);
1008 add_redrawwidget(widget);
1009 } else {
1010 dispatcher_emit(ctk_signal_keypress, (void *)c,
1011 window->owner);
1012 }
1013 break;
1014 }
1015 }
1016
1017 if(redraw & REDRAW_WIDGETS) {
1018 for(i = 0; i < redraw_widgetptr; ++i) {
1019 ctk_widget_redraw(redraw_widgets[i]);
1020 }
1021 redraw &= ~REDRAW_WIDGETS;
1022 redraw_widgetptr = 0;
1023 }
1024 }
1025 if(redraw & REDRAW_ALL) {
1026 do_redraw_all(1, height);
1027#if CTK_CONF_MENUS
1028 } else if(redraw & REDRAW_MENUPART) {
1029 do_redraw_all(1, maxnitems + 1);
1030 } else if(redraw & REDRAW_MENUS) {
1031 ctk_draw_menus(&menus);
1032#endif /* CTK_CONF_MENUS */
1033 } else if(redraw & REDRAW_FOCUS) {
1034 if(dialog != NULL) {
1035 ctk_window_redraw(dialog);
1036 } else if(windows != NULL) {
1037 ctk_window_redraw(windows);
1038 } else {
1039 ctk_window_redraw(&desktop_window);
1040 }
1041 } else if(redraw & REDRAW_WIDGETS) {
1042 for(i = 0; i < redraw_widgetptr; ++i) {
1043 ctk_widget_redraw(redraw_widgets[i]);
1044 }
1045 }
1046 redraw = 0;
1047 redraw_widgetptr = 0;
1048#if CTK_CONF_WINDOWMOVE
1049 } else if(mode == CTK_MODE_WINDOWMOVE) {
1050
1051 redraw = 0;
1052
1053 window = windows;
1054
1055 while(mode == CTK_MODE_WINDOWMOVE && ctk_arch_keyavail()) {
1056
1057 screensaver_timer = 0;
1058
1059 c = ctk_arch_getkey();
1060
1061 switch(c) {
1062 case CH_CURS_RIGHT:
1063 ++window->x;
1064 if(window->x + window->w + 1 >= width) {
1065 --window->x;
1066 }
1067 redraw = REDRAW_ALL;
1068 break;
1069 case CH_CURS_LEFT:
1070 if(window->x > 0) {
1071 --window->x;
1072 }
1073 redraw = REDRAW_ALL;
1074 break;
1075 case CH_CURS_DOWN:
1076 ++window->y;
1077 if(window->y + window->h + 2 >= height) {
1078 --window->y;
1079 }
1080 redraw = REDRAW_ALL;
1081 break;
1082 case CH_CURS_UP:
1083 if(window->y > 0) {
1084 --window->y;
1085 }
1086 redraw = REDRAW_ALL;
1087 break;
1088 case CH_ENTER:
1089 case CH_ESC:
1090 mode = CTK_MODE_NORMAL;
1091 redraw = REDRAW_ALL;
1092 break;
1093 }
1094 }
1095 if(redraw & REDRAW_ALL) {
1096 do_redraw_all(1, height);
1097 }
1098 redraw = 0;
1099#endif /* CTK_CONF_WINDOWMOVE */
1100 }
1101}
1102/*-----------------------------------------------------------------------------------*/
1103static void
1104sighandler(ek_signal_t s, ek_data_t data)
1105{
1106 if(s == ctk_signal_timer) {
1107 if(mode == CTK_MODE_NORMAL) {
1108 ++screensaver_timer;
1109 if(screensaver_timer == SCREENSAVER_TIMEOUT) {
1110#ifdef CTK_SCREENSAVER_INIT
1111 CTK_SCREENSAVER_INIT();
1112#endif /* CTK_SCREENSAVER_INIT */
1113 mode = CTK_MODE_SCREENSAVER;
1114 screensaver_timer = 0;
1115 }
1116 }
1117 dispatcher_timer(ctk_signal_timer, data, CLK_TCK);
1118 }
1119}
1120/*-----------------------------------------------------------------------------------*/