adding a bunch of code (vesa, keyb, mouse, etc) to the menu
authorJohn Tsiombikas <nuclear@member.fsf.org>
Wed, 9 Aug 2023 03:15:59 +0000 (06:15 +0300)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Wed, 9 Aug 2023 03:15:59 +0000 (06:15 +0300)
33 files changed:
.gitignore
menu/Makefile
menu/Makefile.wat [new file with mode: 0644]
menu/src/app.c [new file with mode: 0644]
menu/src/app.h [new file with mode: 0644]
menu/src/dos/cdpmi.c [new file with mode: 0644]
menu/src/dos/cdpmi.h [new file with mode: 0644]
menu/src/dos/dosutil.h [new file with mode: 0644]
menu/src/dos/drv.h [new file with mode: 0644]
menu/src/dos/drv_s3.c [new file with mode: 0644]
menu/src/dos/drv_vbe.c [new file with mode: 0644]
menu/src/dos/drv_vga.c [new file with mode: 0644]
menu/src/dos/keyb.c [new file with mode: 0644]
menu/src/dos/keyb.h [new file with mode: 0644]
menu/src/dos/main.c [new file with mode: 0644]
menu/src/dos/mouse.asm [new file with mode: 0644]
menu/src/dos/mouse.h [new file with mode: 0644]
menu/src/dos/pit8254.h [new file with mode: 0644]
menu/src/dos/scancode.h [new file with mode: 0644]
menu/src/dos/timer.c [new file with mode: 0644]
menu/src/dos/vbe.h [new file with mode: 0644]
menu/src/dos/vga.h [new file with mode: 0644]
menu/src/dos/vidsys.c [new file with mode: 0644]
menu/src/dos/vidsys.h [new file with mode: 0644]
menu/src/logger.c [new file with mode: 0644]
menu/src/logger.h [new file with mode: 0644]
menu/src/menuscr.c [new file with mode: 0644]
menu/src/rtk.c [new file with mode: 0644]
menu/src/rtk.h [new file with mode: 0644]
menu/src/rtk_draw.c [new file with mode: 0644]
menu/src/rtk_impl.h [new file with mode: 0644]
menu/src/sizeint.h [new file with mode: 0644]
menu/src/timer.h [new file with mode: 0644]

index e45a17c..a8bcdd3 100644 (file)
@@ -1,5 +1,11 @@
 *.o
+*.obj
 *.swp
 *.d
 *.pgm
 *.metrics
+*.occ
+*.lnk
+*.err
+fontconv
+data/
index bb74e85..995762e 100644 (file)
@@ -14,6 +14,7 @@ endif
 #opt = -O3
 dbg = -g
 warn = -pedantic -Wall
+inc = -Isrc/dos
 
 CC = $(TOOLPREFIX)gcc
 AR = $(TOOLPREFIX)ar
diff --git a/menu/Makefile.wat b/menu/Makefile.wat
new file mode 100644 (file)
index 0000000..ff75920
--- /dev/null
@@ -0,0 +1,62 @@
+!ifdef __UNIX__
+dosobj = src/dos/main.obj src/dos/keyb.obj src/dos/mouse.obj src/dos/timer.obj &
+       src/dos/cdpmi.obj src/dos/vidsys.obj src/dos/drv_vga.obj src/dos/drv_vbe.obj &
+       src/dos/drv_s3.obj
+appobj = src/app.obj src/logger.obj src/menuscr.obj
+rtkobj = src/rtk.obj src/rtk_draw.obj
+
+incpath = -Isrc -Isrc/dos
+!else
+dosobj = src\dos\main.obj src\dos\keyb.obj src\dos\mouse.obj src\dos\timer.obj &
+       src\dos\cdpmi.obj src\dos\vidsys.obj src\dos\drv_vga.obj src\dos\drv_vbe.obj &
+       src\dos\drv_s3.obj
+appobj = src\app.obj src\logger.obj src\menuscr.obj
+rtkobj = src\rtk.obj src\rtk_draw.obj
+
+incpath = -Isrc -Isrc\dos
+!endif
+
+obj = $(dosobj) $(appobj) $(rtkobj)
+bin = menu.exe
+
+opt = -otexan
+
+AS = nasm
+CC = wcc386
+LD = wlink
+ASFLAGS = -fobj
+CFLAGS = -d3 $(opt) $(def) -s -zq -bt=dos $(incpath)
+#LDFLAGS = option map $(libpath) library { $(libs) }
+
+$(bin): cflags.occ $(obj) $(libs)
+       %write objects.lnk $(obj)
+       %write ldflags.lnk $(LDFLAGS)
+       $(LD) debug all name $@ system dos4g file { @objects } @ldflags
+
+.c: src;src/dos
+.asm: src;src/dos
+
+cflags.occ: Makefile
+       %write $@ $(CFLAGS)
+
+.c.obj: .autodepend
+       $(CC) -fo=$@ @cflags.occ $[*
+
+.asm.obj:
+       nasm $(ASFLAGS) -o $@ $[*.asm
+
+
+!ifdef __UNIX__
+clean: .symbolic
+       rm -f $(obj)
+       rm -f $(bin)
+       rm -f cflags.occ *.lnk
+!else
+clean: .symbolic
+       del src\*.obj
+       del src\dos\*.obj
+       del src\gaw\*.obj
+       del *.lnk
+       del cflags.occ
+       del $(bin)
+!endif
diff --git a/menu/src/app.c b/menu/src/app.c
new file mode 100644 (file)
index 0000000..bb7f73f
--- /dev/null
@@ -0,0 +1,221 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "app.h"
+#include "timer.h"
+#include "rtk.h"
+
+int mouse_x, mouse_y, mouse_state[3];
+unsigned int modkeys;
+int scr_width, scr_height;
+int fullscr;
+
+long time_msec;
+
+struct app_screen *cur_scr;
+
+unsigned char *framebuf;
+
+/* available screens */
+#define MAX_SCREENS    8
+static struct app_screen *screens[MAX_SCREENS];
+static int num_screens;
+
+
+int app_init(void)
+{
+       int i;
+       char *start_scr_name;
+       static rtk_draw_ops guigfx = {gui_fill, 0, gui_drawtext, gui_textrect};
+
+       rtk_setup(&guigfx);
+
+       /* initialize screens */
+       screens[num_screens++] = &menuscr;
+
+       start_scr_name = getenv("START_SCREEN");
+
+       for(i=0; i<num_screens; i++) {
+               if(screens[i]->init() == -1) {
+                       return -1;
+               }
+       }
+
+       time_msec = get_msec();
+
+       for(i=0; i<num_screens; i++) {
+               if(screens[i]->name && start_scr_name && strcmp(screens[i]->name, start_scr_name) == 0) {
+                       app_chscr(screens[i]);
+                       break;
+               }
+       }
+       if(!cur_scr) {
+               app_chscr(&menuscr);
+       }
+
+       return 0;
+}
+
+void app_shutdown(void)
+{
+       int i;
+
+       for(i=0; i<num_screens; i++) {
+               if(screens[i]->destroy) {
+                       screens[i]->destroy();
+               }
+       }
+
+       cleanup_logger();
+}
+
+void app_display(void)
+{
+       time_msec = get_msec();
+
+       cur_scr->display();
+}
+
+void app_reshape(int x, int y)
+{
+       int numpix = x * y;
+       int prev_numpix = scr_width * scr_height;
+
+       if(!framebuf || numpix > prev_numpix) {
+               void *tmp;
+               if(!(tmp = realloc(framebuf, numpix * sizeof *framebuf))) {
+                       errormsg("failed to resize framebuffer to %dx%d\n", x, y);
+                       return;
+               }
+               framebuf = tmp;
+       }
+
+       scr_width = x;
+       scr_height = y;
+
+       if(cur_scr && cur_scr->reshape) {
+               cur_scr->reshape(x, y);
+       }
+
+       app_invalidate(0, 0, 0, 0);
+}
+
+void app_keyboard(int key, int press)
+{
+       long msec;
+       static long prev_esc;
+
+       if(press) {
+               switch(key) {
+#ifdef DBG_ESCQUIT
+               case 27:
+                       msec = get_msec();
+                       if(msec - prev_esc < 1000) {
+                               app_quit();
+                               return;
+                       }
+                       prev_esc = msec;
+                       break;
+#endif
+
+               case 'q':
+                       if(modkeys & KEY_MOD_CTRL) {
+                               app_quit();
+                               return;
+                       }
+                       break;
+
+               case '\n':
+               case '\r':
+                       if(modkeys & KEY_MOD_ALT) {
+               case KEY_F11:
+                               app_fullscreen(-1);
+                               return;
+                       }
+                       break;
+               }
+       }
+
+       if(cur_scr && cur_scr->keyboard) {
+               cur_scr->keyboard(key, press);
+       }
+}
+
+void app_mouse(int bn, int st, int x, int y)
+{
+       mouse_x = x;
+       mouse_y = y;
+       if(bn < 3) {
+               mouse_state[bn] = st;
+       }
+
+       if(cur_scr && cur_scr->mouse) {
+               cur_scr->mouse(bn, st, x, y);
+       }
+}
+
+void app_motion(int x, int y)
+{
+       if(cur_scr && cur_scr->motion) {
+               cur_scr->motion(x, y);
+       }
+       mouse_x = x;
+       mouse_y = y;
+}
+
+void app_chscr(struct app_screen *scr)
+{
+       struct app_screen *prev = cur_scr;
+
+       if(!scr) return;
+
+       if(scr->start && scr->start() == -1) {
+               return;
+       }
+       if(scr->reshape) {
+               scr->reshape(scr_width, scr_height);
+       }
+
+       if(prev && prev->stop) {
+               prev->stop();
+       }
+       cur_scr = scr;
+}
+
+void gui_fill(rtk_rect *rect, int color)
+{
+       int i, j;
+       unsigned char *fb;
+
+       if(rect->x < 0) {
+               rect->width += rect->x;
+               rect->x = 0;
+       }
+       if(rect->y < 0) {
+               rect->height += rect->y;
+               rect->y = 0;
+       }
+       if(rect->x + rect->width >= scr_width) {
+               rect->width = scr_width - rect->x;
+       }
+       if(rect->y + rect->height >= scr_height) {
+               rect->height = scr_height - rect->y;
+       }
+
+       fb = framebuf + rect->y * scr_width + rect->x;
+       for(i=0; i<rect->height; i++) {
+               for(j=0; j<rect->width; j++) {
+                       fb[j] = color;
+               }
+               fb += scr_width;
+       }
+}
+
+void gui_drawtext(int x, int y, const char *str)
+{
+}
+
+void gui_textrect(const char *str, rtk_rect *rect)
+{
+}
diff --git a/menu/src/app.h b/menu/src/app.h
new file mode 100644 (file)
index 0000000..b35de22
--- /dev/null
@@ -0,0 +1,89 @@
+#ifndef APP_H_
+#define APP_H_
+
+#include "sizeint.h"
+#include "logger.h"
+#include "rtk.h"
+
+enum {
+       KEY_BACKSP = 8,
+       KEY_ESC = 27,
+       KEY_DEL = 127,
+
+       KEY_NUM_0 = 256, KEY_NUM_1, KEY_NUM_2, KEY_NUM_3, KEY_NUM_4,
+       KEY_NUM_5, KEY_NUM_6, KEY_NUM_7, KEY_NUM_8, KEY_NUM_9,
+       KEY_NUM_DOT, KEY_NUM_DIV, KEY_NUM_MUL, KEY_NUM_MINUS, KEY_NUM_PLUS, KEY_NUM_ENTER, KEY_NUM_EQUALS,
+       KEY_UP, KEY_DOWN, KEY_RIGHT, KEY_LEFT,
+       KEY_INS, KEY_HOME, KEY_END, KEY_PGUP, KEY_PGDN,
+       KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6,
+       KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12,
+       KEY_F13, KEY_F14, KEY_F15,
+       KEY_NUMLK, KEY_CAPSLK, KEY_SCRLK,
+       KEY_RSHIFT, KEY_LSHIFT, KEY_RCTRL, KEY_LCTRL, KEY_RALT, KEY_LALT,
+       KEY_RMETA, KEY_LMETA, KEY_LSUPER, KEY_RSUPER, KEY_MODE, KEY_COMPOSE,
+       KEY_HELP, KEY_PRINT, KEY_SYSRQ, KEY_BREAK
+};
+
+#ifndef KEY_ANY
+#define KEY_ANY                (-1)
+#define KEY_ALT                (-2)
+#define KEY_CTRL       (-3)
+#define KEY_SHIFT      (-4)
+#endif
+
+enum {
+       KEY_MOD_SHIFT   = 1,
+       KEY_MOD_CTRL    = 4,
+       KEY_MOD_ALT     = 8
+};
+
+
+struct app_screen {
+       const char *name;
+
+       int (*init)(void);
+       void (*destroy)(void);
+       int (*start)(void);
+       void (*stop)(void);
+       void (*display)(void);
+       void (*reshape)(int, int);
+       void (*keyboard)(int, int);
+       void (*mouse)(int, int, int, int);
+       void (*motion)(int, int);
+};
+
+extern int mouse_x, mouse_y, mouse_state[3];
+extern unsigned int modkeys;
+extern int scr_width, scr_height;
+extern int fullscr;
+
+extern long time_msec;
+extern struct app_screen *cur_scr;
+extern struct app_screen menuscr;
+
+extern unsigned char *framebuf;
+
+
+int app_init(void);
+void app_shutdown(void);
+
+void app_display(void);
+void app_reshape(int x, int y);
+void app_keyboard(int key, int press);
+void app_mouse(int bn, int st, int x, int y);
+void app_motion(int x, int y);
+
+void app_chscr(struct app_screen *scr);
+
+void gui_fill(rtk_rect *rect, int color);
+void gui_drawtext(int x, int y, const char *str);
+void gui_textrect(const char *str, rtk_rect *rect);
+
+/* defined in main.c */
+void app_invalidate(int x, int y, int w, int h);
+void app_swap_buffers(void);
+void app_quit(void);
+void app_resize(int x, int y);
+void app_fullscreen(int fs);
+
+#endif /* APP_H_ */
diff --git a/menu/src/dos/cdpmi.c b/menu/src/dos/cdpmi.c
new file mode 100644 (file)
index 0000000..9ffb8c6
--- /dev/null
@@ -0,0 +1,103 @@
+#include <stdio.h>
+#include <i86.h>
+#include "cdpmi.h"
+#include "logger.h"
+
+#define LOWBUF_SIZE            8192
+#define RMSTACK_SIZE   4096
+
+static char *lowbuf, *lowfree;
+static uint16_t lowbuf_sel, lowbuf_seg;
+
+int dpmi_init(void)
+{
+       if(!(lowbuf_seg = dpmi_alloc(LOWBUF_SIZE >> 4, &lowbuf_sel))) {
+               errormsg("DPMI init failed to allocate low memory buffer\n");
+               return -1;
+       }
+       lowbuf = (char*)((intptr_t)lowbuf_seg << 4);
+       lowfree = lowbuf + RMSTACK_SIZE;
+       return 0;
+}
+
+void dpmi_cleanup(void)
+{
+       if(!lowbuf_sel) return;
+       dpmi_free(lowbuf_sel);
+       lowbuf = 0;
+       lowbuf_sel = 0;
+       lowbuf_seg = 0;
+}
+
+void *dpmi_lowbuf(void)
+{
+       return lowfree;
+}
+
+uint16_t dpmi_alloc(unsigned int par, uint16_t *sel)
+{
+       union REGS regs = {0};
+
+       regs.w.ax = 0x100;
+       regs.w.bx = par;
+       int386(0x31, &regs, &regs);
+       if(regs.w.cflag != 0) {
+               return 0;
+       }
+       *sel = regs.w.dx;
+       return regs.w.ax;
+}
+
+void dpmi_free(uint16_t sel)
+{
+       union REGS regs = {0};
+
+       regs.w.ax = 0x101;
+       regs.w.dx = sel;
+       int386(0x31, &regs, &regs);
+}
+
+int dpmi_rmint(int inum, struct dpmi_regs *dregs)
+{
+       union REGS regs = {0};
+       struct SREGS sregs = {0};
+
+       regs.x.eax = 0x300;
+       regs.x.ebx = inum;
+       sregs.es = FP_SEG(dregs);
+       regs.x.edi = FP_OFF(dregs);
+       sregs.ss = lowbuf_seg;  /* 4k real mode stack */
+       int386x(0x31, &regs, &regs, &sregs);
+       if(regs.x.cflag != 0) {
+               return -1;
+       }
+       return 0;
+}
+
+void *dpmi_mmap(uint32_t phys_addr, unsigned int size)
+{
+       union REGS regs = {0};
+
+       regs.w.ax = 0x800;
+       regs.w.bx = phys_addr >> 16;
+       regs.w.cx = phys_addr & 0xffff;
+       regs.w.si = size >> 16;
+       regs.w.di = size & 0xffff;
+       int386(0x31, &regs, &regs);
+       if(regs.w.cflag != 0) {
+               return 0;
+       }
+
+       return (void*)(((intptr_t)regs.w.bx << 16) | (intptr_t)regs.w.cx);
+}
+
+void dpmi_munmap(void *ptr)
+{
+       union REGS regs = {0};
+       intptr_t addr = (intptr_t)ptr;
+
+       regs.w.ax = 0x801;
+       regs.w.bx = addr >> 16;
+       regs.w.cx = addr & 0xffff;
+       int386(0x31, &regs, &regs);
+}
diff --git a/menu/src/dos/cdpmi.h b/menu/src/dos/cdpmi.h
new file mode 100644 (file)
index 0000000..751f2d2
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef CDPMI_H_
+#define CDPMI_H_
+
+#include <stdlib.h>
+#include "sizeint.h"
+
+#pragma pack (push, 1)
+struct dpmi_regs {
+       uint32_t edi, esi, ebp;
+       uint32_t reserved;
+       uint32_t ebx, edx, ecx, eax;
+       uint16_t flags;
+       uint16_t es, ds, fs, gs;
+       uint16_t ip, cs, sp, ss;
+};
+#pragma pack (pop)
+
+enum {
+       FLAGS_CF        = 0x000001,
+       FLAGS_PF        = 0x000004,
+       FLAGS_ZF        = 0x000040,
+       FLAGS_SF        = 0x000080,
+       FLAGS_IF        = 0x000020,
+       FLAGS_DF        = 0x000040,
+       FLAGS_VM        = 0x020000,
+       FLAGS_ID        = 0x200000,
+};
+
+int dpmi_init(void);
+void dpmi_cleanup(void);
+void *dpmi_lowbuf(void);
+
+uint16_t dpmi_alloc(unsigned int par, uint16_t *sel);
+void dpmi_free(uint16_t sel);
+int dpmi_rmint(int inum, struct dpmi_regs *regs);
+void *dpmi_mmap(uint32_t phys_addr, unsigned int size);
+void dpmi_munmap(void *addr);
+
+#endif /* CDPMI_H_ */
diff --git a/menu/src/dos/dosutil.h b/menu/src/dos/dosutil.h
new file mode 100644 (file)
index 0000000..4e4c8c3
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef DOSUTIL_H_
+#define DOSUTIL_H_
+
+#include <dos.h>
+#include <conio.h>
+
+#ifdef __DJGPP__
+#include <pc.h>
+
+#define outp(p, v)     outportb(p, v)
+#define outpw(p, v)    outportw(p, v)
+#define outpd(p, v)    outportl(p, v)
+
+#define inp(p)         inportb(p)
+#define inpw(p)                inportw(p)
+#define inpd(p)                inportl(p)
+#endif
+
+#endif /* DOSUTIL_H_ */
diff --git a/menu/src/dos/drv.h b/menu/src/dos/drv.h
new file mode 100644 (file)
index 0000000..172f146
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef DRV_H_
+#define DRV_H_
+
+#include "sizeint.h"
+
+struct vid_drvops {
+       int (*init)(void);
+       void (*cleanup)(void);
+
+       int (*setmode)(int mode);
+       int (*curmode)(void);
+};
+
+#define MAX_DRV        16
+extern struct vid_driver *vid_drvlist[MAX_DRV];
+extern int vid_numdrv;
+
+extern void *vid_vmem;
+extern int vid_vmem_size;
+
+void vid_register_vga(void);           /* drv_vga.c */
+void vid_register_vbe(void);           /* drv_vbe.c */
+void vid_register_s3(void);            /* drv_s3.c */
+
+#endif /* DRV_H_ */
diff --git a/menu/src/dos/drv_s3.c b/menu/src/dos/drv_s3.c
new file mode 100644 (file)
index 0000000..77919e7
--- /dev/null
@@ -0,0 +1,8 @@
+#include <conio.h>
+#include <i86.h>
+#include "drv.h"
+#include "logger.h"
+
+void vid_register_s3(void)
+{
+}
diff --git a/menu/src/dos/drv_vbe.c b/menu/src/dos/drv_vbe.c
new file mode 100644 (file)
index 0000000..ae36be8
--- /dev/null
@@ -0,0 +1,440 @@
+#include <string.h>
+#include <conio.h>
+#include <i86.h>
+#include "vidsys.h"
+#include "drv.h"
+#include "vbe.h"
+#include "vga.h"
+#include "cdpmi.h"
+#include "logger.h"
+
+#define farptr_to_linear(rmaddr) \
+       ((((intptr_t)(rmaddr) >> 12) & 0xffff0) + ((intptr_t)(rmaddr) & 0xffff))
+
+static int init(void);
+static void cleanup(void);
+static struct vid_modeinfo *find_mode(int mode);
+static int setmode(int mode);
+static int getmode(void);
+static const char *memsize_str(long sz);
+static int get_mode_info(int mode, struct vbe_mode_info *mi);
+static int conv_vbeinfo(int mode, struct vid_modeinfo *mi, struct vbe_mode_info *vbemi);
+static unsigned int calc_mask(int nbits, int pos);
+static void print_mode_info(int mode, struct vid_modeinfo *mi);
+
+static void pack(uint32_t *pix, int r, int g, int b);
+static void unpack(uint32_t pix, int *r, int *g, int *b);
+static void clear(uint32_t color);
+static void blit_lfb(int x, int y, int w, int h, void *fb, int pitch);
+static void blit_banked(int x, int y, int w, int h, void *fb, int pitch);
+static void blitfb_lfb(void *fb, int pitch);
+static void blitfb_banked(void *fb, int pitch);
+static void flip(int vsync);
+
+static struct vid_driver drv;
+static struct vid_drvops drvops = {init, cleanup, setmode, getmode};
+static unsigned int vbe_ver;
+
+static int cur_mode;
+static struct vid_modeinfo *cur_mi;
+static int cur_pgsize;
+
+
+void vid_register_vbe(void)
+{
+       drv.name = "vbe";
+       drv.prio = 2;
+       drv.ops = &drvops;
+
+       vid_drvlist[vid_numdrv++] = &drv;
+}
+
+
+static int init(void)
+{
+       struct dpmi_regs regs = {0};
+       struct vbe_info *vbe;
+       struct vbe_mode_info vbemi;
+       unsigned short bufseg;
+       uint16_t *vbe_modelist, *modelist;
+       int i, count;
+       struct vid_modeinfo modeinf;
+
+       cur_mode = -1;
+       cur_mi = 0;
+
+       vbe = dpmi_lowbuf();
+       bufseg = (intptr_t)vbe >> 4;
+
+       /* call VBE function 00 (get controller information) */
+       memcpy(vbe->sig, "VBE2", 4);    /* denote we want VBE 2.0 info */
+       regs.eax = 0x4f00;
+       regs.es = bufseg;
+       dpmi_rmint(0x10, &regs);
+       if((regs.eax & 0xffff) != 0x4f || memcmp(vbe->sig, "VESA", 4) != 0) {
+               errormsg("failed to get VBE controller information\n");
+               return -1;
+       }
+
+       vbe_ver = vbe->ver;
+
+       infomsg("Found VBE %d.%d\n", VBE_VER_MAJOR(vbe_ver), VBE_VER_MINOR(vbe_ver));
+       infomsg("OEM: %s\n", (char*)farptr_to_linear(vbe->oem_name));
+       if(vbe_ver >= 0x0200) {
+               infomsg("%s - %s (%s)\n", (char*)farptr_to_linear(vbe->vendor),
+                               (char*)farptr_to_linear(vbe->product),
+                               (char*)farptr_to_linear(vbe->revstr));
+       }
+       infomsg("Video RAM: %s\n", memsize_str((long)vbe->vmem_blk * 65536));
+
+       vbe_modelist = (uint16_t*)farptr_to_linear(vbe->modelist_addr);
+       count = 0;
+       for(i=0; i<1024; i++) {
+               if(vbe_modelist[i] == 0xffff) break;
+               count++;
+       }
+
+       if(!(modelist = malloc(count * sizeof *modelist))) {
+               errormsg("failed to allocate mode list\n");
+               return -1;
+       }
+       for(i=0; i<count; i++) {
+               modelist[i] = vbe_modelist[i];
+       }
+
+       if(!(drv.modes = malloc(count * sizeof *drv.modes))) {
+               errormsg("failed to allocate mode list\n");
+               free(modelist);
+               return -1;
+       }
+
+       drv.num_modes = 0;
+       for(i=0; i<count; i++) {
+               if(get_mode_info(modelist[i], &vbemi) == -1) {
+                       continue;
+               }
+               if(conv_vbeinfo(modelist[i], drv.modes + drv.num_modes, &vbemi) == -1) {
+                       continue;
+               }
+               drv.num_modes++;
+       }
+
+       free(modelist);
+       return 0;
+}
+
+static void cleanup(void)
+{
+       free(drv.modes);
+       drv.modes = 0;
+       drv.num_modes = 0;
+}
+
+static struct vid_modeinfo *find_mode(int mode)
+{
+       int i;
+       for(i=0; i<drv.num_modes; i++) {
+               if(drv.modes[i].modeno == mode) {
+                       return drv.modes + i;
+               }
+       }
+       return 0;
+}
+
+static int setmode(int mode)
+{
+       struct vid_modeinfo *minf;
+       struct dpmi_regs regs = {0};
+
+       if((minf = find_mode(mode)) && minf->lfb) {
+               mode |= VBE_MODE_LFB;
+       }
+
+retry:
+       regs.eax = 0x4f02;
+       regs.ebx = mode;
+       dpmi_rmint(0x10, &regs);
+
+       if((regs.eax & 0xffff) != 0x4f) {
+               if(mode & VBE_MODE_LFB) {
+                       mode &= ~VBE_MODE_LFB;
+                       goto retry;
+               }
+               return -1;
+       }
+       cur_mode = mode;
+
+       if(!(cur_mi = minf)) return 0;
+
+       cur_pgsize = minf->height * minf->pitch;
+
+       if(mode & VBE_MODE_LFB) {
+               minf->ops.blit = blit_lfb;
+               minf->ops.blitfb = blitfb_lfb;
+       } else {
+               minf->ops.blit = blit_banked;
+               minf->ops.blitfb = blitfb_banked;
+       }
+
+       print_mode_info(mode, minf);
+       return 0;
+}
+
+static int getmode(void)
+{
+       return cur_mode;
+}
+
+static const char *memsize_str(long sz)
+{
+       static const char *suffix[] = {"bytes", "kb", "mb", "gb", 0};
+       static int cnt = 0;
+       static char buf[64];
+
+       while(sz > 1024 && suffix[cnt + 1]) {
+               sz >>= 10;
+               cnt++;
+       }
+
+       sprintf(buf, "%ld %s", sz, suffix[cnt]);
+       return buf;
+}
+
+static int get_mode_info(int mode, struct vbe_mode_info *mi)
+{
+       struct dpmi_regs regs = {0};
+       struct vbe_mode_info *miptr;
+       uint16_t bufseg;
+
+       miptr = dpmi_lowbuf();
+       bufseg = (intptr_t)miptr >> 4;
+
+       regs.eax = 0x4f01;
+       regs.ecx = mode;
+       regs.es = bufseg;
+       dpmi_rmint(0x10, &regs);
+       if((regs.eax & 0xffff) != 0x4f) {
+               return -1;
+       }
+
+       *mi = *miptr;
+       return 0;
+}
+
+int vid_setwin(int wid, int pos)
+{
+       struct dpmi_regs regs = {0};
+
+       regs.eax = 0x4f05;
+       regs.ebx = wid;
+       regs.edx = pos;
+       dpmi_rmint(0x10, &regs);
+       if((regs.eax & 0xffff) != 0x4f) {
+               return -1;
+       }
+       return 0;
+}
+
+static int conv_vbeinfo(int mode, struct vid_modeinfo *mi, struct vbe_mode_info *vbemi)
+{
+       static int gran_shift;
+       static const struct { int width, height, bpp; } stdmode[] = {
+               {640, 400, 8},          /* 100h */
+               {640, 480, 8},          /* 101h */
+               {800, 600, 4}, {800, 600, 8},           /* 102h - 103h */
+               {1024, 768, 4}, {1024, 768, 8},         /* 104h - 105h */
+               {1280, 1024, 4}, {1280, 1024, 8},       /* 106h - 107h */
+               {80, 60, 4}, {132, 25, 4}, {132, 43, 4}, {132, 50, 4}, {132, 60, 4},
+               {320, 200, 15}, {320, 200, 16}, {320, 200, 24}, /* 10dh - 10fh */
+               {640, 480, 15}, {640, 480, 16}, {640, 480, 24}, /* 110h - 112h */
+               {800, 600, 15}, {800, 600, 16}, {800, 600, 24}, /* 113h - 115h */
+               {1024, 768, 15}, {1024, 768, 16}, {1024, 768, 24}, /* 116h - 118h */
+               {1280, 1024, 15}, {1280, 1024, 16}, {1280, 1024, 24} /* 119h - 11bh */
+       };
+
+       if(!(vbemi->attr & VBE_ATTR_AVAIL)) {
+               return -1;      /* ignore unsupported modes */
+       }
+       if(!(vbemi->attr & VBE_ATTR_GFX)) {
+               return -1;      /* ignore text modes */
+       }
+       if(vbemi->attr & VBE_ATTR_LFB) {
+               mi->lfb = 1;
+       }
+
+       mi->drv = &drv;
+       mi->modeno = mode;
+       mi->vmem_addr = 0xa0000;
+
+       if(vbe_ver >= 0x0102) {
+               mi->width = vbemi->xres;
+               mi->height = vbemi->yres;
+               mi->bpp = vbemi->bpp;
+               mi->rshift = vbemi->rpos;
+               mi->gshift = vbemi->gpos;
+               mi->bshift = vbemi->bpos;
+               mi->rmask = calc_mask(vbemi->rsize, vbemi->rpos);
+               mi->gmask = calc_mask(vbemi->gsize, vbemi->gpos);
+               mi->bmask = calc_mask(vbemi->bsize, vbemi->bpos);
+               mi->pages = vbemi->num_img_pages + 1;
+
+               if(vbe_ver >= 0x0200) {
+                       mi->vmem_addr = vbemi->fb_addr;
+                       mi->vmem_size = vbemi->scanline_bytes * mi->height * mi->pages;
+               }
+       } else {
+               if((mode & 0xff) > 7) {
+                       return -1;
+               }
+               mi->width = stdmode[mode & 0xff].width;
+               mi->height = stdmode[mode & 0xff].height;
+               mi->bpp = stdmode[mode & 0xff].bpp;
+       }
+       mi->ncolors = 1 << mi->bpp;
+       mi->pitch = vbemi->scanline_bytes;
+       mi->win_size = vbemi->win_size;
+       mi->win_gran = vbemi->win_gran;
+
+       gran_shift = 0;
+       mi->win_step = 1;
+       if(mi->win_gran > 0 && mi->win_gran < 64) {
+               int gran = mi->win_gran;
+               while(gran < 64) {
+                       gran_shift++;
+                       gran <<= 1;
+               }
+               mi->win_step = 1 << gran_shift;
+       }
+
+       mi->ops.pack = pack;
+       mi->ops.unpack = unpack;
+       mi->ops.setpal = vga_setpal;
+       mi->ops.getpal = vga_getpal;
+       mi->ops.vsync = vid_vsync;
+       mi->ops.clear = clear;
+       mi->ops.blit = 0;
+       mi->ops.blitfb = 0;
+       mi->ops.flip = flip;
+       return 0;
+}
+
+static unsigned int calc_mask(int nbits, int pos)
+{
+       int i;
+       unsigned int mask = 0;
+
+       for(i=0; i<nbits; i++) {
+               mask = (mask << 1) | 1;
+       }
+       return mask << pos;
+}
+
+static void print_mode_info(int mode, struct vid_modeinfo *mi)
+{
+       infomsg("VBE mode %04x\n", mode);
+       infomsg("  %dx%d %d bpp (%d colors)\n", mi->width, mi->height,
+                  mi->bpp, mi->ncolors);
+       infomsg("  pitch: %d bytes, %d vmem pages\n", mi->pitch, mi->pages);
+
+       if(mi->bpp > 8) {
+               infomsg("  RGB mask %06x %06x %06x (pos: %d %d %d)\n", (unsigned int)mi->rmask,
+                               (unsigned int)mi->gmask, (unsigned int)mi->bmask, mi->rshift,
+                               mi->gshift, mi->bshift);
+       }
+
+       if(mode & VBE_MODE_LFB) {
+               infomsg("  LFB address %xh, size: %d\n", (unsigned int)mi->vmem_addr,
+                               (int)mi->vmem_size);
+       } else {
+               infomsg("  banked window %d kb, granularity: %d kb, step: %d\n", mi->win_size,
+                               mi->win_gran, mi->win_step);
+       }
+}
+
+
+static void pack(uint32_t *pix, int r, int g, int b)
+{
+       *pix = (((uint32_t)r << cur_mi->rshift) & cur_mi->rmask) |
+               (((uint32_t)g << cur_mi->gshift) & cur_mi->gmask) |
+               (((uint32_t)b << cur_mi->bshift) & cur_mi->bmask);
+}
+
+static void unpack(uint32_t pix, int *r, int *g, int *b)
+{
+       *r = (pix & cur_mi->rmask) >> cur_mi->rshift;
+       *g = (pix & cur_mi->gmask) >> cur_mi->gshift;
+       *b = (pix & cur_mi->bmask) >> cur_mi->bshift;
+}
+
+static void clear(uint32_t color)
+{
+}
+
+static void blit_lfb(int x, int y, int w, int h, void *fb, int pitch)
+{
+       int i, pixsz, spansz;
+       unsigned char *dest, *src;
+
+       /*dbgmsg("blit: %d,%d (%dx%d)\n", x, y, w, h);*/
+
+       pixsz = (cur_mi->bpp + 7) >> 3;
+       spansz = w * pixsz;
+
+       dest = (char*)vid_vmem + cur_mi->pitch * y + x * pixsz;
+       src = fb;
+
+       for(i=0; i<h; i++) {
+               memcpy(dest, src, spansz);
+               dest += cur_mi->pitch;
+               src += pitch;
+       }
+}
+
+static void blit_banked(int x, int y, int w, int h, void *fb, int pitch)
+{
+       abort();
+}
+
+static void blitfb_lfb(void *fb, int pitch)
+{
+       int i, pixsz, spansz;
+       unsigned char *dest, *src;
+
+       pixsz = (cur_mi->bpp + 7) >> 3;
+       spansz = cur_mi->width * pixsz;
+
+       dest = vid_vmem;
+       src = fb;
+
+       for(i=0; i<cur_mi->height; i++) {
+               memcpy(dest, src, spansz);
+               dest += cur_mi->pitch;
+               src += pitch;
+       }
+}
+
+static void blitfb_banked(void *fb, int pitch)
+{
+       int sz, offs, pending, winsz;
+       unsigned char *pptr = fb;
+
+       winsz = cur_mi->win_size << 10;
+
+       /* assume initial window offset at 0 */
+       offs = 0;
+       pending = cur_pgsize;
+       while(pending > 0) {
+               sz = pending > winsz ? winsz : pending;
+               memcpy((void*)0xa0000, pptr, sz);
+               pptr += sz;
+               pending -= sz;
+               offs += cur_mi->win_step;
+               vid_setwin(0, offs);
+       }
+       vid_setwin(0, 0);
+}
+
+static void flip(int vsync)
+{
+       /* TODO */
+}
diff --git a/menu/src/dos/drv_vga.c b/menu/src/dos/drv_vga.c
new file mode 100644 (file)
index 0000000..ed4d237
--- /dev/null
@@ -0,0 +1,173 @@
+#include <string.h>
+#include <conio.h>
+#include <i86.h>
+#include "vidsys.h"
+#include "drv.h"
+#include "vga.h"
+#include "logger.h"
+
+static int init(void);
+static void cleanup(void);
+static int setmode(int mode);
+static int curmode(void);
+
+static void setpal4(int idx, int count, const struct vid_color *col);
+static void getpal4(int idx, int count, struct vid_color *col);
+static void clear4(uint32_t color);
+static void blitfb4(void *fb, int pitch);
+static void fill4(int x, int y, int w, int h, uint32_t color);
+
+static void clear8(uint32_t color);
+static void blitfb8(void *fb, int pitch);
+static void fill8(int x, int y, int w, int h, uint32_t color);
+
+
+static struct vid_driver drv;
+static struct vid_drvops drvops = {init, cleanup, setmode, curmode};
+static struct vid_modeinfo modes[] = {
+       {3, 80, 25, 4},
+       {0x12, 640, 480, 4},
+       {0x13, 320, 200, 8}
+};
+
+static struct vid_gfxops gfxops_mode12h = {
+       0, 0, setpal4, getpal4, vid_vsync, clear4, blitfb4, 0, fill4 };
+static struct vid_gfxops gfxops_mode13h = {
+       0, 0, vga_setpal, vga_getpal, vid_vsync, clear8, blitfb8, 0, fill8 };
+
+void vid_register_vga(void)
+{
+       int i;
+
+       drv.name = "vga";
+       drv.prio = 1;
+       drv.ops = &drvops;
+       drv.modes = modes;
+       drv.num_modes = sizeof modes / sizeof *modes;
+
+       for(i=0; i<drv.num_modes; i++) {
+               modes[i].drv = &drv;
+               modes[i].vmem_addr = 0xa0000;
+
+               switch(modes[i].modeno) {
+               case 0x3:
+                       modes[i].vmem_addr = 0xb8000;
+                       break;
+                               
+               case 0x13:
+                       modes[i].ops = gfxops_mode13h;
+                       break;
+
+               case 0x12:
+                       modes[i].ops = gfxops_mode12h;
+                       break;
+               }
+       }
+
+       vid_drvlist[vid_numdrv++] = &drv;
+}
+
+void vid_vsync(void)
+{
+       while(inp(VGA_STAT1_PORT) & 8);
+       while((inp(VGA_STAT1_PORT) & 8) == 0);
+}
+
+void vga_setpal(int idx, int count, const struct vid_color *col)
+{
+       int i;
+       outp(VGA_DAC_WADDR_PORT, idx);
+       for(i=0; i<count; i++) {
+               outp(VGA_DAC_DATA_PORT, col->r >> 2);
+               outp(VGA_DAC_DATA_PORT, col->g >> 2);
+               outp(VGA_DAC_DATA_PORT, col->b >> 2);
+               col++;
+       }
+}
+
+void vga_getpal(int idx, int count, struct vid_color *col)
+{
+       int i;
+       outp(VGA_DAC_RADDR_PORT, idx);
+       for(i=0; i<count; i++) {
+               col->r = inp(VGA_DAC_DATA_PORT) << 2;
+               col->g = inp(VGA_DAC_DATA_PORT) << 2;
+               col->b = inp(VGA_DAC_DATA_PORT) << 2;
+               col++;
+       }
+}
+
+
+static int init(void)
+{
+       return 0;
+}
+
+static void cleanup(void)
+{
+}
+
+static int setmode(int mode)
+{
+       union REGS regs = {0};
+       regs.w.ax = mode;
+       int386(0x10, &regs, &regs);
+       return 0;
+}
+
+static int curmode(void)
+{
+       union REGS regs = {0};
+       regs.w.ax = 0xf00;
+       int386(0x10, &regs, &regs);
+       return regs.x.eax & 0xff;
+}
+
+static void setpal4(int idx, int count, const struct vid_color *col)
+{
+}
+
+static void getpal4(int idx, int count, struct vid_color *col)
+{
+}
+
+static void clear4(uint32_t color)
+{
+}
+
+static void blitfb4(void *fb, int pitch)
+{
+}
+
+static void fill4(int x, int y, int w, int h, uint32_t color)
+{
+}
+
+static void clear8(uint32_t color)
+{
+       memset((void*)0xa0000, color, 64000);
+}
+
+static void blitfb8(void *fb, int pitch)
+{
+       int i;
+       unsigned char *src = fb;
+       unsigned char *dest = (unsigned char*)0xa0000;
+       for(i=0; i<200; i++) {
+               memcpy(dest, src, 320);
+               dest += 320;
+               src += pitch;
+       }
+}
+
+static void fill8(int x, int y, int w, int h, uint32_t color)
+{
+       int i;
+       unsigned char *fbptr = (unsigned char*)0xa0000;
+
+       fbptr += y * 320 + x;
+       for(i=0; i<h; i++) {
+               memset(fbptr, color, w);
+               fbptr += 320;
+       }
+}
diff --git a/menu/src/dos/keyb.c b/menu/src/dos/keyb.c
new file mode 100644 (file)
index 0000000..8789ea7
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+DOS interrupt-based keyboard driver.
+Copyright (C) 2013-2023  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License  for more details.
+
+You should have received a copy of the GNU General Public License
+along with the program. If not, see <http://www.gnu.org/licenses/>
+*/
+#define KEYB_C_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <conio.h>
+#include <dos.h>
+
+#ifdef __WATCOMC__
+#include <i86.h>
+#endif
+#ifdef __DJGPP__
+#include <dpmi.h>
+#include <go32.h>
+#include <pc.h>
+#endif
+
+#include "keyb.h"
+#include "scancode.h"
+#include "sizeint.h"
+#include "dosutil.h"
+
+#define KEY_INTR               0x9
+#define KEY_PORT               0x60
+
+#define PIC1_CMD_PORT  0x20
+#define OCW2_EOI               (1 << 5)
+
+#ifdef __WATCOMC__
+#define INTERRUPT __interrupt __far
+
+#define DONE_INIT      (prev_handler)
+static void (INTERRUPT *prev_handler)();
+#endif
+
+#ifdef __DJGPP__
+#define INTERRUPT
+
+#define DONE_INIT prev_intr.pm_offset
+static _go32_dpmi_seginfo intr, prev_intr;
+#endif
+
+static void INTERRUPT kbintr();
+
+#define BUFSIZE                64
+static int buffer[BUFSIZE];
+static int buf_ridx, buf_widx;
+static int last_key;
+
+static unsigned int num_pressed;
+static unsigned char keystate[512];
+
+#define ADVANCE(x)     ((x) = ((x) + 1) & (BUFSIZE - 1))
+
+void kb_init(void)
+{
+       if(DONE_INIT) {
+               errormsg("keyboard driver already initialized!\n");
+               return;
+       }
+
+       buf_ridx = buf_widx = 0;
+       last_key = -1;
+
+       memset(keystate, 0, sizeof keystate);
+       num_pressed = 0;
+
+       /* set our interrupt handler */
+       _disable();
+#ifdef __WATCOMC__
+       prev_handler = _dos_getvect(KEY_INTR);
+       _dos_setvect(KEY_INTR, kbintr);
+#endif
+#ifdef __DJGPP__
+       _go32_dpmi_get_protected_mode_interrupt_vector(KEY_INTR, &prev_intr);
+       intr.pm_offset = (intptr_t)kbintr;
+       intr.pm_selector = _go32_my_cs();
+       _go32_dpmi_allocate_iret_wrapper(&intr);
+       _go32_dpmi_set_protected_mode_interrupt_vector(KEY_INTR, &intr);
+#endif
+       _enable();
+}
+
+void kb_shutdown(void)
+{
+       if(!DONE_INIT) {
+               return;
+       }
+
+       /* restore the original interrupt handler */
+       _disable();
+#ifdef __WATCOMC__
+       _dos_setvect(KEY_INTR, prev_handler);
+#endif
+#ifdef __DJGPP__
+       _go32_dpmi_set_protected_mode_interrupt_vector(KEY_INTR, &prev_intr);
+       _go32_dpmi_free_iret_wrapper(&intr);
+#endif
+       _enable();
+}
+
+int kb_isdown(int key)
+{
+       switch(key) {
+       case KEY_ANY:
+               return num_pressed;
+
+       case KEY_ALT:
+               return keystate[KEY_LALT] + keystate[KEY_RALT];
+
+       case KEY_CTRL:
+               return keystate[KEY_LCTRL] + keystate[KEY_RCTRL];
+
+       case KEY_SHIFT:
+               return keystate[KEY_LSHIFT] + keystate[KEY_RSHIFT];
+       }
+
+       if(isalpha(key)) {
+               key = tolower(key);
+       }
+       return keystate[key];
+}
+
+#ifdef __WATCOMC__
+void halt(void);
+#pragma aux halt = \
+       "sti" \
+       "hlt";
+#endif
+
+#ifdef __DJGPP__
+#define halt() asm volatile("sti\n\thlt\n\t")
+#endif
+
+void kb_wait(void)
+{
+       int key;
+       while((key = kb_getkey()) == -1) {
+#ifdef USE_HLT
+               /* put the processor to sleep while waiting for keypresses, but first
+                * make sure interrupts are enabled, or we'll sleep forever
+                */
+               halt();
+#endif
+       }
+       kb_putback(key);
+}
+
+int kb_getkey(void)
+{
+       int res;
+
+       if(buffer) {
+               if(buf_ridx == buf_widx) {
+                       return -1;
+               }
+               res = buffer[buf_ridx];
+               ADVANCE(buf_ridx);
+       } else {
+               res = last_key;
+               last_key = -1;
+       }
+       return res;
+}
+
+void kb_putback(int key)
+{
+       if(buffer) {
+               /* go back a place */
+               if(--buf_ridx < 0) {
+                       buf_ridx += BUFSIZE;
+               }
+
+               /* if the write end hasn't caught up with us, go back one place
+                * and put it there, otherwise just overwrite the oldest key which
+                * is right where we were.
+                */
+               if(buf_ridx == buf_widx) {
+                       ADVANCE(buf_ridx);
+               }
+
+               buffer[buf_ridx] = key;
+       } else {
+               last_key = key;
+       }
+}
+
+static void INTERRUPT kbintr()
+{
+       unsigned char code;
+       int key, c, press;
+       static int ext;
+
+       code = inp(KEY_PORT);
+
+       if(code == 0xe0) {
+               ext = 1;
+               goto eoi;
+       }
+
+       if(code & 0x80) {
+               press = 0;
+               code &= 0x7f;
+
+               if(num_pressed > 0) {
+                       num_pressed--;
+               }
+       } else {
+               press = 1;
+
+               num_pressed++;
+       }
+
+       if(ext) {
+               key = scantbl_ext[code];
+               c = key;
+               ext = 0;
+       } else {
+               key = scantbl[code];
+               c = (keystate[KEY_LSHIFT] | keystate[KEY_RSHIFT]) ? scantbl_shift[code] : key;
+       }
+
+       if(press && !keystate[key]) {
+               /* append to buffer */
+               last_key = c;
+               buffer[buf_widx] = c;
+               ADVANCE(buf_widx);
+               /* if the write end overtook the read end, advance the read end
+                * too, to discard the oldest keypress from the buffer
+                */
+               if(buf_widx == buf_ridx) {
+                       ADVANCE(buf_ridx);
+               }
+       }
+
+       /* and update keystate table */
+       keystate[key] = press;
+
+eoi:
+       outp(PIC1_CMD_PORT, OCW2_EOI);  /* send end-of-interrupt */
+}
diff --git a/menu/src/dos/keyb.h b/menu/src/dos/keyb.h
new file mode 100644 (file)
index 0000000..46b6aa4
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+DOS interrupt-based keyboard driver.
+Copyright (C) 2013-2023  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License  for more details.
+
+You should have received a copy of the GNU General Public License
+along with the program. If not, see <http://www.gnu.org/licenses/>
+*/
+#ifndef KEYB_H_
+#define KEYB_H_
+
+#include "app.h"
+
+#define KB_ANY         (-1)
+#define KB_ALT         (-2)
+#define KB_CTRL                (-3)
+#define KB_SHIFT       (-4)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void kb_init(void);
+void kb_shutdown(void); /* don't forget to call this at the end! */
+
+/* Boolean predicate for testing the current state of a particular key.
+ * You may also pass KB_ANY to test if any key is held down.
+ */
+int kb_isdown(int key);
+
+/* waits for any keypress */
+void kb_wait(void);
+
+/* removes and returns a single key from the input buffer.
+ * If buffering is disabled (initialized with kb_init(0)), then it always
+ * returns the last key pressed.
+ */
+int kb_getkey(void);
+
+void kb_putback(int key);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* KEYB_H_ */
diff --git a/menu/src/dos/main.c b/menu/src/dos/main.c
new file mode 100644 (file)
index 0000000..a8e9c1b
--- /dev/null
@@ -0,0 +1,185 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include "app.h"
+#include "timer.h"
+#include "keyb.h"
+#include "vidsys.h"
+#include "cdpmi.h"
+#include "mouse.h"
+#include "logger.h"
+#include "rtk.h"
+
+static unsigned char *vmem;
+static int quit, dirty_valid;
+static rtk_rect dirty;
+static int mx, my, prev_mx, prev_my, use_mouse;
+
+
+int main(int argc, char **argv)
+{
+       int i;
+       int vmidx;
+       int mdx, mdy, bnstate, bndiff;
+       static int prev_bnstate;
+       char *env;
+
+#ifdef __DJGPP__
+       __djgpp_nearptr_enable();
+#endif
+
+       init_logger();
+       if((env = getenv("MENULOG"))) {
+               if(tolower(env[0]) == 'c' && tolower(env[1]) == 'o' && tolower(env[2]) == 'm'
+                               && isdigit(env[3])) {
+                       add_log_console(env);
+               } else {
+                       add_log_file(env);
+               }
+       }
+
+       if(!(use_mouse = have_mouse())) {
+               infomsg("no mouse detected\n");
+       }
+       init_timer(0);
+       kb_init();
+
+       if(vid_init() == -1) {
+               return 1;
+       }
+
+       scr_width = 640;
+       scr_height = 480;
+       if((vmidx = vid_findmode(scr_width, scr_height, 8)) == -1) {
+               return 1;
+       }
+       if(!(vmem = vid_setmode(vmidx))) {
+               return 1;
+       }
+
+       if(app_init() == -1) {
+               goto break_evloop;
+       }
+       app_invalidate(0, 0, 0, 0);
+
+       app_reshape(scr_width, scr_height);
+       mx = scr_width / 2;
+       my = scr_height / 2;
+       prev_mx = prev_my = -1;
+
+       for(;;) {
+               int key;
+
+               modkeys = 0;
+               if(kb_isdown(KEY_ALT)) {
+                       modkeys |= KEY_MOD_ALT;
+               }
+               if(kb_isdown(KEY_CTRL)) {
+                       modkeys |= KEY_MOD_CTRL;
+               }
+               if(kb_isdown(KEY_SHIFT)) {
+                       modkeys |= KEY_MOD_SHIFT;
+               }
+
+               while((key = kb_getkey()) != -1) {
+                       if(key == 'r' && (modkeys & KEY_MOD_CTRL)) {
+                               app_invalidate(0, 0, 0, 0);
+                       } else {
+                               app_keyboard(key, 1);
+                       }
+                       if(quit) goto break_evloop;
+               }
+
+               if(use_mouse) {
+                       bnstate = read_mouse_bn();
+                       bndiff = bnstate ^ prev_bnstate;
+                       prev_bnstate = bnstate;
+
+                       read_mouse_rel(&mdx, &mdy);
+                       mx += mdx;
+                       if(mx < 0) mx = 0;
+                       if(mx >= scr_width) mx = scr_width - 1;
+                       my += mdy;
+                       if(my < 0) my = 0;
+                       if(my >= scr_height) my = scr_height - 1;
+                       mdx = mx - prev_mx;
+                       mdy = my - prev_my;
+
+                       if(bndiff & 1) app_mouse(0, bnstate & 1, mx, my);
+                       if(bndiff & 2) app_mouse(1, bnstate & 2, mx, my);
+                       if(bndiff & 4) app_mouse(3, bnstate & 4, mx, my);
+
+                       if((mdx | mdy) != 0) {
+                               app_motion(mx, my);
+                       }
+               }
+
+               app_display();
+               app_swap_buffers();
+       }
+
+break_evloop:
+       app_shutdown();
+       vid_cleanup();
+       kb_shutdown();
+       return 0;
+}
+
+void app_invalidate(int x, int y, int w, int h)
+{
+       rtk_rect r;
+
+       if((w | h) == 0) {
+               r.x = r.y = 0;
+               r.width = scr_width;
+               r.height = scr_height;
+       } else {
+               r.x = x;
+               r.y = y;
+               r.width = w;
+               r.height = h;
+       }
+
+       if(dirty_valid) {
+               rtk_rect_union(&dirty, &r);
+       } else {
+               dirty = r;
+       }
+       dirty_valid = 1;
+}
+
+void app_swap_buffers(void)
+{
+       unsigned char *src;
+
+       vid_vsync();
+
+       if(dirty_valid) {
+               if(dirty.width < scr_width || dirty.height < scr_height) {
+                       src = framebuf + dirty.y * scr_width + dirty.x;
+                       vid_blit8(dirty.x, dirty.y, dirty.width, dirty.height, src, 0);
+               } else {
+                       vid_blitfb8(framebuf, 0);
+               }
+               dirty_valid = 0;
+       }
+}
+
+void app_quit(void)
+{
+       quit = 1;
+}
+
+void app_resize(int x, int y)
+{
+}
+
+void app_fullscreen(int fs)
+{
+}
+
+void app_vsync(int vsync)
+{
+}
diff --git a/menu/src/dos/mouse.asm b/menu/src/dos/mouse.asm
new file mode 100644 (file)
index 0000000..458c68f
--- /dev/null
@@ -0,0 +1,244 @@
+; vi:set filetype=nasm:
+; foo_ are watcom functions, _foo are djgpp functions
+
+QUERY equ 0
+SHOW equ 1
+HIDE equ 2
+READ equ 3
+WRITE equ 4
+PIXRATE equ 15
+XLIM equ 7
+YLIM equ 8
+READREL equ 0xb
+
+PUSHA_EAX_OFFS equ 28
+PUSHA_ECX_OFFS equ 24
+PUSHA_EDX_OFFS equ 20
+
+       section .text
+       bits 32
+
+; int have_mouse(void)
+       global have_mouse_
+       global _have_mouse
+have_mouse_:
+_have_mouse:
+       pusha
+       mov ax, QUERY
+       int 0x33
+       and eax, 0xffff
+       mov [esp + PUSHA_EAX_OFFS], eax
+       popa
+       ret
+
+; void show_mouse(int show)
+       global show_mouse_
+show_mouse_:
+       pusha
+       test ax, ax
+       mov ax, HIDE
+       jz .skip
+       mov ax, SHOW
+.skip: int 0x33
+       popa
+       ret
+
+       global _show_mouse
+_show_mouse:
+       push ebp
+       mov ebp, esp
+       push ebx
+       push esi
+       push edi
+       mov ax, [ebp + 8]
+       test ax, ax
+       mov ax, HIDE
+       jz .skip
+       mov ax, SHOW
+.skip: int 0x33
+       pop edi
+       pop esi
+       pop ebx
+       pop ebp
+       ret
+
+; int read_mouse_bn(void)
+       global read_mouse_bn_
+       global _read_mouse_bn
+read_mouse_bn_:
+_read_mouse_bn:
+       pusha
+       mov ax, READ
+       int 0x33
+       xor eax, eax
+       mov ax, bx
+       mov [esp + PUSHA_EAX_OFFS], eax
+       popa
+       ret
+
+; int read_mouse(int *xp, int *yp)
+       global read_mouse_
+read_mouse_:
+       pusha
+       mov esi, eax    ; xp
+       mov edi, edx    ; yp
+       mov ax, READ
+       int 0x33
+       xor eax, eax
+       and ecx, 0xffff
+       and edx, 0xffff
+       mov ax, bx
+       mov [esp + PUSHA_EAX_OFFS], eax
+       mov [esi], ecx
+       mov [edi], edx
+       popa
+       ret
+
+       global _read_mouse
+_read_mouse:
+       push ebp
+       mov ebp, esp
+       push ebx
+       push esi
+       push edi
+       mov ax, READ
+       int 0x33
+       xor eax, eax
+       mov ax, bx
+       and ecx, 0xffff
+       mov ebx, [ebp + 8]
+       mov [ebx], ecx
+       and edx, 0xffff
+       mov ebx, [ebp + 12]
+       mov [ebx], edx
+       pop edi
+       pop esi
+       pop ebx
+       pop ebp
+       ret
+
+; void read_mouse_rel(int *xp, int *yp)
+       global read_mouse_rel_
+read_mouse_rel_:
+       pusha
+       mov esi, eax    ; xp
+       mov edi, edx    ; yp
+       mov ax, READREL
+       int 0x33
+       movsx eax, cx
+       mov [esi], eax
+       movsx eax, dx
+       mov [edi], eax
+       popa
+       ret
+
+       global _read_mouse_rel
+_read_mouse_rel:
+       push ebp
+       mov ebp, esp
+       push ebx
+       push esi
+       push edi
+       mov ax, READREL
+       int 0x33
+       mov ebx, [ebp + 8]
+       movsx eax, cx
+       mov [ebx], eax
+       mov ebx, [ebp + 12]
+       movsx eax, dx
+       mov [ebx], eax
+       pop edi
+       pop esi
+       pop ebx
+       pop ebp
+       ret
+
+; void set_mouse(int x, int y)
+       global set_mouse_
+set_mouse_:
+       pusha
+       mov cx, ax
+       mov ax, WRITE
+       int 0x33
+       popa
+       ret
+       
+       global _set_mouse
+_set_mouse:
+       push ebp
+       mov ebp, esp
+       push ebx
+       push esi
+       push edi
+       mov ax, WRITE
+       mov cx, [ebp + 8]
+       mov dx, [ebp + 12]
+       int 0x33
+       pop edi
+       pop esi
+       pop ebx
+       pop ebp
+       ret
+
+; void set_mouse_limits(int xmin[eax], int ymin[edx], int xmax[ebx], int ymax[ecx])
+       global set_mouse_limits_
+set_mouse_limits_:
+       pusha
+       mov cx, ax
+       mov dx, bx
+       mov ax, XLIM
+       int 0x33
+       mov ax, YLIM
+       mov cx, [esp + PUSHA_EDX_OFFS]
+       mov dx, [esp + PUSHA_ECX_OFFS]
+       int 0x33
+       popa
+       ret
+
+       global _set_mouse_limits
+_set_mouse_limits:
+       push ebp
+       mov ebp, esp
+       push ebx
+       push esi
+       push edi
+       mov ax, XLIM
+       mov cx, [ebp + 8]
+       mov dx, [ebp + 16]
+       int 0x33
+       mov ax, YLIM
+       mov cx, [ebp + 12]
+       mov dx, [ebp + 20]
+       int 0x33
+       pop edi
+       pop esi
+       pop ebx
+       pop ebp
+       ret
+
+; void set_mouse_rate(int xrate, int yrate)
+       global set_mouse_rate_
+set_mouse_rate_:
+       pusha
+       mov cx, ax
+       mov ax, PIXRATE
+       int 0x33
+       popa
+       ret
+
+       global _set_mouse_rate
+_set_mouse_rate:
+       push ebp
+       mov ebp, esp
+       push ebx
+       push esi
+       push edi
+       mov ax, PIXRATE
+       mov cx, [esp + 4]
+       mov dx, [esp + 8]
+       int 0x33
+       pop edi
+       pop esi
+       pop ebx
+       pop ebp
+       ret
diff --git a/menu/src/dos/mouse.h b/menu/src/dos/mouse.h
new file mode 100644 (file)
index 0000000..aa46df3
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef MOUSE_H_
+#define MOUSE_H_
+
+enum {
+       MOUSE_LEFT              = 1,
+       MOUSE_RIGHT             = 2,
+       MOUSE_MIDDLE    = 4
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int have_mouse(void);
+void show_mouse(int show);
+int read_mouse_bn(void);
+int read_mouse(int *xp, int *yp);
+void read_mouse_rel(int *xp, int *yp);
+void set_mouse(int x, int y);
+void set_mouse_limits(int xmin, int ymin, int xmax, int ymax);
+void set_mouse_rate(int xrate, int yrate);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MOUSE_H_ */
diff --git a/menu/src/dos/pit8254.h b/menu/src/dos/pit8254.h
new file mode 100644 (file)
index 0000000..5d45f55
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef PIT8254_H_
+#define PIT8254_H_
+
+/* frequency of the oscillator driving the 8254 timer */
+#define OSC_FREQ_HZ            1193182
+
+/* I/O ports connected to the 8254 */
+#define PORT_DATA0     0x40
+#define PORT_DATA1     0x41
+#define PORT_DATA2     0x42
+#define PORT_CMD       0x43
+
+/* command bits */
+#define CMD_CHAN0                      0
+#define CMD_CHAN1                      (1 << 6)
+#define CMD_CHAN2                      (2 << 6)
+#define CMD_RDBACK                     (3 << 6)
+
+#define CMD_LATCH                      0
+#define CMD_ACCESS_LOW         (1 << 4)
+#define CMD_ACCESS_HIGH                (2 << 4)
+#define CMD_ACCESS_BOTH                (3 << 4)
+
+#define CMD_OP_INT_TERM                0
+#define CMD_OP_ONESHOT         (1 << 1)
+#define CMD_OP_RATE                    (2 << 1)
+#define CMD_OP_SQWAVE          (3 << 1)
+#define CMD_OP_SW_STROBE       (4 << 1)
+#define CMD_OP_HW_STROBE       (5 << 1)
+
+#define CMD_MODE_BIN           0
+#define CMD_MODE_BCD           1
+
+#endif /* PIT8254_H_ */
diff --git a/menu/src/dos/scancode.h b/menu/src/dos/scancode.h
new file mode 100644 (file)
index 0000000..39f3033
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+colcycle - color cycling image viewer
+Copyright (C) 2016  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef KEYB_C_
+#error "do not include scancode.h anywhere..."
+#endif
+
+/* table with rough translations from set 1 scancodes to ASCII-ish */
+static int scantbl[] = {
+       0, KEY_ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',           /* 0 - e */
+       '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',                 /* f - 1c */
+       KEY_LCTRL, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`',                         /* 1d - 29 */
+       KEY_LSHIFT, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', KEY_RSHIFT,                 /* 2a - 36 */
+       KEY_NUM_MUL, KEY_LALT, ' ', KEY_CAPSLK, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10,                        /* 37 - 44 */
+       KEY_NUMLK, KEY_SCRLK, KEY_NUM_7, KEY_NUM_8, KEY_NUM_9, KEY_NUM_MINUS, KEY_NUM_4, KEY_NUM_5, KEY_NUM_6, KEY_NUM_PLUS,    /* 45 - 4e */
+       KEY_NUM_1, KEY_NUM_2, KEY_NUM_3, KEY_NUM_0, KEY_NUM_DOT, KEY_SYSRQ, 0, 0, KEY_F11, KEY_F12,                                             /* 4d - 58 */
+       0, 0, 0, 0, 0, 0, 0,                                                                                                                    /* 59 - 5f */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                                                                 /* 60 - 6f */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0                                                                  /* 70 - 7f */
+};
+
+static int scantbl_shift[] = {
+       0, KEY_ESC, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b',           /* 0 - e */
+       '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n',                 /* f - 1c */
+       KEY_LCTRL, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~',                          /* 1d - 29 */
+       KEY_LSHIFT, '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', KEY_RSHIFT,                  /* 2a - 36 */
+       KEY_NUM_MUL, KEY_LALT, ' ', KEY_CAPSLK, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10,                        /* 37 - 44 */
+       KEY_NUMLK, KEY_SCRLK, KEY_NUM_7, KEY_NUM_8, KEY_NUM_9, KEY_NUM_MINUS, KEY_NUM_4, KEY_NUM_5, KEY_NUM_6, KEY_NUM_PLUS,    /* 45 - 4e */
+       KEY_NUM_1, KEY_NUM_2, KEY_NUM_3, KEY_NUM_0, KEY_NUM_DOT, KEY_SYSRQ, 0, 0, KEY_F11, KEY_F12,                                             /* 4d - 58 */
+       0, 0, 0, 0, 0, 0, 0,                                                                                                                    /* 59 - 5f */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                                                                 /* 60 - 6f */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0                                                                  /* 70 - 7f */
+};
+
+
+/* extended scancodes, after the 0xe0 prefix */
+static int scantbl_ext[] = {
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                 /* 0 - f */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\r', KEY_RCTRL, 0, 0,                      /* 10 - 1f */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                 /* 20 - 2f */
+       0, 0, 0, 0, 0, KEY_NUM_MINUS, 0, KEY_SYSRQ, KEY_RALT, 0, 0, 0, 0, 0, 0, 0,                      /* 30 - 3f */
+       0, 0, 0, 0, 0, 0, 0, KEY_HOME, KEY_UP, KEY_PGUP, 0, KEY_LEFT, 0, KEY_RIGHT, 0, KEY_END, /* 40 - 4f */
+       KEY_DOWN, KEY_PGDN, KEY_INS, KEY_DEL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                       /* 50 - 5f */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                 /* 60 - 6f */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                 /* 70 - 7f */
+};
+
diff --git a/menu/src/dos/timer.c b/menu/src/dos/timer.c
new file mode 100644 (file)
index 0000000..b03b115
--- /dev/null
@@ -0,0 +1,174 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <conio.h>
+#include <dos.h>
+
+#ifdef __WATCOMC__
+#include <i86.h>
+#endif
+
+#ifdef __DJGPP__
+#include <dpmi.h>
+#include <go32.h>
+#include <pc.h>
+#endif
+
+#include "pit8254.h"
+#include "sizeint.h"
+#include "dosutil.h"
+
+#define PIT_TIMER_INTR 8
+#define DOS_TIMER_INTR 0x1c
+
+/* macro to divide and round to the nearest integer */
+#define DIV_ROUND(a, b) \
+       ((a) / (b) + ((a) % (b)) / ((b) / 2))
+
+static void set_timer_reload(int reload_val);
+static void cleanup(void);
+
+#ifdef __WATCOMC__
+#define INTERRUPT      __interrupt __far
+
+static void INTERRUPT dos_timer_intr();
+
+static void (INTERRUPT *prev_timer_intr)();
+#endif
+
+#ifdef __DJGPP__
+#define INTERRUPT
+
+static _go32_dpmi_seginfo intr, prev_intr;
+#endif
+
+static void INTERRUPT timer_irq();
+
+static volatile unsigned long ticks;
+static unsigned long tick_interval, ticks_per_dos_intr;
+static int inum;
+
+void init_timer(int res_hz)
+{
+       _disable();
+
+       if(res_hz > 0) {
+               int reload_val = DIV_ROUND(OSC_FREQ_HZ, res_hz);
+               set_timer_reload(reload_val);
+
+               tick_interval = DIV_ROUND(1000, res_hz);
+               ticks_per_dos_intr = DIV_ROUND(65535L, reload_val);
+
+               inum = PIT_TIMER_INTR;
+#ifdef __WATCOMC__
+               prev_timer_intr = _dos_getvect(inum);
+               _dos_setvect(inum, timer_irq);
+#endif
+#ifdef __DJGPP__
+               _go32_dpmi_get_protected_mode_interrupt_vector(inum, &prev_intr);
+               intr.pm_offset = (intptr_t)timer_irq;
+               intr.pm_selector = _go32_my_cs();
+               _go32_dpmi_allocate_iret_wrapper(&intr);
+               _go32_dpmi_set_protected_mode_interrupt_vector(inum, &intr);
+#endif
+       } else {
+               tick_interval = 55;
+
+               inum = DOS_TIMER_INTR;
+#ifdef __WATCOMC__
+               prev_timer_intr = _dos_getvect(inum);
+               _dos_setvect(inum, dos_timer_intr);
+#endif
+#ifdef __DJGPP__
+               assert(0);
+#endif
+       }
+       _enable();
+
+       atexit(cleanup);
+}
+
+static void cleanup(void)
+{
+       if(!inum) {
+               return; /* init hasn't ran, there's nothing to cleanup */
+       }
+
+       _disable();
+       if(inum == PIT_TIMER_INTR) {
+               /* restore the original timer frequency */
+               set_timer_reload(65535);
+       }
+
+       /* restore the original interrupt handler */
+#ifdef __WATCOMC__
+       _dos_setvect(inum, prev_timer_intr);
+#endif
+#ifdef __DJGPP__
+       _go32_dpmi_set_protected_mode_interrupt_vector(inum, &prev_intr);
+       _go32_dpmi_free_iret_wrapper(&intr);
+#endif
+
+       _enable();
+}
+
+void reset_timer(void)
+{
+       ticks = 0;
+}
+
+unsigned long get_msec(void)
+{
+       return ticks * tick_interval;
+}
+
+void sleep_msec(unsigned long msec)
+{
+       unsigned long wakeup_time = ticks + msec / tick_interval;
+       while(ticks < wakeup_time) {
+#ifdef USE_HLT
+               halt();
+#endif
+       }
+}
+
+static void set_timer_reload(int reload_val)
+{
+       outp(PORT_CMD, CMD_CHAN0 | CMD_ACCESS_BOTH | CMD_OP_SQWAVE);
+       outp(PORT_DATA0, reload_val & 0xff);
+       outp(PORT_DATA0, (reload_val >> 8) & 0xff);
+}
+
+#ifdef __WATCOMC__
+static void INTERRUPT dos_timer_intr()
+{
+       ticks++;
+       _chain_intr(prev_timer_intr);   /* DOES NOT RETURN */
+}
+#endif
+
+/* first PIC command port */
+#define PIC1_CMD       0x20
+/* end of interrupt control word */
+#define OCW2_EOI       (1 << 5)
+
+static void INTERRUPT timer_irq()
+{
+       static unsigned long dos_ticks;
+
+       ticks++;
+
+#ifdef __WATCOMC__
+       if(++dos_ticks >= ticks_per_dos_intr) {
+               /* I suppose the dos irq handler does the EOI so I shouldn't
+                * do it if I am to call the previous function
+                */
+               dos_ticks = 0;
+               _chain_intr(prev_timer_intr);   /* XXX DOES NOT RETURN */
+               return; /* just for clarity */
+       }
+#endif
+
+       /* send EOI to the PIC */
+       outp(PIC1_CMD, OCW2_EOI);
+}
diff --git a/menu/src/dos/vbe.h b/menu/src/dos/vbe.h
new file mode 100644 (file)
index 0000000..42417af
--- /dev/null
@@ -0,0 +1,241 @@
+#ifndef VBE_H_
+#define VBE_H_
+
+#include <stdio.h>
+#include "sizeint.h"
+
+#pragma pack (push, 1)
+struct vbe_info {
+       char sig[4];
+       uint16_t ver;
+       char *oem_name;
+       uint32_t caps;
+       uint32_t modelist_addr;
+       uint16_t vmem_blk;      /* video memory size in 64k blocks */
+       uint16_t oem_ver;
+       char *vendor;
+       char *product;
+       char *revstr;
+       uint16_t accel_ver;
+       uint16_t *accel_modes;
+       char reserved[216];
+       char oem_data[256];
+};
+
+struct vbe_mode_info {
+       uint16_t attr;
+       uint8_t wina_attr, winb_attr;
+       uint16_t win_gran, win_size;
+       uint16_t wina_seg, winb_seg;
+       uint32_t win_func;
+       uint16_t scanline_bytes;
+
+       /* VBE 1.2 and above */
+       uint16_t xres, yres;
+       uint8_t xcharsz, ycharsz;
+       uint8_t num_planes;
+       uint8_t bpp;
+       uint8_t num_banks;
+       uint8_t mem_model;
+       uint8_t bank_size;              /* bank size in KB */
+       uint8_t num_img_pages;
+       uint8_t reserved1;
+
+       /* direct color fields */
+       uint8_t rsize, rpos;
+       uint8_t gsize, gpos;
+       uint8_t bsize, bpos;
+       uint8_t xsize, xpos;
+       uint8_t cmode_info;             /* direct color mode attributes */
+
+       /* VBE 2.0 and above */
+       uint32_t fb_addr;               /* physical address of the linear framebuffer */
+       uint32_t os_addr;               /* phys. address of off-screen memory */
+       uint16_t os_size;               /* size in KB of off-screen memory */
+
+       /* VBE 3.0 and above */
+       uint16_t lfb_scanline_bytes;
+       uint8_t banked_num_img_pages;
+       uint8_t lfb_num_img_pages;
+       uint8_t lfb_rsize, lfb_rpos;
+       uint8_t lfb_gsize, lfb_gpos;
+       uint8_t lfb_bsize, lfb_bpos;
+       uint8_t lfb_xsize, lfb_xpos;
+       uint32_t max_pixel_clock;
+
+       char reserved2[190];
+};
+
+struct vbe_crtc_info {
+       uint16_t htotal, hsync_start, hsync_end;
+       uint16_t vtotal, vsync_start, vsync_end;
+       uint8_t flags;
+       uint32_t pixel_clock;
+       uint16_t rate_centihz;  /* refresh rate in 1/100 hz (pck / (htotal * vtotal)) */
+       char reserved[40];
+};
+#pragma pack (pop)
+
+/* returned by vbe_scanline_info() */
+struct vbe_scanline_info {
+       int size;
+       int num_pixels;
+       int max_scanlines;
+};
+
+enum {
+       VBE_8BIT_DAC    = 0x01,
+       VBE_NON_VGA             = 0x02,
+       VBE_DAC_BLANK   = 0x04,
+       VBE_STEREO              = 0x08, /* ? */
+       VBE_ACCEL               = 0x08,
+       VBE_STEREO_VESA = 0x10, /* ? */
+       VBE_MUSTLOCK    = 0x10,
+       VBE_HWCURSOR    = 0x20,
+       VBE_HWCLIP              = 0x40,
+       VBE_TRANSP_BLT  = 0x80
+};
+
+#define VBE_VER_MAJOR(v)       (((v) >> 8) & 0xff)
+#define VBE_VER_MINOR(v)       ((v) & 0xff)
+
+/* VBE mode attribute flags (vbe_mode_info.attr) */
+enum {
+       VBE_ATTR_AVAIL          = 0x0001,
+       VBE_ATTR_OPTINFO        = 0x0002,
+       VBE_ATTR_TTY            = 0x0004,
+       VBE_ATTR_COLOR          = 0x0008,
+       VBE_ATTR_GFX            = 0x0010,
+       /* VBE 2.0 */
+       VBE_ATTR_NOTVGA         = 0x0020,
+       VBE_ATTR_BANKED         = 0x0040,
+       VBE_ATTR_LFB            = 0x0080,
+       VBE_ATTR_DBLSCAN        = 0x0100,
+       /* VBE 3.0 */
+       VBE_ATTR_ILACE          = 0x0200,       /* ! */
+       VBE_ATTR_TRIPLEBUF      = 0x0400,
+       VBE_ATTR_STEREO         = 0x0800,
+       VBE_ATTR_STEREO_2FB     = 0x1000,
+       /* VBE/AF */
+       VBE_ATTR_MUSTLOCK       = 0x0200        /* ! */
+};
+
+/* VBE memory model type (vbe_mode_info.mem_model) */
+enum {
+       VBE_TYPE_TEXT,
+       VBE_TYPE_CGA,
+       VBE_TYPE_HERCULES,
+       VBE_TYPE_PLANAR,
+       VBE_TYPE_PACKED,
+       VBE_TYPE_UNCHAIN,
+       VBE_TYPE_DIRECT,
+       VBE_TYPE_YUV
+};
+
+/* VBE window attribute (vbe_mode_info.win(a|b)_attr) */
+enum {
+       VBE_WIN_AVAIL   = 0x01,
+       VBE_WIN_RD              = 0x02,
+       VBE_WIN_WR              = 0x04
+};
+
+/* mode number flags */
+enum {
+       VBE_MODE_RATE           = 0x0800,       /* VBE 3.0+ user-specified refresh rate */
+       VBE_MODE_ACCEL          = 0x2000,       /* VBE/AF */
+       VBE_MODE_LFB            = 0x4000,       /* VBE 2.0+ */
+       VBE_MODE_PRESERVE       = 0x8000
+};
+
+/* standard mode numbers */
+enum {
+       VBE_640X400_8BPP        = 0x100,
+       VBE_640X480_8BPP        = 0x101,
+       VBE_800X600_4BPP        = 0x102,
+       VBE_800X600_8BPP        = 0x103,
+       VBE_1024X768_4BPP       = 0x104,
+       VBE_1024X768_8BPP       = 0x105,
+       VBE_1280X1024_4BPP      = 0x106,
+       VBE_1280X1024_8BPP      = 0x107,
+       VBE_80X60_TEXT          = 0x108,
+       VBE_132X25_TEXT         = 0x109,
+       VBE_132X43_TEXT         = 0x10a,
+       VBE_132X50_TEXT         = 0x10b,
+       VBE_132X60_TEXT         = 0x10c,
+       /* VBE 1.2 */
+       VBE_320X200_15BPP       = 0x10d,
+       VBE_320X200_16BPP       = 0x10e,
+       VBE_320X200_24BPP       = 0x10f,
+       VBE_640X480_15BPP       = 0x110,
+       VBE_640X480_16BPP       = 0x111,
+       VBE_640X480_24BPP       = 0x112,
+       VBE_800X600_15BPP       = 0x113,
+       VBE_800X600_16BPP       = 0x114,
+       VBE_800X600_24BPP       = 0x115,
+       VBE_1024X768_15BPP      = 0x116,
+       VBE_1024X768_16BPP      = 0x117,
+       VBE_1024X768_24BPP      = 0x118,
+       VBE_1280X1024_15BPP     = 0x119,
+       VBE_1280X1024_16BPP     = 0x11a,
+       VBE_1280X1024_24BPP     = 0x11b,
+       /* VBE 2.0 */
+       VBE_1600X1200_8BPP      = 0x120,
+       VBE_1600X1200_15BPP     = 0x121,
+       VBE_1600X1200_16BPP     = 0x122,
+
+       VBE_VMEM_MODE           = 0x81ff
+};
+
+/* VBE CRTC flags (vbe_crtc_info.flags) */
+enum {
+       VBE_CRTC_DBLSCAN        = 0x01,
+       VBE_CRTC_ILACE          = 0x02,
+       VBE_CRTC_HSYNC_NEG      = 0x04,
+       VBE_CRTC_VSYNC_NEG      = 0x08
+};
+
+enum {
+       VBE_STATE_CTRLHW        = 0x01,
+       VBE_STATE_BIOS          = 0x02,
+       VBE_STATE_DAC           = 0x04,
+       VBE_STATE_REGS          = 0x08,
+
+       VBE_STATE_ALL           = 0xffff
+};
+
+enum {
+       VBE_SWAP_NOW,
+       VBE_SWAP_VBLANK,
+       VBE_SWAP_ASYNC  /* schedule swap and return (triple-buffering) */
+};
+
+int vbe_info(struct vbe_info *info);
+int vbe_num_modes(struct vbe_info *info);
+int vbe_mode_info(int mode, struct vbe_mode_info *minf);
+
+void vbe_print_info(FILE *fp, struct vbe_info *info);
+void vbe_print_mode_info(FILE *fp, struct vbe_mode_info *minf);
+
+int vbe_setmode(uint16_t mode);
+int vbe_setmode_crtc(uint16_t mode, struct vbe_crtc_info *crtc);
+int vbe_getmode(void);
+
+int vbe_state_size(unsigned int flags);
+int vbe_save(void *stbuf, int sz, unsigned int flags);
+int vbe_restore(void *stbuf, int sz, unsigned int flags);
+
+int vbe_setwin(int wid, int pos);
+int vbe_getwin(int wid);
+
+/* returns the actual length in pixels, which might not be what was requested */
+int vbe_setscanlen(int len_pix);
+int vbe_getscanlen(void);
+int vbe_getpitch(void);
+int vbe_scanline_info(struct vbe_scanline_info *sinf);
+
+int vbe_setdisp(int x, int y, int when);
+int vbe_swap(uint32_t voffs, int when);
+int vbe_swap_pending(void);    /* 0: not pending (done) or error, 1: pending swap */
+/* TODO add stereo swap */
+
+#endif /* VBE_H_ */
diff --git a/menu/src/dos/vga.h b/menu/src/dos/vga.h
new file mode 100644 (file)
index 0000000..c15fe17
--- /dev/null
@@ -0,0 +1,93 @@
+#ifndef VGA_H_
+#define VGA_H_
+
+/* ---- VGA registers ---- */
+#define VGA_AC_PORT            0x3c0
+#define VGA_AC_RD_PORT         0x3c1
+#define VGA_SC_ADDR_PORT       0x3c4
+#define VGA_SC_DATA_PORT       0x3c5
+#define VGA_DAC_STATUS_PORT    0x3c7
+#define VGA_DAC_RADDR_PORT     0x3c7
+#define VGA_DAC_WADDR_PORT     0x3c8
+#define VGA_DAC_DATA_PORT      0x3c9
+#define VGA_GC_ADDR_PORT       0x3ce
+#define VGA_GC_DATA_PORT       0x3cf
+#define VGA_CRTC_PORT          0x3d4
+#define VGA_CRTC_ADDR_PORT     0x3d4
+#define VGA_CRTC_DATA_PORT     0x3d5
+#define VGA_STAT0_PORT         0x3c2
+#define VGA_STAT1_PORT         0x3da
+#define VGA_MISC_PORT          0x3c2
+#define VGA_MISC_RD_PORT       0x3cc
+
+/* attribute controller registers */
+#define VGA_AC_EN              0x20
+#define VGA_AC_MODE_REG                0x10
+
+/* sequence controller registers */
+#define VGA_SC_RESET_REG       0x00
+#define VGA_SC_CLOCK_REG       0x01
+#define VGA_SC_MAPMASK_REG     0x02
+#define VGA_SC_MEMMODE_REG     0x04
+
+/* graphics controller registers */
+#define VGA_GC_SR_REG          0x00
+#define VGA_GC_SREN_REG                0x01
+#define VGA_GC_ROT_REG         0x03
+#define VGA_GC_MODE_REG                0x05
+#define VGA_GC_MASK_REG                0x08
+
+/* attribute controller mode register (10h) bits */
+#define VGA_AC_MODE_GFX                0x01
+#define VGA_AC_MODE_MONO       0x02
+#define VGA_AC_MODE_LGE                0x04
+#define VGA_AC_MODE_BLINK      0x08
+#define VGA_AC_MODE_PIXPAN     0x20
+#define VGA_AC_MODE_8BIT       0x40
+
+/* misc register bits */
+#define VGA_MISC_COLOR         0x01
+#define VGA_MISC_CPUEN         0x02
+#define VGA_MISC_CLK25         0
+#define VGA_MISC_CLK28         0x04
+#define VGA_MISC_PG1           0x20
+#define VGA_MISC_400           0
+#define VGA_MISC_350           0x40
+#define VGA_MISC_480           0xc0
+
+
+/* CRTC registers */
+#define CRTC_HTOTAL_REG                0x00
+#define CRTC_HEND_REG          0x01
+#define CRTC_HBLSTART_REG      0x02
+#define CRTC_HBLEND_REG                0x03
+#define CRTC_HRETSTART_REG     0x04
+#define CRTC_HRETEND_REG       0x05
+#define CRTC_VTOTAL_REG                0x06
+#define CRTC_OVF_REG           0x07
+#define CRTC_PRESET_REG                0x08
+#define CRTC_MAXSCAN_REG       0x09
+#define CRTC_CURSTART_REG      0x0a
+#define CRTC_CUREND_REG                0x0b
+#define CRTC_STARTH_REG                0x0c
+#define CRTC_STARTL_REG                0x0d
+#define CRTC_CURH_REG          0x0e
+#define CRTC_CURL_REG          0x0f
+#define CRTC_VRETSTART_REG     0x10
+#define CRTC_VRETEND_REG       0x11
+#define CRTC_VEND_REG          0x12
+#define CRTC_OFFSET_REG                0x13
+#define CRTC_UL_REG            0x14
+#define CRTC_VBLSTART_REG      0x15
+#define CRTC_VBLEND_REG                0x16
+#define CRTC_MODE_REG          0x17
+#define CRTC_LCMP_REG          0x18
+
+/* CRTC register bits */
+#define CRTC_VRETEND_PR                0x80
+
+void vga_vsync(void);
+void vga_setpal(int idx, int count, const struct vid_color *col);
+void vga_getpal(int idx, int count, struct vid_color *col);
+
+#endif /* VGA_H_ */
diff --git a/menu/src/dos/vidsys.c b/menu/src/dos/vidsys.c
new file mode 100644 (file)
index 0000000..9ee925e
--- /dev/null
@@ -0,0 +1,292 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <assert.h>
+#include "vidsys.h"
+#include "vga.h"
+#include "drv.h"
+#include "cdpmi.h"
+#include "logger.h"
+
+struct vid_driver *vid_drvlist[MAX_DRV];
+int vid_numdrv;
+
+void *vid_vmem;
+int vid_vmem_size;
+
+static struct vid_modeinfo **modes, *cur_mode;
+static int num_modes, max_modes;
+
+
+int vid_init(void)
+{
+       int i, j, len;
+       struct vid_modeinfo *vm;
+
+       vid_numdrv = 0;
+       free(modes);
+       modes = 0;
+       cur_mode = 0;
+       num_modes = max_modes = 0;
+
+       vid_vmem = 0;
+       vid_vmem_size = 0;
+
+       if(dpmi_init() == -1) {
+               return -1;
+       }
+
+       vid_register_vga();
+       vid_register_vbe();
+       vid_register_s3();
+
+       for(i=0; i<vid_numdrv; i++) {
+               struct vid_driver *drv = vid_drvlist[i];
+
+               drv->ops->init();
+
+               for(j=0; j<drv->num_modes; j++) {
+                       if(num_modes >= max_modes) {
+                               int newsz = max_modes ? max_modes * 2 : 128;
+                               void *tmp = realloc(modes, newsz * sizeof *modes);
+                               if(!tmp) {
+                                       errormsg("failed to allocate modes list\n");
+                                       return -1;
+                               }
+                               modes = tmp;
+                               max_modes = newsz;
+                       }
+
+                       modes[num_modes++] = drv->modes + j;
+               }
+       }
+
+       infomsg("found %d modes:\n", num_modes);
+       for(i=0; i<num_modes; i+=2) {
+               vm = modes[i];
+               len = infomsg("[%4s] %04x: %dx%d %dbpp", vm->drv->name, vm->modeno,
+                               vm->width, vm->height, vm->bpp);
+               if(i + 1 >= num_modes) {
+                       infomsg("\n");
+                       break;
+               }
+               for(j=len; j<40; j++) infomsg(" ");
+               vm = modes[i + 1];
+               infomsg("[%4s] %04x: %dx%d %dbpp\n", vm->drv->name, vm->modeno,
+                               vm->width, vm->height, vm->bpp);
+       }
+
+       return 0;
+}
+
+void vid_cleanup(void)
+{
+       int i;
+
+       if(cur_mode && cur_mode->modeno != 3) {
+               vid_setmode(3);
+       }
+
+       if(vid_vmem >= (void*)0x100000) {
+               dpmi_munmap(vid_vmem);
+       }
+
+       for(i=0; i<vid_numdrv; i++) {
+               struct vid_driver *drv = vid_drvlist[i];
+               drv->ops->cleanup();
+       }
+
+       free(modes);
+       dpmi_cleanup();
+}
+
+
+int vid_curmode(void)
+{
+       if(cur_mode) {
+               return cur_mode->modeno;
+       }
+       return -1;
+}
+
+void *vid_setmode(int mode)
+{
+       int i;
+       struct vid_driver *drv;
+
+       for(i=0; i<num_modes; i++) {
+               if(modes[i]->modeno == mode) {
+                       drv = modes[i]->drv;
+                       if(drv->ops->setmode(mode) == 0) {
+                               cur_mode = modes[i];
+
+                               if(vid_vmem >= (void*)0x100000) {
+                                       assert(vid_vmem_size);
+                                       dpmi_munmap(vid_vmem);
+                               }
+
+                               if(modes[i]->vmem_addr < 0x100000) {
+                                       vid_vmem = (void*)modes[i]->vmem_addr;
+                                       vid_vmem_size = 0;
+                               } else {
+                                       vid_vmem = dpmi_mmap(modes[i]->vmem_addr, modes[i]->vmem_size);
+                                       vid_vmem_size = modes[i]->vmem_size;
+                               }
+                               return vid_vmem;
+                       }
+               }
+       }
+       return 0;
+}
+
+#define EQUIV_BPP(a, b)        \
+       ((a) == (b) || ((a) == 16 && (b) == 15) || ((a) == 15 && (b) == 16) || \
+        ((a) == 24 && (b) == 32) || ((a) == 32 && (b) == 24))
+
+int vid_findmode(int xsz, int ysz, int bpp)
+{
+       int i;
+
+       for(i=0; i<num_modes; i++) {
+               if(modes[i]->width == xsz && modes[i]->height == ysz && modes[i]->bpp == bpp) {
+                       return modes[i]->modeno;
+               }
+       }
+
+       /* try fuzzy bpp matching */
+       for(i=0; i<num_modes; i++) {
+               if(modes[i]->width == xsz && modes[i]->height == ysz &&
+                               EQUIV_BPP(modes[i]->bpp, bpp)) {
+                       return modes[i]->modeno;
+               }
+       }
+
+       return -1;
+}
+
+
+struct vid_modeinfo *vid_modeinfo(int mode)
+{
+       int i;
+
+       for(i=0; i<num_modes; i++) {
+               if(modes[i]->modeno == mode) {
+                       return modes[i];
+               }
+       }
+       return 0;
+}
+
+int vid_islinear(void)
+{
+       return !vid_isbanked();
+}
+
+int vid_isbanked(void)
+{
+       return cur_mode->win_size && vid_vmem < (void*)0x100000;
+}
+
+void vid_setpal(int idx, int count, const struct vid_color *col)
+{
+       cur_mode->ops.setpal(idx, count, col);
+}
+
+void vid_getpal(int idx, int count, struct vid_color *col)
+{
+       cur_mode->ops.getpal(idx, count, col);
+}
+
+void vid_blit(int x, int y, int w, int h, void *src, int pitch)
+{
+       if(pitch <= 0) {
+               pitch = cur_mode->width << 2;
+       }
+       cur_mode->ops.blit(x, y, w, h, src, pitch);
+}
+
+void vid_blitfb(void *fb, int pitch)
+{
+       if(pitch <= 0) {
+               pitch = cur_mode->width << 2;
+       }
+       cur_mode->ops.blitfb(fb, pitch);
+}
+
+void vid_blit32(int x, int y, int w, int h, uint32_t *src, int pitch)
+{
+       if(cur_mode->bpp == 32) {
+               vid_blit(x, y, w, h, src, pitch);
+               return;
+       }
+
+       if(pitch <= 0) {
+               pitch = cur_mode->width << 2;
+       }
+       /* XXX */
+}
+
+void vid_blitfb32(uint32_t *src, int pitch)
+{
+       int i, j, winpos, winleft, endskip;
+       unsigned char *dest;
+       uint16_t *dest16;
+
+       if(cur_mode->bpp == 32) {
+               vid_blitfb(src, pitch);
+               return;
+       }
+
+       if(pitch <= 0) {
+               pitch = cur_mode->width << 2;
+       }
+
+       if(vid_islinear()) {
+               winleft = INT_MAX;
+       } else {
+               winleft = cur_mode->win_size << 10;
+               winpos = 0;
+               vid_setwin(0, 0);
+       }
+
+       switch(cur_mode->bpp) {
+       case 8:
+               /* TODO */
+               break;
+
+       case 15:
+               /* TODO */
+               break;
+       case 16:
+               /* TODO */
+               break;
+
+       case 24:
+               dest = vid_vmem;
+               endskip = cur_mode->pitch - cur_mode->width * 3;
+
+               for(i=0; i<cur_mode->height; i++) {
+                       for(j=0; j<cur_mode->width; j++) {
+                               uint32_t pixel = src[j];
+                               if(winleft <= 0) {
+                                       winpos += cur_mode->win_step;
+                                       vid_setwin(0, winpos);
+                                       winleft = cur_mode->win_size << 10;
+                                       dest = vid_vmem;
+                               }
+                               dest[0] = pixel & 0xff;
+                               dest[1] = (pixel >> 8) & 0xff;
+                               dest[2] = (pixel >> 16) & 0xff;
+                               dest += 3;
+                               winleft -= 3;
+                       }
+                       src = (uint32_t*)((char*)src + pitch);
+                       dest += endskip;
+                       winleft -= endskip;
+               }
+               break;
+
+       default:
+               break;
+       }
+}
diff --git a/menu/src/dos/vidsys.h b/menu/src/dos/vidsys.h
new file mode 100644 (file)
index 0000000..9132306
--- /dev/null
@@ -0,0 +1,80 @@
+#ifndef VIDSYS_VIDEO_H_
+#define VIDSYS_VIDEO_H_
+
+#include <stdlib.h>
+#include "sizeint.h"
+
+struct vid_drvops;
+
+struct vid_color {
+       int r, g, b;
+};
+
+struct vid_gfxops {
+       void (*pack)(uint32_t *pix, int r, int g, int b);
+       void (*unpack)(uint32_t pix, int *r, int *g, int *b);
+       void (*setpal)(int idx, int count, const struct vid_color *col);
+       void (*getpal)(int idx, int count, struct vid_color *col);
+       void (*vsync)(void);
+
+       void (*clear)(uint32_t color);
+       void (*blitfb)(void *fb, int pitch);
+       void (*flip)(int vsync);
+
+       void (*fill)(int x, int y, int w, int h, uint32_t color);
+       void (*blit)(int x, int y, int w, int h, void *img, int pitch);
+       void (*line)(int x0, int y0, int x1, int y1, uint32_t color);
+       void (*hline)(int x, int y, int len, uint32_t color);
+       void (*vline)(int x, int y, int len, uint32_t color);
+};
+
+struct vid_driver {
+       const char *name;
+       int prio;
+
+       struct vid_modeinfo *modes;
+       int num_modes;
+
+       struct vid_drvops *ops;
+};
+
+struct vid_modeinfo {
+       int modeno;
+       int width, height, bpp, pitch;
+       int ncolors;
+       uint32_t rmask, gmask, bmask;
+       int rshift, gshift, bshift;
+       int pages;
+       int win_size, win_gran, win_step;
+       uint32_t vmem_addr;
+       size_t vmem_size;
+       int lfb;
+
+       struct vid_driver *drv;
+       struct vid_gfxops ops;
+};
+
+int vid_init(void);
+void vid_cleanup(void);
+
+int vid_curmode(void);
+void *vid_setmode(int mode);
+int vid_findmode(int xsz, int ysz, int bpp);
+struct vid_modeinfo *vid_modeinfo(int mode);
+
+void vid_vsync(void);                          /* defined in drv_vga.c */
+int vid_setwin(int win, int pos);      /* defined in drv_vbe.c */
+
+/* current mode functions */
+int vid_islinear(void);
+int vid_isbanked(void);
+
+void vid_setpal(int idx, int count, const struct vid_color *col);
+void vid_getpal(int idx, int count, struct vid_color *col);
+
+void vid_blit(int x, int y, int w, int h, void *src, int pitch);
+void vid_blitfb(void *fb, int pitch);
+void vid_blit32(int x, int y, int w, int h, uint32_t *src, int pitch);
+void vid_blitfb32(uint32_t *fb, int pitch);
+
+#endif /* VIDSYS_VIDEO_H_ */
diff --git a/menu/src/logger.c b/menu/src/logger.c
new file mode 100644 (file)
index 0000000..667ac73
--- /dev/null
@@ -0,0 +1,304 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "logger.h"
+
+#if defined(__MSDOS__) || defined(MSDOS)
+#include "dosutil.h"
+
+static int setup_serial(int sdev);
+
+void ser_putchar(int c);
+void ser_puts(const char *s);
+void ser_printf(const char *fmt, ...);
+#else
+#define USE_STD
+#endif
+
+enum { LOG_FILE, LOG_STREAM, LOG_CON, LOG_CB };
+enum { LOG_DBG, LOG_INFO, LOG_WARN, LOG_ERR };
+
+struct log_callback {
+       void (*func)(const char*, void*);
+       void *cls;
+};
+
+struct log_output {
+       int type, level;
+       union {
+               FILE *fp;
+               int con;
+               struct log_callback cb;
+       } out;
+};
+
+#define MAX_OUTPUTS    8
+static struct log_output outputs[MAX_OUTPUTS];
+static int num_outputs;
+
+void init_logger(void)
+{
+       num_outputs = 0;
+}
+
+void cleanup_logger(void)
+{
+       int i;
+
+       for(i=0; i<num_outputs; i++) {
+               if(outputs[i].type == LOG_FILE) {
+                       fclose(outputs[i].out.fp);
+               }
+       }
+       num_outputs = 0;
+}
+
+int add_log_file(const char *fname)
+{
+       FILE *fp;
+       int idx;
+
+       if(num_outputs >= MAX_OUTPUTS) {
+               return -1;
+       }
+       if(!(fp = fopen(fname, "w"))) {
+               return -1;
+       }
+       idx = num_outputs++;
+
+       outputs[idx].type = LOG_FILE;
+       outputs[idx].out.fp = fp;
+       return 0;
+}
+
+int add_log_stream(FILE *fp)
+{
+       int idx;
+
+       if(num_outputs >= MAX_OUTPUTS) {
+               return -1;
+       }
+       idx = num_outputs++;
+
+       outputs[idx].type = LOG_STREAM;
+       outputs[idx].out.fp = fp;
+       return 0;
+}
+
+int add_log_console(const char *devname)
+{
+#if defined(MSDOS) || defined(__MSDOS__)
+       int i, comport;
+       if(sscanf(devname, "COM%d", &comport) != 1 || comport < 1 || comport > 2) {
+               return -1;
+       }
+       comport--;
+
+       if(num_outputs >= MAX_OUTPUTS) {
+               return -1;
+       }
+       for(i=0; i<num_outputs; i++) {
+               if(outputs[i].type == LOG_CON && outputs[i].out.con == comport) {
+                       return -1;
+               }
+       }
+       if(setup_serial(comport) == -1) {
+               return -1;
+       }
+
+       i = num_outputs++;
+       outputs[i].type = LOG_CON;
+       outputs[i].out.con = comport;
+       return 0;
+
+#elif defined(unix) || defined(__unix__)
+       /* TODO? */
+       return -1;
+#endif
+}
+
+int add_log_callback(void (*cbfunc)(const char*, void*), void *cls)
+{
+       int idx;
+
+       if(num_outputs >= MAX_OUTPUTS) {
+               return -1;
+       }
+       idx = num_outputs++;
+
+       outputs[idx].type = LOG_CB;
+       outputs[idx].out.cb.func = cbfunc;
+       outputs[idx].out.cb.cls = cls;
+       return 0;
+}
+
+#if defined(__WATCOMC__)
+#ifndef vsnprintf
+#define vsnprintf _vsnprintf
+#endif
+#endif
+
+static int logmsg(int type, const char *fmt, va_list ap)
+{
+       static char buf[2048];
+       int i, len;
+
+       len = vsnprintf(buf, sizeof buf, fmt, ap);
+
+       for(i=0; i<num_outputs; i++) {
+               switch(outputs[i].type) {
+               case LOG_FILE:
+               case LOG_STREAM:
+                       fputs(buf, outputs[i].out.fp);
+                       fflush(outputs[i].out.fp);
+                       break;
+
+#if defined(MSDOS) || defined(__MSDOS__)
+               case LOG_CON:
+                       ser_puts(buf);
+                       break;
+#endif
+               case LOG_CB:
+                       outputs[i].out.cb.func(buf, outputs[i].out.cb.cls);
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       return len;
+}
+
+int errormsg(const char *fmt, ...)
+{
+       int len;
+       va_list ap;
+       va_start(ap, fmt);
+       len = logmsg(LOG_ERR, fmt, ap);
+       va_end(ap);
+       return len;
+}
+
+int warnmsg(const char *fmt, ...)
+{
+       int len;
+       va_list ap;
+       va_start(ap, fmt);
+       len = logmsg(LOG_WARN, fmt, ap);
+       va_end(ap);
+       return len;
+}
+
+int infomsg(const char *fmt, ...)
+{
+       int len;
+       va_list ap;
+       va_start(ap, fmt);
+       len = logmsg(LOG_INFO, fmt, ap);
+       va_end(ap);
+       return len;
+}
+
+int dbgmsg(const char *fmt, ...)
+{
+       int len;
+       va_list ap;
+       va_start(ap, fmt);
+       len = logmsg(LOG_DBG, fmt, ap);
+       va_end(ap);
+       return len;
+}
+
+int verrormsg(const char *fmt, va_list ap)
+{
+       return logmsg(LOG_ERR, fmt, ap);
+}
+
+int vwarnmsg(const char *fmt, va_list ap)
+{
+       return logmsg(LOG_ERR, fmt, ap);
+}
+
+int vinfomsg(const char *fmt, va_list ap)
+{
+       return logmsg(LOG_ERR, fmt, ap);
+}
+
+int vdbgmsg(const char *fmt, va_list ap)
+{
+       return logmsg(LOG_ERR, fmt, ap);
+}
+
+
+#if defined(MSDOS) || defined(__MSDOS__)
+#include <conio.h>
+
+#define UART1_BASE     0x3f8
+#define UART2_BASE     0x2f8
+
+#define UART_DATA      0
+#define UART_DIVLO     0
+#define UART_DIVHI     1
+#define UART_FIFO      2
+#define UART_LCTL      3
+#define UART_MCTL      4
+#define UART_LSTAT     5
+
+#define DIV_9600                       (115200 / 9600)
+#define DIV_38400                      (115200 / 38400)
+#define LCTL_8N1                       0x03
+#define LCTL_DLAB                      0x80
+#define FIFO_ENABLE_CLEAR      0x07
+#define MCTL_DTR_RTS_OUT2      0x0b
+#define LST_TRIG_EMPTY         0x20
+
+static unsigned int iobase;
+
+static int setup_serial(int sdev)
+{
+       if(sdev < 0 || sdev > 1) {
+               return -1;
+       }
+       iobase = sdev == 0 ? UART1_BASE : UART2_BASE;
+
+       /* set clock divisor */
+       outp(iobase | UART_LCTL, LCTL_DLAB);
+       outp(iobase | UART_DIVLO, DIV_9600 & 0xff);
+       outp(iobase | UART_DIVHI, DIV_9600 >> 8);
+       /* set format 8n1 */
+       outp(iobase | UART_LCTL, LCTL_8N1);
+       /* assert RTS and DTR */
+       outp(iobase | UART_MCTL, MCTL_DTR_RTS_OUT2);
+       return 0;
+}
+
+void ser_putchar(int c)
+{
+       if(c == '\n') {
+               ser_putchar('\r');
+       }
+
+       while((inp(iobase | UART_LSTAT) & LST_TRIG_EMPTY) == 0);
+       outp(iobase | UART_DATA, c);
+}
+
+void ser_puts(const char *s)
+{
+       while(*s) {
+               ser_putchar(*s++);
+       }
+}
+
+void ser_printf(const char *fmt, ...)
+{
+       va_list ap;
+       char buf[512];
+
+       va_start(ap, fmt);
+       vsprintf(buf, fmt, ap);
+       va_end(ap);
+
+       ser_puts(buf);
+}
+#endif
diff --git a/menu/src/logger.h b/menu/src/logger.h
new file mode 100644 (file)
index 0000000..e3bf5dc
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef LOGGER_H_
+#define LOGGER_H_
+
+#include <stdio.h>
+#include <stdarg.h>
+
+void init_logger(void);
+void cleanup_logger(void);
+
+int add_log_file(const char *fname);
+int add_log_stream(FILE *fp);
+int add_log_console(const char *devname);
+int add_log_callback(void (*cbfunc)(const char*, void*), void *cls);
+
+int errormsg(const char *fmt, ...);
+int warnmsg(const char *fmt, ...);
+int infomsg(const char *fmt, ...);
+int dbgmsg(const char *fmt, ...);
+
+int verrormsg(const char *fmt, va_list ap);
+int vwarnmsg(const char *fmt, va_list ap);
+int vinfomsg(const char *fmt, va_list ap);
+int vdbgmsg(const char *fmt, va_list ap);
+
+#endif /* LOGGER_H_ */
diff --git a/menu/src/menuscr.c b/menu/src/menuscr.c
new file mode 100644 (file)
index 0000000..bcf28c9
--- /dev/null
@@ -0,0 +1,59 @@
+#include "app.h"
+
+static int menu_init(void);
+static void menu_destroy(void);
+static int menu_start(void);
+static void menu_stop(void);
+static void menu_display(void);
+static void menu_reshape(int x, int y);
+static void menu_keyb(int key, int press);
+static void menu_mouse(int bn, int press, int x, int y);
+static void menu_motion(int x, int y);
+
+
+struct app_screen menuscr = {
+       "menu",
+       menu_init, menu_destroy,
+       menu_start, menu_stop,
+       menu_display, menu_reshape,
+       menu_keyb, menu_mouse, menu_motion
+};
+
+
+static int menu_init(void)
+{
+       return 0;
+}
+
+static void menu_destroy(void)
+{
+}
+
+static int menu_start(void)
+{
+       return 0;
+}
+
+static void menu_stop(void)
+{
+}
+
+static void menu_display(void)
+{
+}
+
+static void menu_reshape(int x, int y)
+{
+}
+
+static void menu_keyb(int key, int press)
+{
+}
+
+static void menu_mouse(int bn, int press, int x, int y)
+{
+}
+
+static void menu_motion(int x, int y)
+{
+}
diff --git a/menu/src/rtk.c b/menu/src/rtk.c
new file mode 100644 (file)
index 0000000..5c3704b
--- /dev/null
@@ -0,0 +1,1169 @@
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include "app.h"
+#include "rtk.h"
+#include "rtk_impl.h"
+#include "logger.h"
+
+static void on_any_nop();
+static void on_window_drag(rtk_widget *w, int dx, int dy, int total_dx, int total_dy);
+static void on_button_click(rtk_widget *w);
+static void on_textbox_key(rtk_widget *w, int key, int press);
+
+void inval_vport(void);        /* scr_mod.c */
+
+
+void rtk_setup(rtk_draw_ops *drawop)
+{
+       rtk_gfx = *drawop;
+
+       rtk_init_drawing();
+}
+
+static int wsize(int type)
+{
+       switch(type) {
+       case RTK_LABEL:
+       case RTK_CHECKBOX:
+       case RTK_SEP:
+       case RTK_DRAWBOX:
+               return sizeof(rtk_widget);
+       case RTK_WIN:
+               return sizeof(rtk_window);
+       case RTK_BUTTON:
+               return sizeof(rtk_button);
+       case RTK_TEXTBOX:
+               return sizeof(rtk_textbox);
+       case RTK_SLIDER:
+               return sizeof(rtk_slider);
+       default:
+               break;
+       }
+       return 0;
+}
+
+rtk_widget *rtk_create_widget(int type)
+{
+       rtk_widget *w;
+
+       if(!(w = calloc(1, wsize(type)))) {
+               return 0;
+       }
+       w->type = type;
+       w->flags = VISIBLE | ENABLED | GEOMCHG | DIRTY;
+
+       w->on_key = on_any_nop;
+       w->on_mbutton = on_any_nop;
+       w->on_click = on_any_nop;
+       w->on_drag = on_any_nop;
+       w->on_drop = on_any_nop;
+
+       w->pad = 2;
+       return w;
+}
+
+void rtk_free_widget(rtk_widget *w)
+{
+       if(!w) return;
+
+       if(w->type == RTK_WIN) {
+               rtk_window *win = (rtk_window*)w;
+               while(win->clist) {
+                       rtk_widget *c = win->clist;
+                       win->clist = win->clist->next;
+                       rtk_free_widget(c);
+               }
+       }
+
+       free(w->text);
+       free(w);
+}
+
+int rtk_type(rtk_widget *w)
+{
+       return w->type;
+}
+
+void rtk_padding(rtk_widget *w, int pad)
+{
+       w->pad = pad;
+}
+
+void rtk_move(rtk_widget *w, int x, int y)
+{
+       if(!w->par) {
+               rtk_clearfb(w);
+               rtk_invalfb(w);
+       }
+       w->x = x;
+       w->y = y;
+       w->flags |= GEOMCHG | DIRTY;
+       if(!w->par) {
+               rtk_invalfb(w);
+               inval_vport();
+       }
+}
+
+void rtk_pos(rtk_widget *w, int *xptr, int *yptr)
+{
+       *xptr = w->x;
+       *yptr = w->y;
+}
+
+void rtk_abspos(rtk_widget *w, int *xptr, int *yptr)
+{
+       *xptr = w->absx;
+       *yptr = w->absy;
+}
+
+void rtk_resize(rtk_widget *w, int xsz, int ysz)
+{
+       if(!w->par) {
+               rtk_clearfb(w);
+               rtk_invalfb(w);
+       }
+       w->width = xsz;
+       w->height = ysz;
+       w->flags |= GEOMCHG | DIRTY;
+       if(!w->par) {
+               rtk_invalfb(w);
+               inval_vport();
+       }
+}
+
+void rtk_size(rtk_widget *w, int *xptr, int *yptr)
+{
+       *xptr = w->width;
+       *yptr = w->height;
+}
+
+int rtk_get_width(rtk_widget *w)
+{
+       return w->width;
+}
+
+int rtk_get_height(rtk_widget *w)
+{
+       return w->height;
+}
+
+void rtk_get_rect(rtk_widget *w, rtk_rect *r)
+{
+       r->x = w->x;
+       r->y = w->y;
+       r->width = w->width;
+       r->height = w->height;
+}
+
+void rtk_get_absrect(rtk_widget *w, rtk_rect *r)
+{
+       r->x = w->absx;
+       r->y = w->absy;
+       r->width = w->width;
+       r->height = w->height;
+}
+
+void rtk_autosize(rtk_widget *w, unsigned int szopt)
+{
+       if(szopt & RTK_AUTOSZ_WIDTH) {
+               w->flags |= AUTOWIDTH;
+       } else {
+               w->flags &= ~AUTOWIDTH;
+       }
+
+       if(szopt & RTK_AUTOSZ_HEIGHT) {
+               w->flags |= AUTOHEIGHT;
+       } else {
+               w->flags &= ~AUTOHEIGHT;
+       }
+}
+
+int rtk_set_text(rtk_widget *w, const char *str)
+{
+       rtk_rect rect;
+       char *s = strdup(str);
+       if(!s) return -1;
+
+       free(w->text);
+       w->text = s;
+
+       if(w->type == RTK_TEXTBOX) {
+               rtk_textbox *tb = (rtk_textbox*)w;
+               tb->cursor = tb->scroll = 0;
+               tb->bufsz = strlen(str) + 1;
+       }
+
+       rtk_calc_widget_rect(w, &rect);
+       rtk_resize(w, rect.width, rect.height);
+       rtk_invalidate(w);
+       return 0;
+}
+
+const char *rtk_get_text(rtk_widget *w)
+{
+       return w->text;
+}
+
+void rtk_set_value(rtk_widget *w, int val)
+{
+       w->value = val;
+       rtk_invalidate(w);
+}
+
+int rtk_get_value(rtk_widget *w)
+{
+       return w->value;
+}
+
+void rtk_set_callback(rtk_widget *w, rtk_callback cbfunc, void *cls)
+{
+       w->cbfunc = cbfunc;
+       w->cbcls = cls;
+}
+
+void rtk_set_callback_closure(rtk_widget *w, void *cls)
+{
+       w->cbcls = cls;
+}
+
+rtk_callback rtk_get_callback(const rtk_widget *w)
+{
+       return w->cbfunc;
+}
+
+void *rtk_get_callback_closure(const rtk_widget *w)
+{
+       return w->cbcls;
+}
+
+void rtk_set_drawfunc(rtk_widget *w, rtk_callback drawfunc, void *cls)
+{
+       w->drawcb = drawfunc;
+       w->drawcls = cls;
+}
+
+void rtk_set_key_handler(rtk_widget *w, rtk_key_callback func)
+{
+       w->on_key = func;
+}
+
+void rtk_set_mbutton_handler(rtk_widget *w, rtk_mbutton_callback func)
+{
+       w->on_mbutton = func;
+}
+
+void rtk_set_click_handler(rtk_widget *w, rtk_click_callback func)
+{
+       w->on_click = func;
+}
+
+void rtk_set_drag_handler(rtk_widget *w, rtk_drag_callback func)
+{
+       w->on_drag = func;
+}
+
+void rtk_set_drop_handler(rtk_widget *w, rtk_drop_callback func)
+{
+       w->on_drop = func;
+}
+
+void rtk_show(rtk_widget *w)
+{
+       w->flags |= VISIBLE;
+       rtk_invalidate(w);
+}
+
+void rtk_show_modal(rtk_widget *w)
+{
+       rtk_show(w);
+       if(w->scr) {
+               w->scr->modal = w;
+       }
+}
+
+void rtk_hide(rtk_widget *w)
+{
+       w->flags &= ~VISIBLE;
+       if(w->scr && w->scr->modal == w) {
+               w->scr->modal = 0;
+       }
+       rtk_clearfb(w);
+       rtk_invalfb(w);
+       inval_vport();
+}
+
+int rtk_visible(const rtk_widget *w)
+{
+       return w->flags & VISIBLE;
+}
+
+void rtk_invalidate(rtk_widget *w)
+{
+       w->flags |= DIRTY;
+
+#if !defined(MSDOS) && !defined(__MSDOS__)
+       /* this is necessary because the GLUT backend will have to see a
+        * glutRedisplay in order for anything to be redrawn
+        */
+       rtk_invalfb(w);
+#endif
+}
+
+void rtk_validate(rtk_widget *w)
+{
+       w->flags &= ~DIRTY;
+}
+
+void rtk_win_layout(rtk_widget *w, int layout)
+{
+       ((rtk_window*)w)->layout = layout;
+}
+
+void rtk_win_clear(rtk_widget *w)
+{
+       rtk_widget *tmp;
+       rtk_window *win = (rtk_window*)w;
+
+       RTK_ASSERT_TYPE(w, RTK_WIN);
+
+       while(win->clist) {
+               tmp = win->clist;
+               win->clist = win->clist->next;
+               rtk_free_widget(tmp);
+       }
+
+       win->clist = win->ctail = 0;
+       rtk_invalidate(w);
+}
+
+void rtk_win_add(rtk_widget *par, rtk_widget *child)
+{
+       rtk_window *parwin = (rtk_window*)par;
+
+       RTK_ASSERT_TYPE(par, RTK_WIN);
+
+       if(rtk_win_has(par, child)) {
+               return;
+       }
+
+       if(child->par) {
+               rtk_win_rm((rtk_widget*)child->par, child);
+       }
+
+       if(parwin->clist) {
+               parwin->ctail->next = child;
+               parwin->ctail = child;
+       } else {
+               parwin->clist = parwin->ctail = child;
+       }
+       child->next = 0;
+
+       child->par = parwin;
+       rtk_invalidate(par);
+}
+
+void rtk_win_rm(rtk_widget *par, rtk_widget *child)
+{
+       rtk_widget *prev, dummy;
+       rtk_window *parwin = (rtk_window*)par;
+
+       RTK_ASSERT_TYPE(par, RTK_WIN);
+
+       dummy.next = parwin->clist;
+       prev = &dummy;
+       while(prev->next) {
+               if(prev->next == child) {
+                       if(!child->next) {
+                               parwin->ctail = prev;
+                       }
+                       prev->next = child->next;
+                       break;
+               }
+               prev = prev->next;
+       }
+       parwin->clist = dummy.next;
+       rtk_invalidate(par);
+}
+
+int rtk_win_has(rtk_widget *par, rtk_widget *child)
+{
+       rtk_widget *w;
+       rtk_window *parwin = (rtk_window*)par;
+
+       RTK_ASSERT_TYPE(par, RTK_WIN);
+
+       w = parwin->clist;
+       while(w) {
+               if(w == child) {
+                       return 1;
+               }
+               w = w->next;
+       }
+       return 0;
+}
+
+rtk_widget *rtk_win_child(const rtk_widget *par, int idx)
+{
+       rtk_widget *w;
+       rtk_window *win = (rtk_window*)par;
+
+       RTK_ASSERT_TYPE(par, RTK_WIN);
+
+       w = win->clist;
+       while(w && idx-- > 0) {
+               w = w->next;
+       }
+       return idx > 0 ? 0 : w;
+}
+
+int rtk_win_descendant(const rtk_widget *par, const rtk_widget *w)
+{
+       rtk_widget *c;
+       rtk_window *parwin = (rtk_window*)par;
+
+       RTK_ASSERT_TYPE(par, RTK_WIN);
+
+       c = parwin->clist;
+       while(c) {
+               if(c == w) {
+                       return 1;
+               }
+               if(c->type == RTK_WIN) {
+                       if(rtk_win_descendant(c, w)) {
+                               return 1;
+                       }
+               }
+               c = c->next;
+       }
+       return 0;
+}
+
+/* --- button functions --- */
+void rtk_bn_mode(rtk_widget *w, int mode)
+{
+       RTK_ASSERT_TYPE(w, RTK_BUTTON);
+       ((rtk_button*)w)->mode = mode;
+       rtk_invalidate(w);
+}
+
+void rtk_bn_set_icon(rtk_widget *w, rtk_icon *icon)
+{
+       rtk_rect rect;
+
+       RTK_ASSERT_TYPE(w, RTK_BUTTON);
+       ((rtk_button*)w)->icon = icon;
+
+       rtk_calc_widget_rect(w, &rect);
+       rtk_resize(w, rect.width, rect.height);
+       rtk_invalidate(w);
+}
+
+rtk_icon *rtk_bn_get_icon(rtk_widget *w)
+{
+       RTK_ASSERT_TYPE(w, RTK_BUTTON);
+       return ((rtk_button*)w)->icon;
+}
+
+/* --- slider functions --- */
+void rtk_slider_set_range(rtk_widget *w, int vmin, int vmax)
+{
+       rtk_slider *slider = (rtk_slider*)w;
+       RTK_ASSERT_TYPE(w, RTK_SLIDER);
+
+       slider->vmin = vmin;
+       slider->vmax = vmax;
+}
+
+void rtk_slider_get_range(const rtk_widget *w, int *vmin, int *vmax)
+{
+       rtk_slider *slider = (rtk_slider*)w;
+       RTK_ASSERT_TYPE(w, RTK_SLIDER);
+
+       *vmin = slider->vmin;
+       *vmax = slider->vmax;
+}
+
+
+/* --- constructors --- */
+
+rtk_widget *rtk_create_window(rtk_widget *par, const char *title, int x, int y,
+               int width, int height, unsigned int flags)
+{
+       rtk_widget *w;
+
+       if(!(w = rtk_create_widget(RTK_WIN))) {
+               return 0;
+       }
+       if(par) {
+               rtk_win_add(par, w);
+       }
+       w->on_drag = on_window_drag;
+       rtk_set_text(w, title);
+       rtk_move(w, x, y);
+       rtk_resize(w, width, height);
+       rtk_win_layout(w, RTK_VBOX);
+
+       w->flags |= flags << 16;
+       return w;
+}
+
+rtk_widget *rtk_create_button(rtk_widget *par, const char *str, rtk_callback cbfunc)
+{
+       rtk_widget *w;
+
+       if(!(w = rtk_create_widget(RTK_BUTTON))) {
+               return 0;
+       }
+       if(par) rtk_win_add(par, w);
+       rtk_autosize(w, RTK_AUTOSZ_SIZE);
+       w->on_click = on_button_click;
+       rtk_set_text(w, str);
+       rtk_set_callback(w, cbfunc, 0);
+       return w;
+}
+
+rtk_widget *rtk_create_iconbutton(rtk_widget *par, rtk_icon *icon, rtk_callback cbfunc)
+{
+       rtk_widget *w;
+
+       if(!(w = rtk_create_widget(RTK_BUTTON))) {
+               return 0;
+       }
+       if(par) rtk_win_add(par, w);
+       rtk_autosize(w, RTK_AUTOSZ_SIZE);
+       w->on_click = on_button_click;
+       rtk_bn_set_icon(w, icon);
+       rtk_set_callback(w, cbfunc, 0);
+       return w;
+}
+
+rtk_widget *rtk_create_label(rtk_widget *par, const char *text)
+{
+       rtk_widget *w;
+
+       if(!(w = rtk_create_widget(RTK_LABEL))) {
+               return 0;
+       }
+       if(par) rtk_win_add(par, w);
+       rtk_autosize(w, RTK_AUTOSZ_SIZE);
+       rtk_set_text(w, text);
+       return w;
+}
+
+rtk_widget *rtk_create_checkbox(rtk_widget *par, const char *text, int chk, rtk_callback cbfunc)
+{
+       rtk_widget *w;
+
+       if(!(w = rtk_create_widget(RTK_CHECKBOX))) {
+               return 0;
+       }
+       if(par) rtk_win_add(par, w);
+       rtk_autosize(w, RTK_AUTOSZ_SIZE);
+       rtk_set_text(w, text);
+       rtk_set_value(w, chk ? 1 : 0);
+       rtk_set_callback(w, cbfunc, 0);
+       return w;
+}
+
+rtk_widget *rtk_create_textbox(rtk_widget *par, const char *text, rtk_callback cbfunc)
+{
+       rtk_widget *w;
+
+       if(!(w = rtk_create_widget(RTK_TEXTBOX))) {
+               return 0;
+       }
+       if(par) rtk_win_add(par, w);
+       rtk_autosize(w, RTK_AUTOSZ_HEIGHT);
+       w->on_key = on_textbox_key;
+       if(text) {
+               rtk_set_text(w, text);
+       }
+       rtk_set_callback(w, cbfunc, 0);
+       rtk_resize(w, 40, 1);
+
+       w->flags |= CANFOCUS;
+       return w;
+}
+
+rtk_widget *rtk_create_slider(rtk_widget *par, int vmin, int vmax, int val, rtk_callback cbfunc)
+{
+       rtk_widget *w;
+
+       if(!(w = rtk_create_widget(RTK_SLIDER))) {
+               return 0;
+       }
+       if(par) rtk_win_add(par, w);
+       rtk_autosize(w, RTK_AUTOSZ_HEIGHT);
+       rtk_set_callback(w, cbfunc, 0);
+       rtk_slider_set_range(w, vmin, vmax);
+       rtk_set_value(w, val);
+       return w;
+}
+
+rtk_widget *rtk_create_separator(rtk_widget *par)
+{
+       rtk_widget *w;
+
+       if(!(w = rtk_create_widget(RTK_SEP))) {
+               return 0;
+       }
+       if(par) rtk_win_add(par, w);
+       rtk_autosize(w, RTK_AUTOSZ_SIZE);
+       return w;
+}
+
+rtk_widget *rtk_create_drawbox(rtk_widget *par, int width, int height, rtk_callback cbfunc)
+{
+       rtk_widget *w;
+
+       if(!(w = rtk_create_widget(RTK_DRAWBOX))) {
+               return 0;
+       }
+       if(par) rtk_win_add(par, w);
+       rtk_resize(w, width, height);
+       rtk_set_drawfunc(w, cbfunc, 0);
+       return w;
+}
+
+/* --- compound widgets --- */
+rtk_widget *rtk_create_hbox(rtk_widget *par)
+{
+       rtk_widget *box = rtk_create_window(par, "hbox", 0, 0, 0, 0, 0);
+       rtk_autosize(box, RTK_AUTOSZ_SIZE);
+       rtk_win_layout(box, RTK_HBOX);
+       return box;
+}
+
+rtk_widget *rtk_create_vbox(rtk_widget *par)
+{
+       rtk_widget *box = rtk_create_window(par, "vbox", 0, 0, 0, 0, 0);
+       rtk_autosize(box, RTK_AUTOSZ_SIZE);
+       rtk_win_layout(box, RTK_VBOX);
+       return box;
+}
+
+rtk_widget *rtk_create_field(rtk_widget *par, const char *lbtext, rtk_callback cbfunc)
+{
+       rtk_widget *hbox;
+       rtk_widget *lb, *tb;
+
+       if(!(hbox = rtk_create_window(par, "field", 0, 0, 0, 0, 0))) {
+               return 0;
+       }
+       rtk_win_layout(hbox, RTK_HBOX);
+       if(!(lb = rtk_create_label(hbox, lbtext))) {
+               rtk_free_widget(hbox);
+               return 0;
+       }
+       if(!(tb = rtk_create_textbox(hbox, 0, cbfunc))) {
+               rtk_free_widget(hbox);
+               return 0;
+       }
+       return tb;
+}
+
+/* --- icon functions --- */
+rtk_iconsheet *rtk_load_iconsheet(const char *fname)
+{
+       /*
+        rtk_iconsheet *is;
+
+       if(!(is = malloc(sizeof *is))) {
+               return 0;
+       }
+       is->icons = 0;
+
+       if(!(is->pixels = img_load_pixels(fname, &is->width, &is->height, IMG_FMT_RGBA32))) {
+               free(is);
+               return 0;
+       }
+       return is;
+       */
+       return 0;
+}
+
+void rtk_free_iconsheet(rtk_iconsheet *is)
+{
+       /*
+       rtk_icon *icon;
+
+       img_free_pixels(is->pixels);
+
+       while(is->icons) {
+               icon = is->icons;
+               is->icons = is->icons->next;
+               free(icon->name);
+               free(icon);
+       }
+       free(is);
+       */
+}
+
+rtk_icon *rtk_define_icon(rtk_iconsheet *is, const char *name, int x, int y, int w, int h)
+{
+       rtk_icon *icon;
+
+       if(!(icon = malloc(sizeof *icon))) {
+               return 0;
+       }
+       if(!(icon->name = strdup(name))) {
+               free(icon);
+               return 0;
+       }
+       icon->width = w;
+       icon->height = h;
+       icon->scanlen = is->width;
+       icon->pixels = is->pixels + y * is->width + x;
+
+       icon->next = is->icons;
+       is->icons = icon;
+       return icon;
+}
+
+rtk_icon *rtk_lookup_icon(rtk_iconsheet *is, const char *name)
+{
+       rtk_icon *icon = is->icons;
+       while(icon) {
+               if(strcmp(icon->name, name) == 0) {
+                       return icon;
+               }
+               icon = icon->next;
+       }
+       return 0;
+}
+
+static void sethover(rtk_screen *scr, rtk_widget *w)
+{
+       if(scr->hover == w) return;
+
+       if(scr->hover) {
+               scr->hover->flags &= ~HOVER;
+
+               if(scr->hover->type != RTK_WIN) {
+                       rtk_invalidate(scr->hover);
+               }
+       }
+       scr->hover = w;
+       if(w) {
+               w->flags |= HOVER;
+
+               if(w->type != RTK_WIN) {
+                       rtk_invalidate(w);
+               }
+       }
+
+       /*dbgmsg("hover \"%s\"\n", w ? (w->text ? w->text : "?") : "-");*/
+}
+
+static void setpress(rtk_widget *w, int press)
+{
+       if(press) {
+               w->flags |= PRESS;
+       } else {
+               w->flags &= ~PRESS;
+       }
+       rtk_invalidate(w);
+}
+
+static void setfocus(rtk_screen *scr, rtk_widget *w)
+{
+       rtk_window *win;
+
+       if(scr->focus) {
+               scr->focus->flags &= ~FOCUS;
+       }
+
+       if(scr->focuswin) {
+               scr->focuswin->flags &= ~FOCUS;
+               rtk_invalidate((rtk_widget*)scr->focuswin);
+       }
+
+       if(!w) return;
+
+       if(w->flags & CANFOCUS) {
+               w->flags |= FOCUS;
+               scr->focus = w;
+       }
+
+       win = (rtk_window*)w;
+       while(win->par) {
+               win = win->par;
+       }
+       win->flags |= FOCUS;
+       scr->focuswin = win;
+
+       rtk_invalidate(w);
+       rtk_invalidate((rtk_widget*)win);
+}
+
+/* --- screen functions --- */
+rtk_screen *rtk_create_screen(void)
+{
+       rtk_screen *scr;
+
+       if(!(scr = calloc(1, sizeof *scr))) {
+               return 0;
+       }
+       return scr;
+}
+
+void rtk_free_screen(rtk_screen *scr)
+{
+       int i;
+
+       for(i=0; i<scr->num_win; i++) {
+               rtk_free_widget(scr->winlist[i]);
+       }
+       free(scr);
+}
+
+int rtk_add_window(rtk_screen *scr, rtk_widget *win)
+{
+       if(scr->num_win >= MAX_WINDOWS) {
+               return -1;
+       }
+       scr->winlist[scr->num_win++] = win;
+       win->scr = scr;
+       return 0;
+}
+
+static rtk_widget *find_widget_at(rtk_widget *w, int x, int y, unsigned int flags)
+{
+       rtk_widget *c, *res;
+       rtk_window *win;
+
+       if(!(w->flags & VISIBLE)) {
+               return 0;
+       }
+
+       if(w->type != RTK_WIN) {
+               if((w->flags & flags) && rtk_hittest(w, x, y)) {
+                       return w;
+               }
+               return 0;
+       }
+
+       win = (rtk_window*)w;
+       c = win->clist;
+       while(c) {
+               if((res = find_widget_at(c, x, y, flags))) {
+                       return res;
+               }
+               c = c->next;
+       }
+       return rtk_hittest(w, x, y) ? w : 0;
+}
+
+rtk_widget *rtk_find_widget_at(rtk_screen *scr, rtk_widget *win, int x, int y, unsigned int flags)
+{
+       int i;
+       rtk_widget *w;
+
+       if(!flags) flags = ~0;
+
+       if(win) {
+               RTK_ASSERT_TYPE(win, RTK_WIN);
+               return find_widget_at(win, x, y, flags);
+       }
+
+       for(i=0; i<scr->num_win; i++) {
+               if((w = find_widget_at(scr->winlist[i], x, y, flags))) {
+                       return w;
+               }
+       }
+       return 0;
+}
+
+int rtk_input_key(rtk_screen *scr, int key, int press)
+{
+       if(scr->focus && (!scr->modal || rtk_win_descendant(scr->modal, scr->focus))) {
+               scr->focus->on_key(scr->focus, key, press);
+               return 1;
+       }
+
+       if(scr->modal) {
+               if(!press) return 1;
+
+               switch(key) {
+               case 27:
+                       rtk_hide(scr->modal);
+                       break;
+
+               case '\n':
+                       /* TODO */
+                       break;
+
+               default:
+                       break;
+               }
+               return 1;
+       }
+       return 0;
+}
+
+int rtk_input_mbutton(rtk_screen *scr, int bn, int press, int x, int y)
+{
+       int handled = 0;
+       rtk_widget *w;
+
+       if((w = rtk_find_widget_at(scr, scr->modal, x, y, 0))) {
+               int relx = x - w->absx;
+               int rely = y - w->absy;
+               w->on_mbutton(w, bn, press, relx, rely);
+       }
+
+       if(press) {
+               scr->prev_mx = x;
+               scr->prev_my = y;
+               if(bn == 0) {
+                       scr->press = w;
+                       scr->press_x = x;
+                       scr->press_y = y;
+
+                       if(w) setpress(w, 1);
+               }
+               handled = w ? 1 : 0;
+       } else {
+               if(!w && scr->modal) {
+                       rtk_hide(scr->modal);
+                       return 1;
+               }
+
+               if(bn == 0) {
+                       rtk_widget *newfocus = 0;
+                       if(w) {
+                               if(scr->press == w) {
+                                       newfocus = rtk_find_widget_at(scr, scr->modal, x, y, 0);
+                                       w->on_click(w);
+                               } else {
+                                       w->on_drop(scr->press, w);
+                               }
+                               handled = 1;
+                       }
+
+                       setfocus(scr, newfocus);
+
+                       if(scr->press) {
+                               setpress(scr->press, 0);
+                               scr->press = 0;
+                       }
+               }
+       }
+       return scr->modal ? 1 : handled;
+}
+
+int rtk_input_mmotion(rtk_screen *scr, int x, int y)
+{
+       int dx, dy;
+       rtk_widget *w;
+
+       dx = x - scr->prev_mx;
+       dy = y - scr->prev_my;
+       scr->prev_mx = x;
+       scr->prev_my = y;
+
+       if(scr->press) {
+               w = scr->press;
+               if((dx | dy)) {
+                       w->on_drag(w, dx, dy, x - scr->press_x, y - scr->press_y);
+               }
+               return 1;
+       }
+
+       if(!(w = rtk_find_widget_at(scr, scr->modal, x, y, 0))) {
+               return scr->modal ? 1 : 0;
+       }
+       sethover(scr, w);
+       return 1;
+}
+
+void rtk_invalidate_screen(rtk_screen *scr)
+{
+       int i;
+       for(i=0; i<scr->num_win; i++) {
+               rtk_invalidate(scr->winlist[i]);
+       }
+}
+
+void rtk_draw_screen(rtk_screen *scr)
+{
+       int i;
+       for(i=0; i<scr->num_win; i++) {
+               rtk_draw_widget(scr->winlist[i]);
+       }
+}
+
+/* --- misc functions --- */
+void rtk_null_rect(rtk_rect *rect)
+{
+       rect->x = rect->y = INT_MAX / 2;
+       rect->width = rect->height = INT_MIN;
+}
+
+void rtk_fix_rect(rtk_rect *rect)
+{
+       int x, y, w, h;
+
+       x = rect->x;
+       y = rect->y;
+
+       if(rect->width < 0) {
+               w = -rect->width;
+               x += rect->width;
+       } else {
+               w = rect->width;
+       }
+       if(rect->height < 0) {
+               h = -rect->height;
+               y += rect->height;
+       } else {
+               h = rect->height;
+       }
+
+       rect->x = x;
+       rect->y = y;
+       rect->width = w;
+       rect->height = h;
+}
+
+void rtk_rect_union(rtk_rect *a, const rtk_rect *b)
+{
+       int x0, y0, x1, y1;
+
+       x0 = a->x;
+       y0 = a->y;
+       x1 = a->x + a->width;
+       y1 = a->y + a->height;
+
+       if(b->x < x0) x0 = b->x;
+       if(b->y < y0) y0 = b->y;
+       if(b->x + b->width > x1) x1 = b->x + b->width;
+       if(b->y + b->height > y1) y1 = b->y + b->height;
+
+       a->x = x0;
+       a->y = y0;
+       a->width = x1 - x0;
+       a->height = y1 - y0;
+}
+
+
+/* --- widget event handlers --- */
+static void on_any_nop()
+{
+}
+
+static void on_window_drag(rtk_widget *w, int dx, int dy, int total_dx, int total_dy)
+{
+       if(w->flags & MOVABLE) {
+               rtk_move(w, w->x + dx, w->y + dy);
+       }
+}
+
+static void on_button_click(rtk_widget *w)
+{
+       rtk_button *bn = (rtk_button*)w;
+
+       switch(w->type) {
+       case RTK_BUTTON:
+               if(bn->mode == RTK_TOGGLEBN) {
+               case RTK_CHECKBOX:
+                       bn->value ^= 1;
+               }
+               if(bn->cbfunc) {
+                       bn->cbfunc(w, bn->cbcls);
+               }
+               rtk_invalidate(w);
+               break;
+
+       default:
+               break;
+       }
+}
+
+static void on_textbox_key(rtk_widget *w, int key, int press)
+{
+       rtk_textbox *tb = (rtk_textbox*)w;
+
+       if(!press) return;
+
+       switch(key) {
+       case KEY_BACKSP:
+               if(tb->cursor > 0) {
+                       if(tb->cursor < tb->len) {
+                               memmove(tb->text + tb->cursor - 1, tb->text + tb->cursor, tb->len - tb->cursor + 1);
+                               tb->cursor--;
+                       } else {
+                               tb->text[--tb->cursor] = 0;
+                       }
+                       tb->len--;
+               }
+               rtk_invalidate(w);
+               return;
+
+       case KEY_HOME:
+               tb->cursor = 0;
+               tb->scroll = 0;
+               rtk_invalidate(w);
+               return;
+
+       case KEY_END:
+               tb->cursor = tb->len;
+               rtk_invalidate(w);
+               return;
+
+       case KEY_LEFT:
+               if(tb->cursor > 0) {
+                       tb->cursor--;
+               }
+               if(tb->cursor < tb->scroll) {
+                       tb->scroll = tb->cursor;
+               }
+               rtk_invalidate(w);
+               return;
+
+       case KEY_RIGHT:
+               if(tb->cursor < tb->len) {
+                       tb->cursor++;
+               }
+               rtk_invalidate(w);
+               return;
+
+       default:
+               if(!isprint(key)) {
+                       return;
+               }
+               break;
+       }
+
+       /* we end up here if it's a printable character */
+       if(tb->len >= tb->bufsz - 1) {
+               int nsz = tb->bufsz ? tb->bufsz * 2 : 16;
+               void *tmp = realloc(tb->text, nsz);
+               if(!tmp) return;
+               tb->text = tmp;
+               tb->bufsz = nsz;
+       }
+
+       if(tb->len <= 0 || tb->cursor >= tb->len) {
+               tb->text[tb->len++] = key;
+               tb->text[tb->len] = 0;
+               tb->cursor++;
+       } else {
+               memmove(tb->text + tb->cursor + 1, tb->text + tb->cursor, tb->len - tb->cursor + 1);
+               tb->text[tb->cursor++] = key;
+               tb->len++;
+       }
+
+       rtk_invalidate(w);
+}
+
+void rtk_dbg_showrect(rtk_widget *w, int show)
+{
+       if(show) {
+               w->flags |= DBGRECT;
+       } else {
+               w->flags &= ~DBGRECT;
+       }
+}
diff --git a/menu/src/rtk.h b/menu/src/rtk.h
new file mode 100644 (file)
index 0000000..c06e176
--- /dev/null
@@ -0,0 +1,191 @@
+#ifndef RTK_H_
+#define RTK_H_
+
+#include "sizeint.h"
+
+/* widget type */
+enum {
+       RTK_ANY,
+       RTK_WIN,
+       RTK_BUTTON,
+       RTK_LABEL,
+       RTK_CHECKBOX,
+       RTK_TEXTBOX,
+       RTK_SLIDER,
+       RTK_SEP,
+       RTK_DRAWBOX
+};
+/* widget sizing policy */
+enum {
+       RTK_AUTOSZ_NONE         = 0,
+       RTK_AUTOSZ_WIDTH        = 1,
+       RTK_AUTOSZ_HEIGHT       = 2,
+       RTK_AUTOSZ_SIZE         = RTK_AUTOSZ_WIDTH | RTK_AUTOSZ_HEIGHT
+};
+/* window layout */
+enum { RTK_NONE, RTK_VBOX, RTK_HBOX };
+/* window flags */
+enum {
+       RTK_WIN_FRAME           = 1,
+       RTK_WIN_MOVABLE         = 2,
+       RTK_WIN_RESIZABLE       = 4
+};
+/* button mode */
+enum { RTK_PUSHBN, RTK_TOGGLEBN };
+
+typedef struct rtk_screen rtk_screen;
+typedef struct rtk_widget rtk_widget;
+typedef struct rtk_icon rtk_icon;
+typedef struct rtk_iconsheet rtk_iconsheet;
+
+typedef struct rtk_rect {
+       int x, y, width, height;
+} rtk_rect;
+
+typedef struct rtk_icon {
+       char *name;
+       int width, height, scanlen;
+       uint32_t *pixels;
+
+       struct rtk_icon *next;
+} rtk_icon;
+
+
+typedef struct rtk_draw_ops {
+       void (*fill)(rtk_rect *rect, uint32_t color);
+       void (*blit)(int x, int y, rtk_icon *icon);
+       void (*drawtext)(int x, int y, const char *str);
+       void (*textrect)(const char *str, rtk_rect *rect);
+} rtk_draw_ops;
+
+typedef void (*rtk_callback)(rtk_widget*, void*);
+/* low level event handling override callbacks */
+typedef void (*rtk_key_callback)(rtk_widget *w, int key, int press);
+typedef void (*rtk_mbutton_callback)(rtk_widget *w, int bn, int press, int x, int y);
+typedef void (*rtk_click_callback)(rtk_widget *w);
+typedef void (*rtk_drag_callback)(rtk_widget *w, int dx, int dy, int total_dx, int total_dy);
+typedef void (*rtk_drop_callback)(rtk_widget *w, rtk_widget *targ);
+
+/* global state */
+void rtk_setup(rtk_draw_ops *drawop);
+
+/* widget functions */
+rtk_widget *rtk_create_widget(int type);
+void rtk_free_widget(rtk_widget *w);
+
+int rtk_type(rtk_widget *w);
+rtk_widget *rtk_parent(rtk_widget *w);
+
+void rtk_padding(rtk_widget *w, int pad);
+
+void rtk_move(rtk_widget *w, int x, int y);
+void rtk_pos(rtk_widget *w, int *xptr, int *yptr);
+void rtk_abspos(rtk_widget *w, int *xptr, int *yptr);
+void rtk_resize(rtk_widget *w, int xsz, int ysz);
+void rtk_size(rtk_widget *w, int *xptr, int *yptr);
+int rtk_get_width(rtk_widget *w);
+int rtk_get_height(rtk_widget *w);
+void rtk_get_rect(rtk_widget *w, rtk_rect *r);
+void rtk_get_absrect(rtk_widget *w, rtk_rect *r);
+
+void rtk_autosize(rtk_widget *w, unsigned int szopt);
+
+int rtk_set_text(rtk_widget *w, const char *str);
+const char *rtk_get_text(rtk_widget *w);
+
+void rtk_set_value(rtk_widget *w, int val);
+int rtk_get_value(rtk_widget *w);
+
+void rtk_set_userdata(rtk_widget *w, void *udata);
+void *rtk_get_userdata(rtk_widget *w);
+
+void rtk_set_callback(rtk_widget *w, rtk_callback cbfunc, void *cls);
+void rtk_set_callback_closure(rtk_widget *w, void *cls);
+rtk_callback rtk_get_callback(const rtk_widget *w);
+void *rtk_get_callback_closure(const rtk_widget *w);
+
+void rtk_set_drawfunc(rtk_widget *w, rtk_callback drawfunc, void *cls);
+void rtk_set_key_handler(rtk_widget *w, rtk_key_callback func);
+void rtk_set_mbutton_handler(rtk_widget *w, rtk_mbutton_callback func);
+void rtk_set_click_handler(rtk_widget *w, rtk_click_callback func);
+void rtk_set_drag_handler(rtk_widget *w, rtk_drag_callback func);
+void rtk_set_drop_handler(rtk_widget *w, rtk_drop_callback func);
+
+void rtk_show(rtk_widget *w);
+void rtk_show_modal(rtk_widget *w);
+void rtk_hide(rtk_widget *w);
+int rtk_visible(const rtk_widget *w);
+
+void rtk_invalidate(rtk_widget *w);
+void rtk_validate(rtk_widget *w);
+
+/* window functions */
+void rtk_win_layout(rtk_widget *w, int layout);
+void rtk_win_clear(rtk_widget *w);
+void rtk_win_add(rtk_widget *par, rtk_widget *child);
+void rtk_win_rm(rtk_widget *par, rtk_widget *child);
+int rtk_win_has(rtk_widget *par, rtk_widget *child);
+rtk_widget *rtk_win_child(const rtk_widget *par, int idx);
+
+/* button functions */
+void rtk_bn_mode(rtk_widget *w, int mode);
+void rtk_bn_set_icon(rtk_widget *w, rtk_icon *icon);
+rtk_icon *rtk_bn_get_icon(rtk_widget *w);
+
+/* slider functions */
+void rtk_slider_set_range(rtk_widget *w, int vmin, int vmax);
+void rtk_slider_get_range(const rtk_widget *w, int *vmin, int *vmax);
+
+/* basic widgets */
+rtk_widget *rtk_create_window(rtk_widget *par, const char *title, int x, int y,
+               int width, int height, unsigned int flags);
+rtk_widget *rtk_create_button(rtk_widget *par, const char *str, rtk_callback cbfunc);
+rtk_widget *rtk_create_iconbutton(rtk_widget *par, rtk_icon *icon, rtk_callback cbfunc);
+rtk_widget *rtk_create_label(rtk_widget *par, const char *text);
+rtk_widget *rtk_create_checkbox(rtk_widget *par, const char *text, int chk, rtk_callback cbfunc);
+rtk_widget *rtk_create_textbox(rtk_widget *par, const char *text, rtk_callback cbfunc);
+rtk_widget *rtk_create_slider(rtk_widget *par, int vmin, int vmax, int val, rtk_callback cbfunc);
+rtk_widget *rtk_create_separator(rtk_widget *par);
+rtk_widget *rtk_create_drawbox(rtk_widget *par, int width, int height, rtk_callback cbfunc);
+
+/* compound widgets */
+rtk_widget *rtk_create_hbox(rtk_widget *par);
+rtk_widget *rtk_create_vbox(rtk_widget *par);
+rtk_widget *rtk_create_field(rtk_widget *par, const char *lbtext, rtk_callback cbfunc);
+
+/* icon functions */
+rtk_iconsheet *rtk_load_iconsheet(const char *fname);
+void rtk_free_iconsheet(rtk_iconsheet *is);
+
+rtk_icon *rtk_define_icon(rtk_iconsheet *is, const char *name, int x, int y, int w, int h);
+rtk_icon *rtk_lookup_icon(rtk_iconsheet *is, const char *name);
+
+
+void rtk_draw_widget(rtk_widget *w);
+
+/* screen functions */
+rtk_screen *rtk_create_screen(void);
+void rtk_free_screen(rtk_screen *scr);
+
+int rtk_add_window(rtk_screen *scr, rtk_widget *win);
+/* if win is not null, search only for win descendants */
+rtk_widget *rtk_find_widget_at(rtk_screen *scr, rtk_widget *win, int x, int y, unsigned int flags);
+
+int rtk_input_resize(rtk_screen *scr, int x, int y);
+int rtk_input_key(rtk_screen *scr, int key, int press);
+int rtk_input_mbutton(rtk_screen *scr, int bn, int press, int x, int y);
+int rtk_input_mmotion(rtk_screen *scr, int x, int y);
+
+void rtk_invalidate_screen(rtk_screen *scr);
+void rtk_draw_screen(rtk_screen *scr);
+
+/* misc */
+void rtk_null_rect(rtk_rect *r);
+void rtk_fix_rect(rtk_rect *r);
+void rtk_rect_union(rtk_rect *a, const rtk_rect *b);
+
+void rtk_calc_widget_rect(rtk_widget *w, rtk_rect *r);
+
+void rtk_dbg_showrect(rtk_widget *w, int show);
+
+#endif /* RTK_H_ */
diff --git a/menu/src/rtk_draw.c b/menu/src/rtk_draw.c
new file mode 100644 (file)
index 0000000..ad58bad
--- /dev/null
@@ -0,0 +1,702 @@
+#include <string.h>
+#include "app.h"
+#include "rtk.h"
+#include "rtk_impl.h"
+
+rtk_draw_ops rtk_gfx;
+#define gfx rtk_gfx
+
+static int fontheight;
+
+enum {
+       FRM_SOLID,
+       FRM_OUTSET,
+       FRM_INSET,
+       FRM_FILLBG = 0x100
+};
+
+static void widget_rect(rtk_widget *w, rtk_rect *rect);
+static void abs_widget_rect(rtk_widget *w, rtk_rect *rect);
+static void uicolor(uint32_t col, uint32_t lcol, uint32_t scol);
+static void draw_frame(rtk_rect *rect, int type, int sz);
+
+static void draw_window(rtk_widget *w);
+static void draw_label(rtk_widget *w);
+static void draw_button(rtk_widget *w);
+static void draw_checkbox(rtk_widget *w);
+static void draw_textbox(rtk_widget *w);
+static void draw_slider(rtk_widget *w);
+static void draw_separator(rtk_widget *w);
+static void draw_drawbox(rtk_widget *w);
+
+
+#define BEVELSZ                1
+#define PAD                    (w->pad)
+#define OFFS           (BEVELSZ + PAD)
+#define CHKBOXSZ       (BEVELSZ * 2 + 8)
+
+#define WINFRM_SZ      2
+#define WINFRM_TBAR    16
+
+void rtk_init_drawing(void)
+{
+       const char *s = "QI|9g/";
+       rtk_rect r;
+
+       gfx.textrect(s, &r);
+       fontheight = r.height;
+}
+
+
+void rtk_calc_widget_rect(rtk_widget *w, rtk_rect *rect)
+{
+       rtk_widget *child;
+       rtk_button *bn;
+       rtk_rect txrect = {0};
+
+       rect->x = w->x;
+       rect->y = w->y;
+       rect->width = w->width;
+       rect->height = w->height;
+
+       rtk_abs_pos(w, &w->absx, &w->absy);
+
+       if((w->flags & (AUTOWIDTH | AUTOHEIGHT)) == 0) {
+               return;
+       }
+
+       if(w->text) {
+               gfx.textrect(w->text, &txrect);
+       }
+
+       switch(w->type) {
+       case RTK_WIN:
+               if((w->flags & (AUTOWIDTH | AUTOHEIGHT))) {
+                       rtk_rect subrect;
+                       rtk_window *win = (rtk_window*)w;
+                       rtk_null_rect(&subrect);
+                       child = win->clist;
+                       while(child) {
+                               rtk_rect crect;
+                               widget_rect(child, &crect);
+                               rtk_rect_union(&subrect, &crect);
+                               child = child->next;
+                       }
+                       if(w->flags & AUTOWIDTH) {
+                               rect->width = subrect.width + PAD * 2;
+                       }
+                       if(w->flags & AUTOHEIGHT) {
+                               rect->height = subrect.height + PAD * 2;
+                       }
+               }
+               break;
+
+       case RTK_DRAWBOX:
+               if(w->flags & AUTOWIDTH) {
+                       rect->width = w->width;
+               }
+               if(w->flags & AUTOHEIGHT) {
+                       rect->height = w->height;
+               }
+               break;
+
+       case RTK_BUTTON:
+               bn = (rtk_button*)w;
+               if(bn->icon) {
+                       if(w->flags & AUTOWIDTH) {
+                               rect->width = bn->icon->width + OFFS * 2;
+                       }
+                       if(w->flags & AUTOHEIGHT) {
+                               rect->height = bn->icon->height + OFFS * 2;
+                       }
+               } else {
+                       if(w->flags & AUTOWIDTH) {
+                               rect->width = txrect.width + OFFS * 2;
+                       }
+                       if(w->flags & AUTOHEIGHT) {
+                               rect->height = txrect.height + OFFS * 2;
+                       }
+               }
+               break;
+
+       case RTK_CHECKBOX:
+               if(w->flags & AUTOWIDTH) {
+                       rect->width = txrect.width + CHKBOXSZ + OFFS * 2 + PAD;
+               }
+               if(w->flags & AUTOHEIGHT) {
+                       rect->height = txrect.height + OFFS * 2;
+               }
+               break;
+
+       case RTK_LABEL:
+               if(w->flags & AUTOWIDTH) {
+                       rect->width = txrect.width + PAD * 2;
+               }
+               if(w->flags & AUTOHEIGHT) {
+                       rect->height = txrect.height + PAD * 2;
+               }
+               break;
+
+       case RTK_TEXTBOX:
+               if(w->flags & AUTOHEIGHT) {
+                       if(rect->height < fontheight + OFFS * 2) {
+                               rect->height = fontheight + OFFS * 2;
+                       }
+               }
+               break;
+
+       case RTK_SEP:
+               if(w->par->layout == RTK_VBOX) {
+                       if(w->flags & AUTOWIDTH) {
+                               rect->width = w->par->width - PAD * 2;
+                       }
+                       if(w->flags & AUTOHEIGHT) {
+                               rect->height = PAD * 4 + BEVELSZ * 2;
+                       }
+               } else if(w->par->layout == RTK_HBOX) {
+                       if(w->flags & AUTOWIDTH) {
+                               rect->width = PAD * 4 + BEVELSZ * 2;
+                       }
+                       if(w->flags & AUTOHEIGHT) {
+                               rect->height = w->par->height - PAD * 2;
+                       }
+               } else {
+                       if(w->flags & AUTOWIDTH) {
+                               rect->width = 0;
+                       }
+                       if(w->flags & AUTOHEIGHT) {
+                               rect->height = 0;
+                       }
+               }
+               break;
+
+       default:
+               if(w->flags & AUTOWIDTH) {
+                       rect->width = 0;
+               }
+               if(w->flags & AUTOHEIGHT) {
+                       rect->height = 0;
+               }
+       }
+}
+
+void rtk_abs_pos(rtk_widget *w, int *xpos, int *ypos)
+{
+       int x, y, px, py;
+
+       x = w->x;
+       y = w->y;
+
+       if(w->par) {
+               rtk_abs_pos((rtk_widget*)w->par, &px, &py);
+               x += px;
+               y += py;
+       }
+
+       *xpos = x;
+       *ypos = y;
+}
+
+
+int rtk_hittest(rtk_widget *w, int x, int y)
+{
+       int x0, y0, x1, y1;
+
+       if(!(w->flags & VISIBLE)) {
+               return 0;
+       }
+
+       x0 = w->absx;
+       y0 = w->absy;
+       x1 = x0 + w->width;
+       y1 = y0 + w->height;
+
+       if(w->type == RTK_WIN && (w->flags & FRAME)) {
+               x0 -= WINFRM_SZ;
+               y0 -= WINFRM_SZ + WINFRM_TBAR;
+               x1 += WINFRM_SZ;
+               y1 += WINFRM_SZ;
+       }
+
+       if(x < x0 || y < y0) return 0;
+       if(x >= x1 || y >= y1) return 0;
+       return 1;
+}
+
+
+void rtk_invalfb(rtk_widget *w)
+{
+       rtk_rect rect;
+
+       rect.x = w->x;
+       rect.y = w->y;
+       rect.width = w->width;
+       rect.height = w->height;
+
+       rtk_abs_pos(w, &rect.x, &rect.y);
+
+       if(w->type == RTK_WIN && (w->flags & FRAME)) {
+               rect.x -= WINFRM_SZ;
+               rect.y -= WINFRM_SZ + WINFRM_TBAR;
+               rect.width += WINFRM_SZ * 2;
+               rect.height += WINFRM_SZ * 2 + WINFRM_TBAR;
+       }
+
+       app_invalidate(rect.x, rect.y, rect.width, rect.height);
+}
+
+void rtk_clearfb(rtk_widget *w)
+{
+       rtk_rect rect;
+
+       rect.x = w->x;
+       rect.y = w->y;
+       rect.width = w->width;
+       rect.height = w->height;
+
+       rtk_abs_pos(w, &rect.x, &rect.y);
+
+       if(w->type == RTK_WIN && (w->flags & FRAME)) {
+               rect.x -= WINFRM_SZ;
+               rect.y -= WINFRM_SZ + WINFRM_TBAR;
+               rect.width += WINFRM_SZ * 2;
+               rect.height += WINFRM_SZ * 2 + WINFRM_TBAR;
+       }
+
+       app_clear_rect(rect.x, rect.y, rect.width, rect.height);
+}
+
+static int need_relayout(rtk_widget *w)
+{
+       rtk_widget *c;
+
+       if(w->flags & GEOMCHG) {
+               return 1;
+       }
+
+       if(w->type == RTK_WIN) {
+               c = ((rtk_window*)w)->clist;
+               while(c) {
+                       if(need_relayout(c)) {
+                               return 1;
+                       }
+                       c = c->next;
+               }
+       }
+       return 0;
+}
+
+static void calc_layout(rtk_widget *w)
+{
+       int x, y, dx, dy;
+       rtk_widget *c;
+       rtk_window *win = (rtk_window*)w;
+       rtk_rect rect;
+
+       if(w->type == RTK_WIN) {
+               x = y = PAD;
+
+               c = win->clist;
+               while(c) {
+                       if(win->layout != RTK_NONE) {
+                               rtk_move(c, x, y);
+                               calc_layout(c);
+
+                               if(win->layout == RTK_VBOX) {
+                                       y += c->height + PAD * 2;
+                               } else if(win->layout == RTK_HBOX) {
+                                       x += c->width + PAD * 2;
+                               }
+                       } else {
+                               calc_layout(c);
+                       }
+
+                       c = c->next;
+               }
+       }
+
+       rtk_calc_widget_rect(w, &rect);
+       dx = rect.width - w->width;
+       dy = rect.height - w->height;
+       w->width = rect.width;
+       w->height = rect.height;
+
+       if((dx | dy) == 0) {
+               w->flags &= ~GEOMCHG;
+       }
+       rtk_invalidate(w);
+}
+
+static int calc_substr_width(const char *str, int start, int end)
+{
+       rtk_rect rect;
+       int len;
+       char *buf;
+
+       if(end <= start) {
+               return 0;
+       }
+       if(end <= 0) {
+               end = strlen(str);
+       }
+
+       len = end - start;
+       buf = alloca(len + 1);
+
+       memcpy(buf, str + start, len);
+       buf[len] = 0;
+
+       gfx.textrect(buf, &rect);
+       return rect.width;
+}
+
+void rtk_draw_widget(rtk_widget *w)
+{
+       int dirty;
+
+       if(!(w->flags & VISIBLE)) {
+               return;
+       }
+
+       if(need_relayout(w)) {
+               calc_layout(w);
+       }
+
+       dirty = w->flags & DIRTY;
+       if(!dirty && w->type != RTK_WIN) {
+               return;
+       }
+
+       switch(w->type) {
+       case RTK_WIN:
+               draw_window(w);
+               break;
+
+       case RTK_LABEL:
+               draw_label(w);
+               break;
+
+       case RTK_BUTTON:
+               draw_button(w);
+               break;
+
+       case RTK_CHECKBOX:
+               draw_checkbox(w);
+               break;
+
+       case RTK_TEXTBOX:
+               draw_textbox(w);
+               break;
+
+       case RTK_SLIDER:
+               draw_slider(w);
+               break;
+
+       case RTK_SEP:
+               draw_separator(w);
+               break;
+
+       case RTK_DRAWBOX:
+               draw_drawbox(w);
+               break;
+
+       default:
+               break;
+       }
+
+       if(w->drawcb) {
+               w->drawcb(w, w->drawcls);
+       }
+
+       if(w->flags & DBGRECT) {
+               rtk_rect r;
+               abs_widget_rect(w, &r);
+               uicolor(0xffff0000, 0xffff0000, 0xffff0000);
+               draw_frame(&r, FRM_SOLID, 1);
+       }
+
+       if(dirty) {
+               rtk_validate(w);
+               rtk_invalfb(w);
+       }
+}
+
+static void widget_rect(rtk_widget *w, rtk_rect *rect)
+{
+       rect->x = w->x;
+       rect->y = w->y;
+       rect->width = w->width;
+       rect->height = w->height;
+}
+
+static void abs_widget_rect(rtk_widget *w, rtk_rect *rect)
+{
+       rect->x = w->absx;
+       rect->y = w->absy;
+       rect->width = w->width;
+       rect->height = w->height;
+}
+
+#define COL_BG                                 0xff666666
+#define COL_BGHL                               0xff808080
+#define COL_LBEV                               0xffaaaaaa
+#define COL_SBEV                               0xff222222
+#define COL_TEXT                               0xff000000
+#define COL_WINFRM_FOCUS               0xff6688cc
+#define COL_WINFRM_LIT_FOCUS   0xff88aaff
+#define COL_WINFRM_SHAD_FOCUS  0xff224466
+#define COL_WINFRM                             0xff667788
+#define COL_WINFRM_LIT                 0xff8899aa
+#define COL_WINFRM_SHAD                        0xff224455
+#define COL_TBOX                               0xffeeccbb
+
+static void hline(int x, int y, int sz, uint32_t col)
+{
+       rtk_rect rect;
+       rect.x = x;
+       rect.y = y;
+       rect.width = sz;
+       rect.height = 1;
+       gfx.fill(&rect, col);
+}
+
+static void vline(int x, int y, int sz, uint32_t col)
+{
+       rtk_rect rect;
+       rect.x = x;
+       rect.y = y;
+       rect.width = 1;
+       rect.height = sz;
+       gfx.fill(&rect, col);
+}
+
+enum {UICOL_BG, UICOL_LBEV, UICOL_SBEV};
+static uint32_t uicol[3];
+
+static void uicolor(uint32_t col, uint32_t lcol, uint32_t scol)
+{
+       uicol[UICOL_BG] = col;
+       uicol[UICOL_LBEV] = lcol;
+       uicol[UICOL_SBEV] = scol;
+}
+
+static void draw_frame(rtk_rect *rect, int type, int sz)
+{
+       int i, tlcol, brcol, fillbg;
+       rtk_rect r = *rect;
+
+       fillbg = type & FRM_FILLBG;
+       type &= ~FRM_FILLBG;
+
+       switch(type) {
+       case FRM_OUTSET:
+               tlcol = uicol[UICOL_LBEV];
+               brcol = uicol[UICOL_SBEV];
+               break;
+       case FRM_INSET:
+               tlcol = uicol[UICOL_SBEV];
+               brcol = uicol[UICOL_LBEV];
+               break;
+       case FRM_SOLID:
+       default:
+               tlcol = brcol = uicol[UICOL_BG];
+       }
+
+       for(i=0; i<sz; i++) {
+               if(r.width < 2 || r.height < 2) break;
+
+               hline(r.x, r.y, r.width, tlcol);
+               vline(r.x, r.y + 1, r.height - 2, tlcol);
+               hline(r.x, r.y + r.height - 1, r.width, brcol);
+               vline(r.x + r.width - 1, r.y + 1, r.height - 2, brcol);
+
+               r.x++;
+               r.y++;
+               r.width -= 2;
+               r.height -= 2;
+       }
+
+       if(fillbg) {
+               gfx.fill(&r, uicol[UICOL_BG]);
+       }
+}
+
+static void draw_window(rtk_widget *w)
+{
+       rtk_rect rect, frmrect, tbrect;
+       rtk_widget *c;
+       rtk_window *win = (rtk_window*)w;
+       int win_dirty = w->flags & DIRTY;
+
+       if(win_dirty) {
+               abs_widget_rect(w, &rect);
+
+               if(w->flags & FRAME) {
+                       if(w->flags & FOCUS) {
+                               uicolor(COL_WINFRM_FOCUS, COL_WINFRM_LIT_FOCUS, COL_WINFRM_SHAD_FOCUS);
+                       } else {
+                               uicolor(COL_WINFRM, COL_WINFRM_LIT, COL_WINFRM_SHAD);
+                       }
+
+                       frmrect = rect;
+                       frmrect.width += WINFRM_SZ * 2;
+                       frmrect.height += WINFRM_SZ * 2 + WINFRM_TBAR;
+                       frmrect.x -= WINFRM_SZ;
+                       frmrect.y -= WINFRM_SZ + WINFRM_TBAR;
+
+                       tbrect.x = rect.x;
+                       tbrect.y = rect.y - WINFRM_TBAR;
+                       tbrect.width = rect.width;
+                       tbrect.height = WINFRM_TBAR;
+
+                       draw_frame(&frmrect, FRM_OUTSET, 1);
+                       frmrect.x++;
+                       frmrect.y++;
+                       frmrect.width -= 2;
+                       frmrect.height -= 2;
+                       draw_frame(&frmrect, FRM_INSET, 1);
+
+                       draw_frame(&tbrect, FRM_OUTSET | FRM_FILLBG, 1);
+                       tbrect.x++;
+                       tbrect.y++;
+                       tbrect.width -= 2;
+                       tbrect.height -= 2;
+
+                       gfx.drawtext(tbrect.x, tbrect.y + tbrect.height - 1, w->text);
+               }
+
+               gfx.fill(&rect, COL_BG);
+       }
+
+       c = win->clist;
+       while(c) {
+               if(win_dirty) {
+                       rtk_invalidate(c);
+               }
+               rtk_draw_widget(c);
+               c = c->next;
+       }
+}
+
+static void draw_label(rtk_widget *w)
+{
+       rtk_rect rect;
+
+       abs_widget_rect(w, &rect);
+
+       gfx.drawtext(rect.x + PAD, rect.y + rect.height - PAD, w->text);
+}
+
+static void draw_button(rtk_widget *w)
+{
+       int pressed;
+       rtk_rect rect;
+       rtk_button *bn = (rtk_button*)w;
+
+       abs_widget_rect(w, &rect);
+
+       if(bn->mode == RTK_TOGGLEBN) {
+               pressed = w->value;
+       } else {
+               pressed = w->flags & PRESS;
+       }
+
+       uicolor(w->flags & HOVER ? COL_BGHL : COL_BG, COL_LBEV, COL_SBEV);
+
+       draw_frame(&rect, (pressed ? FRM_INSET : FRM_OUTSET) | FRM_FILLBG, 1);
+       rect.x++;
+       rect.y++;
+       rect.width -= 2;
+       rect.height -= 2;
+
+       if(bn->icon) {
+               int offs = w->flags & PRESS ? PAD + 1 : PAD;
+               gfx.blit(rect.x + offs, rect.y + offs, bn->icon);
+       } else if(w->text) {
+               gfx.drawtext(rect.x + PAD, rect.y + rect.height - PAD, w->text);
+       }
+}
+
+static void draw_checkbox(rtk_widget *w)
+{
+}
+
+static void draw_textbox(rtk_widget *w)
+{
+       rtk_rect rect;
+       rtk_textbox *tb = (rtk_textbox*)w;
+       int curx = 0;
+
+       abs_widget_rect(w, &rect);
+
+       uicolor(COL_TBOX, COL_LBEV, COL_SBEV);
+
+       draw_frame(&rect, FRM_INSET | FRM_FILLBG, w->flags & FOCUS ? 2 : 1);
+
+       rect.x++;
+       rect.y++;
+       rect.width -= 2;
+       rect.height -= 2;
+
+       if(w->text) {
+               gfx.drawtext(rect.x + PAD, rect.y + rect.height - PAD, w->text);
+
+               if(w->flags & FOCUS) {
+                       curx = calc_substr_width(w->text, tb->scroll, tb->cursor);
+               }
+       }
+
+       /* cursor */
+       if(w->flags & FOCUS) {
+               int x = rect.x + PAD + curx - 1;
+               int y = rect.y + rect.height - PAD - fontheight;
+               vline(x, y, fontheight, 0xff000000);
+       }
+
+       rtk_invalfb(w);
+}
+
+static void draw_slider(rtk_widget *w)
+{
+}
+
+static void draw_separator(rtk_widget *w)
+{
+       rtk_window *win = (rtk_window*)w->par;
+       rtk_rect rect;
+
+       if(!win) return;
+
+       uicolor(COL_BG, COL_LBEV, COL_SBEV);
+
+       abs_widget_rect(w, &rect);
+
+       switch(win->layout) {
+       case RTK_VBOX:
+               rect.y += PAD * 2;
+               rect.height = 2;
+               break;
+
+       case RTK_HBOX:
+               rect.x += PAD * 2;
+               rect.width = 2;
+               break;
+
+       default:
+               break;
+       }
+
+       draw_frame(&rect, FRM_INSET, 1);
+}
+
+static void draw_drawbox(rtk_widget *w)
+{
+       if(!w->cbfunc) {
+               rtk_rect r;
+               abs_widget_rect(w, &r);
+               gfx.fill(&r, 0xff000000);
+               return;
+       }
+
+       w->cbfunc(w, w->cbcls);
+}
diff --git a/menu/src/rtk_impl.h b/menu/src/rtk_impl.h
new file mode 100644 (file)
index 0000000..aa9f6dc
--- /dev/null
@@ -0,0 +1,109 @@
+#ifndef RTK_IMPL_H_
+#define RTK_IMPL_H_
+
+#include <assert.h>
+#include "sizeint.h"
+#include "rtk.h"
+
+enum {
+       VISIBLE         = 0x0001,
+       ENABLED         = 0x0002,
+       HOVER           = 0x0010,
+       PRESS           = 0x0020,
+       FOCUS           = 0x0040,
+       GEOMCHG         = 0x0100,
+       DIRTY           = 0x0200,
+       CANFOCUS        = 0x0400,
+       AUTOWIDTH       = 0x1000,
+       AUTOHEIGHT      = 0x2000,
+
+       /* window flags */
+       FRAME           = RTK_WIN_FRAME << 16,
+       MOVABLE         = RTK_WIN_MOVABLE << 16,
+       RESIZABLE       = RTK_WIN_RESIZABLE << 16,
+
+       DBGRECT         = 0x40000000
+};
+
+#define WIDGET_COMMON \
+       int type; \
+       int x, y, width, height; \
+       int absx, absy; \
+       int pad; \
+       char *text; \
+       int value; \
+       unsigned int flags; \
+       struct rtk_window *par; \
+       rtk_widget *next; \
+       rtk_callback cbfunc, drawcb; \
+       void *cbcls, *drawcls; \
+       void *udata; \
+       rtk_key_callback on_key; \
+       rtk_mbutton_callback on_mbutton; \
+       rtk_click_callback on_click; \
+       rtk_drag_callback on_drag; \
+       rtk_drop_callback on_drop; \
+       rtk_screen *scr
+
+typedef struct rtk_widget {
+       WIDGET_COMMON;
+} rtk_widget;
+
+typedef struct rtk_window {
+       WIDGET_COMMON;
+       rtk_widget *clist, *ctail;
+       int layout;
+} rtk_window;
+
+typedef struct rtk_button {
+       WIDGET_COMMON;
+       int mode;
+       rtk_icon *icon;
+} rtk_button;
+
+typedef struct rtk_textbox {
+       WIDGET_COMMON;
+       int cursor, scroll;
+       int len, bufsz;
+} rtk_textbox;
+
+typedef struct rtk_slider {
+       WIDGET_COMMON;
+       int vmin, vmax;
+} rtk_slider;
+
+typedef struct rtk_iconsheet {
+       int width, height;
+       uint32_t *pixels;
+
+       struct rtk_icon *icons;
+} rtk_iconsheet;
+
+#define MAX_WINDOWS    64
+
+typedef struct rtk_screen {
+       rtk_widget *winlist[MAX_WINDOWS];
+       int num_win;
+       rtk_widget *hover, *focus;
+       rtk_window *focuswin;
+       int prev_mx, prev_my;
+
+       rtk_widget *press;                                      /* currently pressed widget */
+       int press_x, press_y;                           /* position of last mouse press */
+
+       rtk_widget *modal;              /* which window is currently modal (null if none) */
+} rtk_screen;
+
+#define RTK_ASSERT_TYPE(w, t)  assert(w->type == t)
+
+extern rtk_draw_ops rtk_gfx;
+
+void rtk_init_drawing(void);
+void rtk_calc_widget_rect(rtk_widget *w, rtk_rect *rect);
+void rtk_abs_pos(rtk_widget *w, int *xpos, int *ypos);
+int rtk_hittest(rtk_widget *w, int x, int y);
+void rtk_invalfb(rtk_widget *w);
+void rtk_clearfb(rtk_widget *w);
+
+
+#endif /* RTK_IMPL_H_ */
diff --git a/menu/src/sizeint.h b/menu/src/sizeint.h
new file mode 100644 (file)
index 0000000..1574854
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef SIZEINT_H_
+#define SIZEINT_H_
+
+/* for C99 or selected toolchain versions we can use stdint.h */
+#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199900) || \
+       (defined(_MSC_VER) && _MSC_VER >= 1600) || \
+       (defined(__WATCOMC__) && __WATCOMC__ >= 1200)
+#include <stdint.h>
+
+#elif defined(__DOS__) && defined(__WATCOMC__) && __WATCOMC__ < 1200
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef short int16_t;
+typedef unsigned short uint16_t;
+typedef long int32_t;
+typedef unsigned long uint32_t;
+typedef long intptr_t;
+typedef unsigned long uintptr_t;
+
+#elif defined(_MSC_VER) && (_MSC_VER < 1600)
+typedef signed __int8 int8_t;
+typedef unsigned __int8 uint8_t;
+typedef __int16 int16_t;
+typedef unsigned __int16 uint16_t;
+typedef __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef long intptr_t;
+typedef unsigned long uintptr_t;
+
+#else
+
+#ifdef __sgi
+#include <inttypes.h>
+#else
+#include <sys/types.h>
+#endif
+#endif
+
+
+#endif /* SIZEINT_H_ */
diff --git a/menu/src/timer.h b/menu/src/timer.h
new file mode 100644 (file)
index 0000000..395381c
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef TIMER_H_
+#define TIMER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* expects the required timer resolution in hertz
+ * if res_hz is 0, the current resolution is retained
+ */
+void init_timer(int res_hz);
+
+void reset_timer(void);
+unsigned long get_msec(void);
+
+void sleep_msec(unsigned long msec);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TIMER_H_ */