From 03cdbbddcd8ebd92a48cd48ed4cb82d5a5c3882a Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Wed, 9 Aug 2023 06:15:59 +0300 Subject: [PATCH] adding a bunch of code (vesa, keyb, mouse, etc) to the menu --- .gitignore | 6 + menu/Makefile | 1 + menu/Makefile.wat | 62 +++ menu/src/app.c | 221 +++++++++ menu/src/app.h | 89 ++++ menu/src/dos/cdpmi.c | 103 +++++ menu/src/dos/cdpmi.h | 39 ++ menu/src/dos/dosutil.h | 19 + menu/src/dos/drv.h | 25 + menu/src/dos/drv_s3.c | 8 + menu/src/dos/drv_vbe.c | 440 ++++++++++++++++++ menu/src/dos/drv_vga.c | 173 +++++++ menu/src/dos/keyb.c | 259 +++++++++++ menu/src/dos/keyb.h | 55 +++ menu/src/dos/main.c | 185 ++++++++ menu/src/dos/mouse.asm | 244 ++++++++++ menu/src/dos/mouse.h | 27 ++ menu/src/dos/pit8254.h | 34 ++ menu/src/dos/scancode.h | 61 +++ menu/src/dos/timer.c | 174 +++++++ menu/src/dos/vbe.h | 241 ++++++++++ menu/src/dos/vga.h | 93 ++++ menu/src/dos/vidsys.c | 292 ++++++++++++ menu/src/dos/vidsys.h | 80 ++++ menu/src/logger.c | 304 ++++++++++++ menu/src/logger.h | 25 + menu/src/menuscr.c | 59 +++ menu/src/rtk.c | 1169 +++++++++++++++++++++++++++++++++++++++++++++++ menu/src/rtk.h | 191 ++++++++ menu/src/rtk_draw.c | 702 ++++++++++++++++++++++++++++ menu/src/rtk_impl.h | 109 +++++ menu/src/sizeint.h | 40 ++ menu/src/timer.h | 22 + 33 files changed, 5552 insertions(+) create mode 100644 menu/Makefile.wat create mode 100644 menu/src/app.c create mode 100644 menu/src/app.h create mode 100644 menu/src/dos/cdpmi.c create mode 100644 menu/src/dos/cdpmi.h create mode 100644 menu/src/dos/dosutil.h create mode 100644 menu/src/dos/drv.h create mode 100644 menu/src/dos/drv_s3.c create mode 100644 menu/src/dos/drv_vbe.c create mode 100644 menu/src/dos/drv_vga.c create mode 100644 menu/src/dos/keyb.c create mode 100644 menu/src/dos/keyb.h create mode 100644 menu/src/dos/main.c create mode 100644 menu/src/dos/mouse.asm create mode 100644 menu/src/dos/mouse.h create mode 100644 menu/src/dos/pit8254.h create mode 100644 menu/src/dos/scancode.h create mode 100644 menu/src/dos/timer.c create mode 100644 menu/src/dos/vbe.h create mode 100644 menu/src/dos/vga.h create mode 100644 menu/src/dos/vidsys.c create mode 100644 menu/src/dos/vidsys.h create mode 100644 menu/src/logger.c create mode 100644 menu/src/logger.h create mode 100644 menu/src/menuscr.c create mode 100644 menu/src/rtk.c create mode 100644 menu/src/rtk.h create mode 100644 menu/src/rtk_draw.c create mode 100644 menu/src/rtk_impl.h create mode 100644 menu/src/sizeint.h create mode 100644 menu/src/timer.h diff --git a/.gitignore b/.gitignore index e45a17c..a8bcdd3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,11 @@ *.o +*.obj *.swp *.d *.pgm *.metrics +*.occ +*.lnk +*.err +fontconv +data/ diff --git a/menu/Makefile b/menu/Makefile index bb74e85..995762e 100644 --- a/menu/Makefile +++ b/menu/Makefile @@ -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 index 0000000..ff75920 --- /dev/null +++ b/menu/Makefile.wat @@ -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 index 0000000..bb7f73f --- /dev/null +++ b/menu/src/app.c @@ -0,0 +1,221 @@ +#include +#include +#include +#include +#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; iinit() == -1) { + return -1; + } + } + + time_msec = get_msec(); + + for(i=0; iname && 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; idestroy) { + 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; iheight; i++) { + for(j=0; jwidth; 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 index 0000000..b35de22 --- /dev/null +++ b/menu/src/app.h @@ -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 index 0000000..9ffb8c6 --- /dev/null +++ b/menu/src/dos/cdpmi.c @@ -0,0 +1,103 @@ +#include +#include +#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, ®s, ®s); + 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, ®s, ®s); +} + +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, ®s, ®s, &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, ®s, ®s); + 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, ®s, ®s); +} diff --git a/menu/src/dos/cdpmi.h b/menu/src/dos/cdpmi.h new file mode 100644 index 0000000..751f2d2 --- /dev/null +++ b/menu/src/dos/cdpmi.h @@ -0,0 +1,39 @@ +#ifndef CDPMI_H_ +#define CDPMI_H_ + +#include +#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 index 0000000..4e4c8c3 --- /dev/null +++ b/menu/src/dos/dosutil.h @@ -0,0 +1,19 @@ +#ifndef DOSUTIL_H_ +#define DOSUTIL_H_ + +#include +#include + +#ifdef __DJGPP__ +#include + +#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 index 0000000..172f146 --- /dev/null +++ b/menu/src/dos/drv.h @@ -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 index 0000000..77919e7 --- /dev/null +++ b/menu/src/dos/drv_s3.c @@ -0,0 +1,8 @@ +#include +#include +#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 index 0000000..ae36be8 --- /dev/null +++ b/menu/src/dos/drv_vbe.c @@ -0,0 +1,440 @@ +#include +#include +#include +#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, ®s); + 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; ilfb) { + mode |= VBE_MODE_LFB; + } + +retry: + regs.eax = 0x4f02; + regs.ebx = mode; + dpmi_rmint(0x10, ®s); + + 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, ®s); + 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, ®s); + 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; iwidth, 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; ipitch; + 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; iheight; 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 index 0000000..ed4d237 --- /dev/null +++ b/menu/src/dos/drv_vga.c @@ -0,0 +1,173 @@ +#include +#include +#include +#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; ir >> 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; ir = 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, ®s, ®s); + return 0; +} + +static int curmode(void) +{ + union REGS regs = {0}; + regs.w.ax = 0xf00; + int386(0x10, ®s, ®s); + 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 + +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 +*/ +#define KEYB_C_ + +#include +#include +#include +#include +#include +#include + +#ifdef __WATCOMC__ +#include +#endif +#ifdef __DJGPP__ +#include +#include +#include +#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 index 0000000..46b6aa4 --- /dev/null +++ b/menu/src/dos/keyb.h @@ -0,0 +1,55 @@ +/* +DOS interrupt-based keyboard driver. +Copyright (C) 2013-2023 John Tsiombikas + +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 +*/ +#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 index 0000000..a8e9c1b --- /dev/null +++ b/menu/src/dos/main.c @@ -0,0 +1,185 @@ +#include +#include +#include +#include +#include +#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 index 0000000..458c68f --- /dev/null +++ b/menu/src/dos/mouse.asm @@ -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 index 0000000..aa46df3 --- /dev/null +++ b/menu/src/dos/mouse.h @@ -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 index 0000000..5d45f55 --- /dev/null +++ b/menu/src/dos/pit8254.h @@ -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 index 0000000..39f3033 --- /dev/null +++ b/menu/src/dos/scancode.h @@ -0,0 +1,61 @@ +/* +colcycle - color cycling image viewer +Copyright (C) 2016 John Tsiombikas + +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 . +*/ +#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 index 0000000..b03b115 --- /dev/null +++ b/menu/src/dos/timer.c @@ -0,0 +1,174 @@ +#include +#include +#include +#include +#include + +#ifdef __WATCOMC__ +#include +#endif + +#ifdef __DJGPP__ +#include +#include +#include +#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 index 0000000..42417af --- /dev/null +++ b/menu/src/dos/vbe.h @@ -0,0 +1,241 @@ +#ifndef VBE_H_ +#define VBE_H_ + +#include +#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 index 0000000..c15fe17 --- /dev/null +++ b/menu/src/dos/vga.h @@ -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 index 0000000..9ee925e --- /dev/null +++ b/menu/src/dos/vidsys.c @@ -0,0 +1,292 @@ +#include +#include +#include +#include +#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; iops->init(); + + for(j=0; jnum_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; idrv->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; iops->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; imodeno == 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; iwidth == xsz && modes[i]->height == ysz && modes[i]->bpp == bpp) { + return modes[i]->modeno; + } + } + + /* try fuzzy bpp matching */ + for(i=0; iwidth == 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; imodeno == 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; iheight; i++) { + for(j=0; jwidth; 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 index 0000000..9132306 --- /dev/null +++ b/menu/src/dos/vidsys.h @@ -0,0 +1,80 @@ +#ifndef VIDSYS_VIDEO_H_ +#define VIDSYS_VIDEO_H_ + +#include +#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 index 0000000..667ac73 --- /dev/null +++ b/menu/src/logger.c @@ -0,0 +1,304 @@ +#include +#include +#include +#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= 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= 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 + +#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 index 0000000..e3bf5dc --- /dev/null +++ b/menu/src/logger.h @@ -0,0 +1,25 @@ +#ifndef LOGGER_H_ +#define LOGGER_H_ + +#include +#include + +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 index 0000000..bcf28c9 --- /dev/null +++ b/menu/src/menuscr.c @@ -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 index 0000000..5c3704b --- /dev/null +++ b/menu/src/rtk.c @@ -0,0 +1,1169 @@ +#include +#include +#include +#include +#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; inum_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; inum_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; inum_win; i++) { + rtk_invalidate(scr->winlist[i]); + } +} + +void rtk_draw_screen(rtk_screen *scr) +{ + int i; + for(i=0; inum_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 index 0000000..c06e176 --- /dev/null +++ b/menu/src/rtk.h @@ -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 index 0000000..ad58bad --- /dev/null +++ b/menu/src/rtk_draw.c @@ -0,0 +1,702 @@ +#include +#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; iflags & 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 index 0000000..aa9f6dc --- /dev/null +++ b/menu/src/rtk_impl.h @@ -0,0 +1,109 @@ +#ifndef RTK_IMPL_H_ +#define RTK_IMPL_H_ + +#include +#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 index 0000000..1574854 --- /dev/null +++ b/menu/src/sizeint.h @@ -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 + +#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 +#else +#include +#endif +#endif + + +#endif /* SIZEINT_H_ */ diff --git a/menu/src/timer.h b/menu/src/timer.h new file mode 100644 index 0000000..395381c --- /dev/null +++ b/menu/src/timer.h @@ -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_ */ -- 1.7.10.4