*.o
+*.obj
*.swp
*.d
*.pgm
*.metrics
+*.occ
+*.lnk
+*.err
+fontconv
+data/
#opt = -O3
dbg = -g
warn = -pedantic -Wall
+inc = -Isrc/dos
CC = $(TOOLPREFIX)gcc
AR = $(TOOLPREFIX)ar
--- /dev/null
+!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
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "app.h"
+#include "timer.h"
+#include "rtk.h"
+
+int mouse_x, mouse_y, mouse_state[3];
+unsigned int modkeys;
+int scr_width, scr_height;
+int fullscr;
+
+long time_msec;
+
+struct app_screen *cur_scr;
+
+unsigned char *framebuf;
+
+/* available screens */
+#define MAX_SCREENS 8
+static struct app_screen *screens[MAX_SCREENS];
+static int num_screens;
+
+
+int app_init(void)
+{
+ int i;
+ char *start_scr_name;
+ static rtk_draw_ops guigfx = {gui_fill, 0, gui_drawtext, gui_textrect};
+
+ rtk_setup(&guigfx);
+
+ /* initialize screens */
+ screens[num_screens++] = &menuscr;
+
+ start_scr_name = getenv("START_SCREEN");
+
+ for(i=0; i<num_screens; i++) {
+ if(screens[i]->init() == -1) {
+ return -1;
+ }
+ }
+
+ time_msec = get_msec();
+
+ for(i=0; i<num_screens; i++) {
+ if(screens[i]->name && start_scr_name && strcmp(screens[i]->name, start_scr_name) == 0) {
+ app_chscr(screens[i]);
+ break;
+ }
+ }
+ if(!cur_scr) {
+ app_chscr(&menuscr);
+ }
+
+ return 0;
+}
+
+void app_shutdown(void)
+{
+ int i;
+
+ for(i=0; i<num_screens; i++) {
+ if(screens[i]->destroy) {
+ screens[i]->destroy();
+ }
+ }
+
+ cleanup_logger();
+}
+
+void app_display(void)
+{
+ time_msec = get_msec();
+
+ cur_scr->display();
+}
+
+void app_reshape(int x, int y)
+{
+ int numpix = x * y;
+ int prev_numpix = scr_width * scr_height;
+
+ if(!framebuf || numpix > prev_numpix) {
+ void *tmp;
+ if(!(tmp = realloc(framebuf, numpix * sizeof *framebuf))) {
+ errormsg("failed to resize framebuffer to %dx%d\n", x, y);
+ return;
+ }
+ framebuf = tmp;
+ }
+
+ scr_width = x;
+ scr_height = y;
+
+ if(cur_scr && cur_scr->reshape) {
+ cur_scr->reshape(x, y);
+ }
+
+ app_invalidate(0, 0, 0, 0);
+}
+
+void app_keyboard(int key, int press)
+{
+ long msec;
+ static long prev_esc;
+
+ if(press) {
+ switch(key) {
+#ifdef DBG_ESCQUIT
+ case 27:
+ msec = get_msec();
+ if(msec - prev_esc < 1000) {
+ app_quit();
+ return;
+ }
+ prev_esc = msec;
+ break;
+#endif
+
+ case 'q':
+ if(modkeys & KEY_MOD_CTRL) {
+ app_quit();
+ return;
+ }
+ break;
+
+ case '\n':
+ case '\r':
+ if(modkeys & KEY_MOD_ALT) {
+ case KEY_F11:
+ app_fullscreen(-1);
+ return;
+ }
+ break;
+ }
+ }
+
+ if(cur_scr && cur_scr->keyboard) {
+ cur_scr->keyboard(key, press);
+ }
+}
+
+void app_mouse(int bn, int st, int x, int y)
+{
+ mouse_x = x;
+ mouse_y = y;
+ if(bn < 3) {
+ mouse_state[bn] = st;
+ }
+
+ if(cur_scr && cur_scr->mouse) {
+ cur_scr->mouse(bn, st, x, y);
+ }
+}
+
+void app_motion(int x, int y)
+{
+ if(cur_scr && cur_scr->motion) {
+ cur_scr->motion(x, y);
+ }
+ mouse_x = x;
+ mouse_y = y;
+}
+
+void app_chscr(struct app_screen *scr)
+{
+ struct app_screen *prev = cur_scr;
+
+ if(!scr) return;
+
+ if(scr->start && scr->start() == -1) {
+ return;
+ }
+ if(scr->reshape) {
+ scr->reshape(scr_width, scr_height);
+ }
+
+ if(prev && prev->stop) {
+ prev->stop();
+ }
+ cur_scr = scr;
+}
+
+void gui_fill(rtk_rect *rect, int color)
+{
+ int i, j;
+ unsigned char *fb;
+
+ if(rect->x < 0) {
+ rect->width += rect->x;
+ rect->x = 0;
+ }
+ if(rect->y < 0) {
+ rect->height += rect->y;
+ rect->y = 0;
+ }
+ if(rect->x + rect->width >= scr_width) {
+ rect->width = scr_width - rect->x;
+ }
+ if(rect->y + rect->height >= scr_height) {
+ rect->height = scr_height - rect->y;
+ }
+
+ fb = framebuf + rect->y * scr_width + rect->x;
+ for(i=0; i<rect->height; i++) {
+ for(j=0; j<rect->width; j++) {
+ fb[j] = color;
+ }
+ fb += scr_width;
+ }
+}
+
+void gui_drawtext(int x, int y, const char *str)
+{
+}
+
+void gui_textrect(const char *str, rtk_rect *rect)
+{
+}
--- /dev/null
+#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_ */
--- /dev/null
+#include <stdio.h>
+#include <i86.h>
+#include "cdpmi.h"
+#include "logger.h"
+
+#define LOWBUF_SIZE 8192
+#define RMSTACK_SIZE 4096
+
+static char *lowbuf, *lowfree;
+static uint16_t lowbuf_sel, lowbuf_seg;
+
+int dpmi_init(void)
+{
+ if(!(lowbuf_seg = dpmi_alloc(LOWBUF_SIZE >> 4, &lowbuf_sel))) {
+ errormsg("DPMI init failed to allocate low memory buffer\n");
+ return -1;
+ }
+ lowbuf = (char*)((intptr_t)lowbuf_seg << 4);
+ lowfree = lowbuf + RMSTACK_SIZE;
+ return 0;
+}
+
+void dpmi_cleanup(void)
+{
+ if(!lowbuf_sel) return;
+ dpmi_free(lowbuf_sel);
+ lowbuf = 0;
+ lowbuf_sel = 0;
+ lowbuf_seg = 0;
+}
+
+void *dpmi_lowbuf(void)
+{
+ return lowfree;
+}
+
+uint16_t dpmi_alloc(unsigned int par, uint16_t *sel)
+{
+ union REGS regs = {0};
+
+ regs.w.ax = 0x100;
+ regs.w.bx = par;
+ int386(0x31, ®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);
+}
--- /dev/null
+#ifndef CDPMI_H_
+#define CDPMI_H_
+
+#include <stdlib.h>
+#include "sizeint.h"
+
+#pragma pack (push, 1)
+struct dpmi_regs {
+ uint32_t edi, esi, ebp;
+ uint32_t reserved;
+ uint32_t ebx, edx, ecx, eax;
+ uint16_t flags;
+ uint16_t es, ds, fs, gs;
+ uint16_t ip, cs, sp, ss;
+};
+#pragma pack (pop)
+
+enum {
+ FLAGS_CF = 0x000001,
+ FLAGS_PF = 0x000004,
+ FLAGS_ZF = 0x000040,
+ FLAGS_SF = 0x000080,
+ FLAGS_IF = 0x000020,
+ FLAGS_DF = 0x000040,
+ FLAGS_VM = 0x020000,
+ FLAGS_ID = 0x200000,
+};
+
+int dpmi_init(void);
+void dpmi_cleanup(void);
+void *dpmi_lowbuf(void);
+
+uint16_t dpmi_alloc(unsigned int par, uint16_t *sel);
+void dpmi_free(uint16_t sel);
+int dpmi_rmint(int inum, struct dpmi_regs *regs);
+void *dpmi_mmap(uint32_t phys_addr, unsigned int size);
+void dpmi_munmap(void *addr);
+
+#endif /* CDPMI_H_ */
--- /dev/null
+#ifndef DOSUTIL_H_
+#define DOSUTIL_H_
+
+#include <dos.h>
+#include <conio.h>
+
+#ifdef __DJGPP__
+#include <pc.h>
+
+#define outp(p, v) outportb(p, v)
+#define outpw(p, v) outportw(p, v)
+#define outpd(p, v) outportl(p, v)
+
+#define inp(p) inportb(p)
+#define inpw(p) inportw(p)
+#define inpd(p) inportl(p)
+#endif
+
+#endif /* DOSUTIL_H_ */
--- /dev/null
+#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_ */
--- /dev/null
+#include <conio.h>
+#include <i86.h>
+#include "drv.h"
+#include "logger.h"
+
+void vid_register_s3(void)
+{
+}
--- /dev/null
+#include <string.h>
+#include <conio.h>
+#include <i86.h>
+#include "vidsys.h"
+#include "drv.h"
+#include "vbe.h"
+#include "vga.h"
+#include "cdpmi.h"
+#include "logger.h"
+
+#define farptr_to_linear(rmaddr) \
+ ((((intptr_t)(rmaddr) >> 12) & 0xffff0) + ((intptr_t)(rmaddr) & 0xffff))
+
+static int init(void);
+static void cleanup(void);
+static struct vid_modeinfo *find_mode(int mode);
+static int setmode(int mode);
+static int getmode(void);
+static const char *memsize_str(long sz);
+static int get_mode_info(int mode, struct vbe_mode_info *mi);
+static int conv_vbeinfo(int mode, struct vid_modeinfo *mi, struct vbe_mode_info *vbemi);
+static unsigned int calc_mask(int nbits, int pos);
+static void print_mode_info(int mode, struct vid_modeinfo *mi);
+
+static void pack(uint32_t *pix, int r, int g, int b);
+static void unpack(uint32_t pix, int *r, int *g, int *b);
+static void clear(uint32_t color);
+static void blit_lfb(int x, int y, int w, int h, void *fb, int pitch);
+static void blit_banked(int x, int y, int w, int h, void *fb, int pitch);
+static void blitfb_lfb(void *fb, int pitch);
+static void blitfb_banked(void *fb, int pitch);
+static void flip(int vsync);
+
+static struct vid_driver drv;
+static struct vid_drvops drvops = {init, cleanup, setmode, getmode};
+static unsigned int vbe_ver;
+
+static int cur_mode;
+static struct vid_modeinfo *cur_mi;
+static int cur_pgsize;
+
+
+void vid_register_vbe(void)
+{
+ drv.name = "vbe";
+ drv.prio = 2;
+ drv.ops = &drvops;
+
+ vid_drvlist[vid_numdrv++] = &drv;
+}
+
+
+static int init(void)
+{
+ struct dpmi_regs regs = {0};
+ struct vbe_info *vbe;
+ struct vbe_mode_info vbemi;
+ unsigned short bufseg;
+ uint16_t *vbe_modelist, *modelist;
+ int i, count;
+ struct vid_modeinfo modeinf;
+
+ cur_mode = -1;
+ cur_mi = 0;
+
+ vbe = dpmi_lowbuf();
+ bufseg = (intptr_t)vbe >> 4;
+
+ /* call VBE function 00 (get controller information) */
+ memcpy(vbe->sig, "VBE2", 4); /* denote we want VBE 2.0 info */
+ regs.eax = 0x4f00;
+ regs.es = bufseg;
+ dpmi_rmint(0x10, ®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; i<count; i++) {
+ modelist[i] = vbe_modelist[i];
+ }
+
+ if(!(drv.modes = malloc(count * sizeof *drv.modes))) {
+ errormsg("failed to allocate mode list\n");
+ free(modelist);
+ return -1;
+ }
+
+ drv.num_modes = 0;
+ for(i=0; i<count; i++) {
+ if(get_mode_info(modelist[i], &vbemi) == -1) {
+ continue;
+ }
+ if(conv_vbeinfo(modelist[i], drv.modes + drv.num_modes, &vbemi) == -1) {
+ continue;
+ }
+ drv.num_modes++;
+ }
+
+ free(modelist);
+ return 0;
+}
+
+static void cleanup(void)
+{
+ free(drv.modes);
+ drv.modes = 0;
+ drv.num_modes = 0;
+}
+
+static struct vid_modeinfo *find_mode(int mode)
+{
+ int i;
+ for(i=0; i<drv.num_modes; i++) {
+ if(drv.modes[i].modeno == mode) {
+ return drv.modes + i;
+ }
+ }
+ return 0;
+}
+
+static int setmode(int mode)
+{
+ struct vid_modeinfo *minf;
+ struct dpmi_regs regs = {0};
+
+ if((minf = find_mode(mode)) && minf->lfb) {
+ mode |= VBE_MODE_LFB;
+ }
+
+retry:
+ regs.eax = 0x4f02;
+ regs.ebx = mode;
+ dpmi_rmint(0x10, ®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; i<nbits; i++) {
+ mask = (mask << 1) | 1;
+ }
+ return mask << pos;
+}
+
+static void print_mode_info(int mode, struct vid_modeinfo *mi)
+{
+ infomsg("VBE mode %04x\n", mode);
+ infomsg(" %dx%d %d bpp (%d colors)\n", mi->width, mi->height,
+ mi->bpp, mi->ncolors);
+ infomsg(" pitch: %d bytes, %d vmem pages\n", mi->pitch, mi->pages);
+
+ if(mi->bpp > 8) {
+ infomsg(" RGB mask %06x %06x %06x (pos: %d %d %d)\n", (unsigned int)mi->rmask,
+ (unsigned int)mi->gmask, (unsigned int)mi->bmask, mi->rshift,
+ mi->gshift, mi->bshift);
+ }
+
+ if(mode & VBE_MODE_LFB) {
+ infomsg(" LFB address %xh, size: %d\n", (unsigned int)mi->vmem_addr,
+ (int)mi->vmem_size);
+ } else {
+ infomsg(" banked window %d kb, granularity: %d kb, step: %d\n", mi->win_size,
+ mi->win_gran, mi->win_step);
+ }
+}
+
+
+static void pack(uint32_t *pix, int r, int g, int b)
+{
+ *pix = (((uint32_t)r << cur_mi->rshift) & cur_mi->rmask) |
+ (((uint32_t)g << cur_mi->gshift) & cur_mi->gmask) |
+ (((uint32_t)b << cur_mi->bshift) & cur_mi->bmask);
+}
+
+static void unpack(uint32_t pix, int *r, int *g, int *b)
+{
+ *r = (pix & cur_mi->rmask) >> cur_mi->rshift;
+ *g = (pix & cur_mi->gmask) >> cur_mi->gshift;
+ *b = (pix & cur_mi->bmask) >> cur_mi->bshift;
+}
+
+static void clear(uint32_t color)
+{
+}
+
+static void blit_lfb(int x, int y, int w, int h, void *fb, int pitch)
+{
+ int i, pixsz, spansz;
+ unsigned char *dest, *src;
+
+ /*dbgmsg("blit: %d,%d (%dx%d)\n", x, y, w, h);*/
+
+ pixsz = (cur_mi->bpp + 7) >> 3;
+ spansz = w * pixsz;
+
+ dest = (char*)vid_vmem + cur_mi->pitch * y + x * pixsz;
+ src = fb;
+
+ for(i=0; i<h; i++) {
+ memcpy(dest, src, spansz);
+ dest += cur_mi->pitch;
+ src += pitch;
+ }
+}
+
+static void blit_banked(int x, int y, int w, int h, void *fb, int pitch)
+{
+ abort();
+}
+
+static void blitfb_lfb(void *fb, int pitch)
+{
+ int i, pixsz, spansz;
+ unsigned char *dest, *src;
+
+ pixsz = (cur_mi->bpp + 7) >> 3;
+ spansz = cur_mi->width * pixsz;
+
+ dest = vid_vmem;
+ src = fb;
+
+ for(i=0; i<cur_mi->height; i++) {
+ memcpy(dest, src, spansz);
+ dest += cur_mi->pitch;
+ src += pitch;
+ }
+}
+
+static void blitfb_banked(void *fb, int pitch)
+{
+ int sz, offs, pending, winsz;
+ unsigned char *pptr = fb;
+
+ winsz = cur_mi->win_size << 10;
+
+ /* assume initial window offset at 0 */
+ offs = 0;
+ pending = cur_pgsize;
+ while(pending > 0) {
+ sz = pending > winsz ? winsz : pending;
+ memcpy((void*)0xa0000, pptr, sz);
+ pptr += sz;
+ pending -= sz;
+ offs += cur_mi->win_step;
+ vid_setwin(0, offs);
+ }
+ vid_setwin(0, 0);
+}
+
+static void flip(int vsync)
+{
+ /* TODO */
+}
--- /dev/null
+#include <string.h>
+#include <conio.h>
+#include <i86.h>
+#include "vidsys.h"
+#include "drv.h"
+#include "vga.h"
+#include "logger.h"
+
+static int init(void);
+static void cleanup(void);
+static int setmode(int mode);
+static int curmode(void);
+
+static void setpal4(int idx, int count, const struct vid_color *col);
+static void getpal4(int idx, int count, struct vid_color *col);
+static void clear4(uint32_t color);
+static void blitfb4(void *fb, int pitch);
+static void fill4(int x, int y, int w, int h, uint32_t color);
+
+static void clear8(uint32_t color);
+static void blitfb8(void *fb, int pitch);
+static void fill8(int x, int y, int w, int h, uint32_t color);
+
+
+static struct vid_driver drv;
+static struct vid_drvops drvops = {init, cleanup, setmode, curmode};
+static struct vid_modeinfo modes[] = {
+ {3, 80, 25, 4},
+ {0x12, 640, 480, 4},
+ {0x13, 320, 200, 8}
+};
+
+static struct vid_gfxops gfxops_mode12h = {
+ 0, 0, setpal4, getpal4, vid_vsync, clear4, blitfb4, 0, fill4 };
+static struct vid_gfxops gfxops_mode13h = {
+ 0, 0, vga_setpal, vga_getpal, vid_vsync, clear8, blitfb8, 0, fill8 };
+
+void vid_register_vga(void)
+{
+ int i;
+
+ drv.name = "vga";
+ drv.prio = 1;
+ drv.ops = &drvops;
+ drv.modes = modes;
+ drv.num_modes = sizeof modes / sizeof *modes;
+
+ for(i=0; i<drv.num_modes; i++) {
+ modes[i].drv = &drv;
+ modes[i].vmem_addr = 0xa0000;
+
+ switch(modes[i].modeno) {
+ case 0x3:
+ modes[i].vmem_addr = 0xb8000;
+ break;
+
+ case 0x13:
+ modes[i].ops = gfxops_mode13h;
+ break;
+
+ case 0x12:
+ modes[i].ops = gfxops_mode12h;
+ break;
+ }
+ }
+
+ vid_drvlist[vid_numdrv++] = &drv;
+}
+
+void vid_vsync(void)
+{
+ while(inp(VGA_STAT1_PORT) & 8);
+ while((inp(VGA_STAT1_PORT) & 8) == 0);
+}
+
+void vga_setpal(int idx, int count, const struct vid_color *col)
+{
+ int i;
+ outp(VGA_DAC_WADDR_PORT, idx);
+ for(i=0; i<count; i++) {
+ outp(VGA_DAC_DATA_PORT, col->r >> 2);
+ outp(VGA_DAC_DATA_PORT, col->g >> 2);
+ outp(VGA_DAC_DATA_PORT, col->b >> 2);
+ col++;
+ }
+}
+
+void vga_getpal(int idx, int count, struct vid_color *col)
+{
+ int i;
+ outp(VGA_DAC_RADDR_PORT, idx);
+ for(i=0; i<count; i++) {
+ col->r = inp(VGA_DAC_DATA_PORT) << 2;
+ col->g = inp(VGA_DAC_DATA_PORT) << 2;
+ col->b = inp(VGA_DAC_DATA_PORT) << 2;
+ col++;
+ }
+}
+
+
+static int init(void)
+{
+ return 0;
+}
+
+static void cleanup(void)
+{
+}
+
+static int setmode(int mode)
+{
+ union REGS regs = {0};
+ regs.w.ax = mode;
+ int386(0x10, ®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<h; i++) {
+ memset(fbptr, color, w);
+ fbptr += 320;
+ }
+}
--- /dev/null
+/*
+DOS interrupt-based keyboard driver.
+Copyright (C) 2013-2023 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the program. If not, see <http://www.gnu.org/licenses/>
+*/
+#define KEYB_C_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <conio.h>
+#include <dos.h>
+
+#ifdef __WATCOMC__
+#include <i86.h>
+#endif
+#ifdef __DJGPP__
+#include <dpmi.h>
+#include <go32.h>
+#include <pc.h>
+#endif
+
+#include "keyb.h"
+#include "scancode.h"
+#include "sizeint.h"
+#include "dosutil.h"
+
+#define KEY_INTR 0x9
+#define KEY_PORT 0x60
+
+#define PIC1_CMD_PORT 0x20
+#define OCW2_EOI (1 << 5)
+
+#ifdef __WATCOMC__
+#define INTERRUPT __interrupt __far
+
+#define DONE_INIT (prev_handler)
+static void (INTERRUPT *prev_handler)();
+#endif
+
+#ifdef __DJGPP__
+#define INTERRUPT
+
+#define DONE_INIT prev_intr.pm_offset
+static _go32_dpmi_seginfo intr, prev_intr;
+#endif
+
+static void INTERRUPT kbintr();
+
+#define BUFSIZE 64
+static int buffer[BUFSIZE];
+static int buf_ridx, buf_widx;
+static int last_key;
+
+static unsigned int num_pressed;
+static unsigned char keystate[512];
+
+#define ADVANCE(x) ((x) = ((x) + 1) & (BUFSIZE - 1))
+
+void kb_init(void)
+{
+ if(DONE_INIT) {
+ errormsg("keyboard driver already initialized!\n");
+ return;
+ }
+
+ buf_ridx = buf_widx = 0;
+ last_key = -1;
+
+ memset(keystate, 0, sizeof keystate);
+ num_pressed = 0;
+
+ /* set our interrupt handler */
+ _disable();
+#ifdef __WATCOMC__
+ prev_handler = _dos_getvect(KEY_INTR);
+ _dos_setvect(KEY_INTR, kbintr);
+#endif
+#ifdef __DJGPP__
+ _go32_dpmi_get_protected_mode_interrupt_vector(KEY_INTR, &prev_intr);
+ intr.pm_offset = (intptr_t)kbintr;
+ intr.pm_selector = _go32_my_cs();
+ _go32_dpmi_allocate_iret_wrapper(&intr);
+ _go32_dpmi_set_protected_mode_interrupt_vector(KEY_INTR, &intr);
+#endif
+ _enable();
+}
+
+void kb_shutdown(void)
+{
+ if(!DONE_INIT) {
+ return;
+ }
+
+ /* restore the original interrupt handler */
+ _disable();
+#ifdef __WATCOMC__
+ _dos_setvect(KEY_INTR, prev_handler);
+#endif
+#ifdef __DJGPP__
+ _go32_dpmi_set_protected_mode_interrupt_vector(KEY_INTR, &prev_intr);
+ _go32_dpmi_free_iret_wrapper(&intr);
+#endif
+ _enable();
+}
+
+int kb_isdown(int key)
+{
+ switch(key) {
+ case KEY_ANY:
+ return num_pressed;
+
+ case KEY_ALT:
+ return keystate[KEY_LALT] + keystate[KEY_RALT];
+
+ case KEY_CTRL:
+ return keystate[KEY_LCTRL] + keystate[KEY_RCTRL];
+
+ case KEY_SHIFT:
+ return keystate[KEY_LSHIFT] + keystate[KEY_RSHIFT];
+ }
+
+ if(isalpha(key)) {
+ key = tolower(key);
+ }
+ return keystate[key];
+}
+
+#ifdef __WATCOMC__
+void halt(void);
+#pragma aux halt = \
+ "sti" \
+ "hlt";
+#endif
+
+#ifdef __DJGPP__
+#define halt() asm volatile("sti\n\thlt\n\t")
+#endif
+
+void kb_wait(void)
+{
+ int key;
+ while((key = kb_getkey()) == -1) {
+#ifdef USE_HLT
+ /* put the processor to sleep while waiting for keypresses, but first
+ * make sure interrupts are enabled, or we'll sleep forever
+ */
+ halt();
+#endif
+ }
+ kb_putback(key);
+}
+
+int kb_getkey(void)
+{
+ int res;
+
+ if(buffer) {
+ if(buf_ridx == buf_widx) {
+ return -1;
+ }
+ res = buffer[buf_ridx];
+ ADVANCE(buf_ridx);
+ } else {
+ res = last_key;
+ last_key = -1;
+ }
+ return res;
+}
+
+void kb_putback(int key)
+{
+ if(buffer) {
+ /* go back a place */
+ if(--buf_ridx < 0) {
+ buf_ridx += BUFSIZE;
+ }
+
+ /* if the write end hasn't caught up with us, go back one place
+ * and put it there, otherwise just overwrite the oldest key which
+ * is right where we were.
+ */
+ if(buf_ridx == buf_widx) {
+ ADVANCE(buf_ridx);
+ }
+
+ buffer[buf_ridx] = key;
+ } else {
+ last_key = key;
+ }
+}
+
+static void INTERRUPT kbintr()
+{
+ unsigned char code;
+ int key, c, press;
+ static int ext;
+
+ code = inp(KEY_PORT);
+
+ if(code == 0xe0) {
+ ext = 1;
+ goto eoi;
+ }
+
+ if(code & 0x80) {
+ press = 0;
+ code &= 0x7f;
+
+ if(num_pressed > 0) {
+ num_pressed--;
+ }
+ } else {
+ press = 1;
+
+ num_pressed++;
+ }
+
+ if(ext) {
+ key = scantbl_ext[code];
+ c = key;
+ ext = 0;
+ } else {
+ key = scantbl[code];
+ c = (keystate[KEY_LSHIFT] | keystate[KEY_RSHIFT]) ? scantbl_shift[code] : key;
+ }
+
+ if(press && !keystate[key]) {
+ /* append to buffer */
+ last_key = c;
+ buffer[buf_widx] = c;
+ ADVANCE(buf_widx);
+ /* if the write end overtook the read end, advance the read end
+ * too, to discard the oldest keypress from the buffer
+ */
+ if(buf_widx == buf_ridx) {
+ ADVANCE(buf_ridx);
+ }
+ }
+
+ /* and update keystate table */
+ keystate[key] = press;
+
+eoi:
+ outp(PIC1_CMD_PORT, OCW2_EOI); /* send end-of-interrupt */
+}
--- /dev/null
+/*
+DOS interrupt-based keyboard driver.
+Copyright (C) 2013-2023 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the program. If not, see <http://www.gnu.org/licenses/>
+*/
+#ifndef KEYB_H_
+#define KEYB_H_
+
+#include "app.h"
+
+#define KB_ANY (-1)
+#define KB_ALT (-2)
+#define KB_CTRL (-3)
+#define KB_SHIFT (-4)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void kb_init(void);
+void kb_shutdown(void); /* don't forget to call this at the end! */
+
+/* Boolean predicate for testing the current state of a particular key.
+ * You may also pass KB_ANY to test if any key is held down.
+ */
+int kb_isdown(int key);
+
+/* waits for any keypress */
+void kb_wait(void);
+
+/* removes and returns a single key from the input buffer.
+ * If buffering is disabled (initialized with kb_init(0)), then it always
+ * returns the last key pressed.
+ */
+int kb_getkey(void);
+
+void kb_putback(int key);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* KEYB_H_ */
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include "app.h"
+#include "timer.h"
+#include "keyb.h"
+#include "vidsys.h"
+#include "cdpmi.h"
+#include "mouse.h"
+#include "logger.h"
+#include "rtk.h"
+
+static unsigned char *vmem;
+static int quit, dirty_valid;
+static rtk_rect dirty;
+static int mx, my, prev_mx, prev_my, use_mouse;
+
+
+int main(int argc, char **argv)
+{
+ int i;
+ int vmidx;
+ int mdx, mdy, bnstate, bndiff;
+ static int prev_bnstate;
+ char *env;
+
+#ifdef __DJGPP__
+ __djgpp_nearptr_enable();
+#endif
+
+ init_logger();
+ if((env = getenv("MENULOG"))) {
+ if(tolower(env[0]) == 'c' && tolower(env[1]) == 'o' && tolower(env[2]) == 'm'
+ && isdigit(env[3])) {
+ add_log_console(env);
+ } else {
+ add_log_file(env);
+ }
+ }
+
+ if(!(use_mouse = have_mouse())) {
+ infomsg("no mouse detected\n");
+ }
+ init_timer(0);
+ kb_init();
+
+ if(vid_init() == -1) {
+ return 1;
+ }
+
+ scr_width = 640;
+ scr_height = 480;
+ if((vmidx = vid_findmode(scr_width, scr_height, 8)) == -1) {
+ return 1;
+ }
+ if(!(vmem = vid_setmode(vmidx))) {
+ return 1;
+ }
+
+ if(app_init() == -1) {
+ goto break_evloop;
+ }
+ app_invalidate(0, 0, 0, 0);
+
+ app_reshape(scr_width, scr_height);
+ mx = scr_width / 2;
+ my = scr_height / 2;
+ prev_mx = prev_my = -1;
+
+ for(;;) {
+ int key;
+
+ modkeys = 0;
+ if(kb_isdown(KEY_ALT)) {
+ modkeys |= KEY_MOD_ALT;
+ }
+ if(kb_isdown(KEY_CTRL)) {
+ modkeys |= KEY_MOD_CTRL;
+ }
+ if(kb_isdown(KEY_SHIFT)) {
+ modkeys |= KEY_MOD_SHIFT;
+ }
+
+ while((key = kb_getkey()) != -1) {
+ if(key == 'r' && (modkeys & KEY_MOD_CTRL)) {
+ app_invalidate(0, 0, 0, 0);
+ } else {
+ app_keyboard(key, 1);
+ }
+ if(quit) goto break_evloop;
+ }
+
+ if(use_mouse) {
+ bnstate = read_mouse_bn();
+ bndiff = bnstate ^ prev_bnstate;
+ prev_bnstate = bnstate;
+
+ read_mouse_rel(&mdx, &mdy);
+ mx += mdx;
+ if(mx < 0) mx = 0;
+ if(mx >= scr_width) mx = scr_width - 1;
+ my += mdy;
+ if(my < 0) my = 0;
+ if(my >= scr_height) my = scr_height - 1;
+ mdx = mx - prev_mx;
+ mdy = my - prev_my;
+
+ if(bndiff & 1) app_mouse(0, bnstate & 1, mx, my);
+ if(bndiff & 2) app_mouse(1, bnstate & 2, mx, my);
+ if(bndiff & 4) app_mouse(3, bnstate & 4, mx, my);
+
+ if((mdx | mdy) != 0) {
+ app_motion(mx, my);
+ }
+ }
+
+ app_display();
+ app_swap_buffers();
+ }
+
+break_evloop:
+ app_shutdown();
+ vid_cleanup();
+ kb_shutdown();
+ return 0;
+}
+
+void app_invalidate(int x, int y, int w, int h)
+{
+ rtk_rect r;
+
+ if((w | h) == 0) {
+ r.x = r.y = 0;
+ r.width = scr_width;
+ r.height = scr_height;
+ } else {
+ r.x = x;
+ r.y = y;
+ r.width = w;
+ r.height = h;
+ }
+
+ if(dirty_valid) {
+ rtk_rect_union(&dirty, &r);
+ } else {
+ dirty = r;
+ }
+ dirty_valid = 1;
+}
+
+void app_swap_buffers(void)
+{
+ unsigned char *src;
+
+ vid_vsync();
+
+ if(dirty_valid) {
+ if(dirty.width < scr_width || dirty.height < scr_height) {
+ src = framebuf + dirty.y * scr_width + dirty.x;
+ vid_blit8(dirty.x, dirty.y, dirty.width, dirty.height, src, 0);
+ } else {
+ vid_blitfb8(framebuf, 0);
+ }
+ dirty_valid = 0;
+ }
+}
+
+void app_quit(void)
+{
+ quit = 1;
+}
+
+void app_resize(int x, int y)
+{
+}
+
+void app_fullscreen(int fs)
+{
+}
+
+void app_vsync(int vsync)
+{
+}
--- /dev/null
+; 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
--- /dev/null
+#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_ */
--- /dev/null
+#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_ */
--- /dev/null
+/*
+colcycle - color cycling image viewer
+Copyright (C) 2016 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef KEYB_C_
+#error "do not include scancode.h anywhere..."
+#endif
+
+/* table with rough translations from set 1 scancodes to ASCII-ish */
+static int scantbl[] = {
+ 0, KEY_ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', /* 0 - e */
+ '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', /* f - 1c */
+ KEY_LCTRL, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', /* 1d - 29 */
+ KEY_LSHIFT, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', KEY_RSHIFT, /* 2a - 36 */
+ KEY_NUM_MUL, KEY_LALT, ' ', KEY_CAPSLK, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, /* 37 - 44 */
+ KEY_NUMLK, KEY_SCRLK, KEY_NUM_7, KEY_NUM_8, KEY_NUM_9, KEY_NUM_MINUS, KEY_NUM_4, KEY_NUM_5, KEY_NUM_6, KEY_NUM_PLUS, /* 45 - 4e */
+ KEY_NUM_1, KEY_NUM_2, KEY_NUM_3, KEY_NUM_0, KEY_NUM_DOT, KEY_SYSRQ, 0, 0, KEY_F11, KEY_F12, /* 4d - 58 */
+ 0, 0, 0, 0, 0, 0, 0, /* 59 - 5f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 70 - 7f */
+};
+
+static int scantbl_shift[] = {
+ 0, KEY_ESC, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', /* 0 - e */
+ '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', /* f - 1c */
+ KEY_LCTRL, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', /* 1d - 29 */
+ KEY_LSHIFT, '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', KEY_RSHIFT, /* 2a - 36 */
+ KEY_NUM_MUL, KEY_LALT, ' ', KEY_CAPSLK, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, /* 37 - 44 */
+ KEY_NUMLK, KEY_SCRLK, KEY_NUM_7, KEY_NUM_8, KEY_NUM_9, KEY_NUM_MINUS, KEY_NUM_4, KEY_NUM_5, KEY_NUM_6, KEY_NUM_PLUS, /* 45 - 4e */
+ KEY_NUM_1, KEY_NUM_2, KEY_NUM_3, KEY_NUM_0, KEY_NUM_DOT, KEY_SYSRQ, 0, 0, KEY_F11, KEY_F12, /* 4d - 58 */
+ 0, 0, 0, 0, 0, 0, 0, /* 59 - 5f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 70 - 7f */
+};
+
+
+/* extended scancodes, after the 0xe0 prefix */
+static int scantbl_ext[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\r', KEY_RCTRL, 0, 0, /* 10 - 1f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 2f */
+ 0, 0, 0, 0, 0, KEY_NUM_MINUS, 0, KEY_SYSRQ, KEY_RALT, 0, 0, 0, 0, 0, 0, 0, /* 30 - 3f */
+ 0, 0, 0, 0, 0, 0, 0, KEY_HOME, KEY_UP, KEY_PGUP, 0, KEY_LEFT, 0, KEY_RIGHT, 0, KEY_END, /* 40 - 4f */
+ KEY_DOWN, KEY_PGDN, KEY_INS, KEY_DEL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50 - 5f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70 - 7f */
+};
+
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <conio.h>
+#include <dos.h>
+
+#ifdef __WATCOMC__
+#include <i86.h>
+#endif
+
+#ifdef __DJGPP__
+#include <dpmi.h>
+#include <go32.h>
+#include <pc.h>
+#endif
+
+#include "pit8254.h"
+#include "sizeint.h"
+#include "dosutil.h"
+
+#define PIT_TIMER_INTR 8
+#define DOS_TIMER_INTR 0x1c
+
+/* macro to divide and round to the nearest integer */
+#define DIV_ROUND(a, b) \
+ ((a) / (b) + ((a) % (b)) / ((b) / 2))
+
+static void set_timer_reload(int reload_val);
+static void cleanup(void);
+
+#ifdef __WATCOMC__
+#define INTERRUPT __interrupt __far
+
+static void INTERRUPT dos_timer_intr();
+
+static void (INTERRUPT *prev_timer_intr)();
+#endif
+
+#ifdef __DJGPP__
+#define INTERRUPT
+
+static _go32_dpmi_seginfo intr, prev_intr;
+#endif
+
+static void INTERRUPT timer_irq();
+
+static volatile unsigned long ticks;
+static unsigned long tick_interval, ticks_per_dos_intr;
+static int inum;
+
+void init_timer(int res_hz)
+{
+ _disable();
+
+ if(res_hz > 0) {
+ int reload_val = DIV_ROUND(OSC_FREQ_HZ, res_hz);
+ set_timer_reload(reload_val);
+
+ tick_interval = DIV_ROUND(1000, res_hz);
+ ticks_per_dos_intr = DIV_ROUND(65535L, reload_val);
+
+ inum = PIT_TIMER_INTR;
+#ifdef __WATCOMC__
+ prev_timer_intr = _dos_getvect(inum);
+ _dos_setvect(inum, timer_irq);
+#endif
+#ifdef __DJGPP__
+ _go32_dpmi_get_protected_mode_interrupt_vector(inum, &prev_intr);
+ intr.pm_offset = (intptr_t)timer_irq;
+ intr.pm_selector = _go32_my_cs();
+ _go32_dpmi_allocate_iret_wrapper(&intr);
+ _go32_dpmi_set_protected_mode_interrupt_vector(inum, &intr);
+#endif
+ } else {
+ tick_interval = 55;
+
+ inum = DOS_TIMER_INTR;
+#ifdef __WATCOMC__
+ prev_timer_intr = _dos_getvect(inum);
+ _dos_setvect(inum, dos_timer_intr);
+#endif
+#ifdef __DJGPP__
+ assert(0);
+#endif
+ }
+ _enable();
+
+ atexit(cleanup);
+}
+
+static void cleanup(void)
+{
+ if(!inum) {
+ return; /* init hasn't ran, there's nothing to cleanup */
+ }
+
+ _disable();
+ if(inum == PIT_TIMER_INTR) {
+ /* restore the original timer frequency */
+ set_timer_reload(65535);
+ }
+
+ /* restore the original interrupt handler */
+#ifdef __WATCOMC__
+ _dos_setvect(inum, prev_timer_intr);
+#endif
+#ifdef __DJGPP__
+ _go32_dpmi_set_protected_mode_interrupt_vector(inum, &prev_intr);
+ _go32_dpmi_free_iret_wrapper(&intr);
+#endif
+
+ _enable();
+}
+
+void reset_timer(void)
+{
+ ticks = 0;
+}
+
+unsigned long get_msec(void)
+{
+ return ticks * tick_interval;
+}
+
+void sleep_msec(unsigned long msec)
+{
+ unsigned long wakeup_time = ticks + msec / tick_interval;
+ while(ticks < wakeup_time) {
+#ifdef USE_HLT
+ halt();
+#endif
+ }
+}
+
+static void set_timer_reload(int reload_val)
+{
+ outp(PORT_CMD, CMD_CHAN0 | CMD_ACCESS_BOTH | CMD_OP_SQWAVE);
+ outp(PORT_DATA0, reload_val & 0xff);
+ outp(PORT_DATA0, (reload_val >> 8) & 0xff);
+}
+
+#ifdef __WATCOMC__
+static void INTERRUPT dos_timer_intr()
+{
+ ticks++;
+ _chain_intr(prev_timer_intr); /* DOES NOT RETURN */
+}
+#endif
+
+/* first PIC command port */
+#define PIC1_CMD 0x20
+/* end of interrupt control word */
+#define OCW2_EOI (1 << 5)
+
+static void INTERRUPT timer_irq()
+{
+ static unsigned long dos_ticks;
+
+ ticks++;
+
+#ifdef __WATCOMC__
+ if(++dos_ticks >= ticks_per_dos_intr) {
+ /* I suppose the dos irq handler does the EOI so I shouldn't
+ * do it if I am to call the previous function
+ */
+ dos_ticks = 0;
+ _chain_intr(prev_timer_intr); /* XXX DOES NOT RETURN */
+ return; /* just for clarity */
+ }
+#endif
+
+ /* send EOI to the PIC */
+ outp(PIC1_CMD, OCW2_EOI);
+}
--- /dev/null
+#ifndef VBE_H_
+#define VBE_H_
+
+#include <stdio.h>
+#include "sizeint.h"
+
+#pragma pack (push, 1)
+struct vbe_info {
+ char sig[4];
+ uint16_t ver;
+ char *oem_name;
+ uint32_t caps;
+ uint32_t modelist_addr;
+ uint16_t vmem_blk; /* video memory size in 64k blocks */
+ uint16_t oem_ver;
+ char *vendor;
+ char *product;
+ char *revstr;
+ uint16_t accel_ver;
+ uint16_t *accel_modes;
+ char reserved[216];
+ char oem_data[256];
+};
+
+struct vbe_mode_info {
+ uint16_t attr;
+ uint8_t wina_attr, winb_attr;
+ uint16_t win_gran, win_size;
+ uint16_t wina_seg, winb_seg;
+ uint32_t win_func;
+ uint16_t scanline_bytes;
+
+ /* VBE 1.2 and above */
+ uint16_t xres, yres;
+ uint8_t xcharsz, ycharsz;
+ uint8_t num_planes;
+ uint8_t bpp;
+ uint8_t num_banks;
+ uint8_t mem_model;
+ uint8_t bank_size; /* bank size in KB */
+ uint8_t num_img_pages;
+ uint8_t reserved1;
+
+ /* direct color fields */
+ uint8_t rsize, rpos;
+ uint8_t gsize, gpos;
+ uint8_t bsize, bpos;
+ uint8_t xsize, xpos;
+ uint8_t cmode_info; /* direct color mode attributes */
+
+ /* VBE 2.0 and above */
+ uint32_t fb_addr; /* physical address of the linear framebuffer */
+ uint32_t os_addr; /* phys. address of off-screen memory */
+ uint16_t os_size; /* size in KB of off-screen memory */
+
+ /* VBE 3.0 and above */
+ uint16_t lfb_scanline_bytes;
+ uint8_t banked_num_img_pages;
+ uint8_t lfb_num_img_pages;
+ uint8_t lfb_rsize, lfb_rpos;
+ uint8_t lfb_gsize, lfb_gpos;
+ uint8_t lfb_bsize, lfb_bpos;
+ uint8_t lfb_xsize, lfb_xpos;
+ uint32_t max_pixel_clock;
+
+ char reserved2[190];
+};
+
+struct vbe_crtc_info {
+ uint16_t htotal, hsync_start, hsync_end;
+ uint16_t vtotal, vsync_start, vsync_end;
+ uint8_t flags;
+ uint32_t pixel_clock;
+ uint16_t rate_centihz; /* refresh rate in 1/100 hz (pck / (htotal * vtotal)) */
+ char reserved[40];
+};
+#pragma pack (pop)
+
+/* returned by vbe_scanline_info() */
+struct vbe_scanline_info {
+ int size;
+ int num_pixels;
+ int max_scanlines;
+};
+
+enum {
+ VBE_8BIT_DAC = 0x01,
+ VBE_NON_VGA = 0x02,
+ VBE_DAC_BLANK = 0x04,
+ VBE_STEREO = 0x08, /* ? */
+ VBE_ACCEL = 0x08,
+ VBE_STEREO_VESA = 0x10, /* ? */
+ VBE_MUSTLOCK = 0x10,
+ VBE_HWCURSOR = 0x20,
+ VBE_HWCLIP = 0x40,
+ VBE_TRANSP_BLT = 0x80
+};
+
+#define VBE_VER_MAJOR(v) (((v) >> 8) & 0xff)
+#define VBE_VER_MINOR(v) ((v) & 0xff)
+
+/* VBE mode attribute flags (vbe_mode_info.attr) */
+enum {
+ VBE_ATTR_AVAIL = 0x0001,
+ VBE_ATTR_OPTINFO = 0x0002,
+ VBE_ATTR_TTY = 0x0004,
+ VBE_ATTR_COLOR = 0x0008,
+ VBE_ATTR_GFX = 0x0010,
+ /* VBE 2.0 */
+ VBE_ATTR_NOTVGA = 0x0020,
+ VBE_ATTR_BANKED = 0x0040,
+ VBE_ATTR_LFB = 0x0080,
+ VBE_ATTR_DBLSCAN = 0x0100,
+ /* VBE 3.0 */
+ VBE_ATTR_ILACE = 0x0200, /* ! */
+ VBE_ATTR_TRIPLEBUF = 0x0400,
+ VBE_ATTR_STEREO = 0x0800,
+ VBE_ATTR_STEREO_2FB = 0x1000,
+ /* VBE/AF */
+ VBE_ATTR_MUSTLOCK = 0x0200 /* ! */
+};
+
+/* VBE memory model type (vbe_mode_info.mem_model) */
+enum {
+ VBE_TYPE_TEXT,
+ VBE_TYPE_CGA,
+ VBE_TYPE_HERCULES,
+ VBE_TYPE_PLANAR,
+ VBE_TYPE_PACKED,
+ VBE_TYPE_UNCHAIN,
+ VBE_TYPE_DIRECT,
+ VBE_TYPE_YUV
+};
+
+/* VBE window attribute (vbe_mode_info.win(a|b)_attr) */
+enum {
+ VBE_WIN_AVAIL = 0x01,
+ VBE_WIN_RD = 0x02,
+ VBE_WIN_WR = 0x04
+};
+
+/* mode number flags */
+enum {
+ VBE_MODE_RATE = 0x0800, /* VBE 3.0+ user-specified refresh rate */
+ VBE_MODE_ACCEL = 0x2000, /* VBE/AF */
+ VBE_MODE_LFB = 0x4000, /* VBE 2.0+ */
+ VBE_MODE_PRESERVE = 0x8000
+};
+
+/* standard mode numbers */
+enum {
+ VBE_640X400_8BPP = 0x100,
+ VBE_640X480_8BPP = 0x101,
+ VBE_800X600_4BPP = 0x102,
+ VBE_800X600_8BPP = 0x103,
+ VBE_1024X768_4BPP = 0x104,
+ VBE_1024X768_8BPP = 0x105,
+ VBE_1280X1024_4BPP = 0x106,
+ VBE_1280X1024_8BPP = 0x107,
+ VBE_80X60_TEXT = 0x108,
+ VBE_132X25_TEXT = 0x109,
+ VBE_132X43_TEXT = 0x10a,
+ VBE_132X50_TEXT = 0x10b,
+ VBE_132X60_TEXT = 0x10c,
+ /* VBE 1.2 */
+ VBE_320X200_15BPP = 0x10d,
+ VBE_320X200_16BPP = 0x10e,
+ VBE_320X200_24BPP = 0x10f,
+ VBE_640X480_15BPP = 0x110,
+ VBE_640X480_16BPP = 0x111,
+ VBE_640X480_24BPP = 0x112,
+ VBE_800X600_15BPP = 0x113,
+ VBE_800X600_16BPP = 0x114,
+ VBE_800X600_24BPP = 0x115,
+ VBE_1024X768_15BPP = 0x116,
+ VBE_1024X768_16BPP = 0x117,
+ VBE_1024X768_24BPP = 0x118,
+ VBE_1280X1024_15BPP = 0x119,
+ VBE_1280X1024_16BPP = 0x11a,
+ VBE_1280X1024_24BPP = 0x11b,
+ /* VBE 2.0 */
+ VBE_1600X1200_8BPP = 0x120,
+ VBE_1600X1200_15BPP = 0x121,
+ VBE_1600X1200_16BPP = 0x122,
+
+ VBE_VMEM_MODE = 0x81ff
+};
+
+/* VBE CRTC flags (vbe_crtc_info.flags) */
+enum {
+ VBE_CRTC_DBLSCAN = 0x01,
+ VBE_CRTC_ILACE = 0x02,
+ VBE_CRTC_HSYNC_NEG = 0x04,
+ VBE_CRTC_VSYNC_NEG = 0x08
+};
+
+enum {
+ VBE_STATE_CTRLHW = 0x01,
+ VBE_STATE_BIOS = 0x02,
+ VBE_STATE_DAC = 0x04,
+ VBE_STATE_REGS = 0x08,
+
+ VBE_STATE_ALL = 0xffff
+};
+
+enum {
+ VBE_SWAP_NOW,
+ VBE_SWAP_VBLANK,
+ VBE_SWAP_ASYNC /* schedule swap and return (triple-buffering) */
+};
+
+int vbe_info(struct vbe_info *info);
+int vbe_num_modes(struct vbe_info *info);
+int vbe_mode_info(int mode, struct vbe_mode_info *minf);
+
+void vbe_print_info(FILE *fp, struct vbe_info *info);
+void vbe_print_mode_info(FILE *fp, struct vbe_mode_info *minf);
+
+int vbe_setmode(uint16_t mode);
+int vbe_setmode_crtc(uint16_t mode, struct vbe_crtc_info *crtc);
+int vbe_getmode(void);
+
+int vbe_state_size(unsigned int flags);
+int vbe_save(void *stbuf, int sz, unsigned int flags);
+int vbe_restore(void *stbuf, int sz, unsigned int flags);
+
+int vbe_setwin(int wid, int pos);
+int vbe_getwin(int wid);
+
+/* returns the actual length in pixels, which might not be what was requested */
+int vbe_setscanlen(int len_pix);
+int vbe_getscanlen(void);
+int vbe_getpitch(void);
+int vbe_scanline_info(struct vbe_scanline_info *sinf);
+
+int vbe_setdisp(int x, int y, int when);
+int vbe_swap(uint32_t voffs, int when);
+int vbe_swap_pending(void); /* 0: not pending (done) or error, 1: pending swap */
+/* TODO add stereo swap */
+
+#endif /* VBE_H_ */
--- /dev/null
+#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_ */
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <assert.h>
+#include "vidsys.h"
+#include "vga.h"
+#include "drv.h"
+#include "cdpmi.h"
+#include "logger.h"
+
+struct vid_driver *vid_drvlist[MAX_DRV];
+int vid_numdrv;
+
+void *vid_vmem;
+int vid_vmem_size;
+
+static struct vid_modeinfo **modes, *cur_mode;
+static int num_modes, max_modes;
+
+
+int vid_init(void)
+{
+ int i, j, len;
+ struct vid_modeinfo *vm;
+
+ vid_numdrv = 0;
+ free(modes);
+ modes = 0;
+ cur_mode = 0;
+ num_modes = max_modes = 0;
+
+ vid_vmem = 0;
+ vid_vmem_size = 0;
+
+ if(dpmi_init() == -1) {
+ return -1;
+ }
+
+ vid_register_vga();
+ vid_register_vbe();
+ vid_register_s3();
+
+ for(i=0; i<vid_numdrv; i++) {
+ struct vid_driver *drv = vid_drvlist[i];
+
+ drv->ops->init();
+
+ for(j=0; j<drv->num_modes; j++) {
+ if(num_modes >= max_modes) {
+ int newsz = max_modes ? max_modes * 2 : 128;
+ void *tmp = realloc(modes, newsz * sizeof *modes);
+ if(!tmp) {
+ errormsg("failed to allocate modes list\n");
+ return -1;
+ }
+ modes = tmp;
+ max_modes = newsz;
+ }
+
+ modes[num_modes++] = drv->modes + j;
+ }
+ }
+
+ infomsg("found %d modes:\n", num_modes);
+ for(i=0; i<num_modes; i+=2) {
+ vm = modes[i];
+ len = infomsg("[%4s] %04x: %dx%d %dbpp", vm->drv->name, vm->modeno,
+ vm->width, vm->height, vm->bpp);
+ if(i + 1 >= num_modes) {
+ infomsg("\n");
+ break;
+ }
+ for(j=len; j<40; j++) infomsg(" ");
+ vm = modes[i + 1];
+ infomsg("[%4s] %04x: %dx%d %dbpp\n", vm->drv->name, vm->modeno,
+ vm->width, vm->height, vm->bpp);
+ }
+
+ return 0;
+}
+
+void vid_cleanup(void)
+{
+ int i;
+
+ if(cur_mode && cur_mode->modeno != 3) {
+ vid_setmode(3);
+ }
+
+ if(vid_vmem >= (void*)0x100000) {
+ dpmi_munmap(vid_vmem);
+ }
+
+ for(i=0; i<vid_numdrv; i++) {
+ struct vid_driver *drv = vid_drvlist[i];
+ drv->ops->cleanup();
+ }
+
+ free(modes);
+ dpmi_cleanup();
+}
+
+
+int vid_curmode(void)
+{
+ if(cur_mode) {
+ return cur_mode->modeno;
+ }
+ return -1;
+}
+
+void *vid_setmode(int mode)
+{
+ int i;
+ struct vid_driver *drv;
+
+ for(i=0; i<num_modes; i++) {
+ if(modes[i]->modeno == mode) {
+ drv = modes[i]->drv;
+ if(drv->ops->setmode(mode) == 0) {
+ cur_mode = modes[i];
+
+ if(vid_vmem >= (void*)0x100000) {
+ assert(vid_vmem_size);
+ dpmi_munmap(vid_vmem);
+ }
+
+ if(modes[i]->vmem_addr < 0x100000) {
+ vid_vmem = (void*)modes[i]->vmem_addr;
+ vid_vmem_size = 0;
+ } else {
+ vid_vmem = dpmi_mmap(modes[i]->vmem_addr, modes[i]->vmem_size);
+ vid_vmem_size = modes[i]->vmem_size;
+ }
+ return vid_vmem;
+ }
+ }
+ }
+ return 0;
+}
+
+#define EQUIV_BPP(a, b) \
+ ((a) == (b) || ((a) == 16 && (b) == 15) || ((a) == 15 && (b) == 16) || \
+ ((a) == 24 && (b) == 32) || ((a) == 32 && (b) == 24))
+
+int vid_findmode(int xsz, int ysz, int bpp)
+{
+ int i;
+
+ for(i=0; i<num_modes; i++) {
+ if(modes[i]->width == xsz && modes[i]->height == ysz && modes[i]->bpp == bpp) {
+ return modes[i]->modeno;
+ }
+ }
+
+ /* try fuzzy bpp matching */
+ for(i=0; i<num_modes; i++) {
+ if(modes[i]->width == xsz && modes[i]->height == ysz &&
+ EQUIV_BPP(modes[i]->bpp, bpp)) {
+ return modes[i]->modeno;
+ }
+ }
+
+ return -1;
+}
+
+
+struct vid_modeinfo *vid_modeinfo(int mode)
+{
+ int i;
+
+ for(i=0; i<num_modes; i++) {
+ if(modes[i]->modeno == mode) {
+ return modes[i];
+ }
+ }
+ return 0;
+}
+
+int vid_islinear(void)
+{
+ return !vid_isbanked();
+}
+
+int vid_isbanked(void)
+{
+ return cur_mode->win_size && vid_vmem < (void*)0x100000;
+}
+
+void vid_setpal(int idx, int count, const struct vid_color *col)
+{
+ cur_mode->ops.setpal(idx, count, col);
+}
+
+void vid_getpal(int idx, int count, struct vid_color *col)
+{
+ cur_mode->ops.getpal(idx, count, col);
+}
+
+void vid_blit(int x, int y, int w, int h, void *src, int pitch)
+{
+ if(pitch <= 0) {
+ pitch = cur_mode->width << 2;
+ }
+ cur_mode->ops.blit(x, y, w, h, src, pitch);
+}
+
+void vid_blitfb(void *fb, int pitch)
+{
+ if(pitch <= 0) {
+ pitch = cur_mode->width << 2;
+ }
+ cur_mode->ops.blitfb(fb, pitch);
+}
+
+void vid_blit32(int x, int y, int w, int h, uint32_t *src, int pitch)
+{
+ if(cur_mode->bpp == 32) {
+ vid_blit(x, y, w, h, src, pitch);
+ return;
+ }
+
+ if(pitch <= 0) {
+ pitch = cur_mode->width << 2;
+ }
+ /* XXX */
+}
+
+void vid_blitfb32(uint32_t *src, int pitch)
+{
+ int i, j, winpos, winleft, endskip;
+ unsigned char *dest;
+ uint16_t *dest16;
+
+ if(cur_mode->bpp == 32) {
+ vid_blitfb(src, pitch);
+ return;
+ }
+
+ if(pitch <= 0) {
+ pitch = cur_mode->width << 2;
+ }
+
+ if(vid_islinear()) {
+ winleft = INT_MAX;
+ } else {
+ winleft = cur_mode->win_size << 10;
+ winpos = 0;
+ vid_setwin(0, 0);
+ }
+
+ switch(cur_mode->bpp) {
+ case 8:
+ /* TODO */
+ break;
+
+ case 15:
+ /* TODO */
+ break;
+ case 16:
+ /* TODO */
+ break;
+
+ case 24:
+ dest = vid_vmem;
+ endskip = cur_mode->pitch - cur_mode->width * 3;
+
+ for(i=0; i<cur_mode->height; i++) {
+ for(j=0; j<cur_mode->width; j++) {
+ uint32_t pixel = src[j];
+ if(winleft <= 0) {
+ winpos += cur_mode->win_step;
+ vid_setwin(0, winpos);
+ winleft = cur_mode->win_size << 10;
+ dest = vid_vmem;
+ }
+ dest[0] = pixel & 0xff;
+ dest[1] = (pixel >> 8) & 0xff;
+ dest[2] = (pixel >> 16) & 0xff;
+ dest += 3;
+ winleft -= 3;
+ }
+ src = (uint32_t*)((char*)src + pitch);
+ dest += endskip;
+ winleft -= endskip;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
--- /dev/null
+#ifndef VIDSYS_VIDEO_H_
+#define VIDSYS_VIDEO_H_
+
+#include <stdlib.h>
+#include "sizeint.h"
+
+struct vid_drvops;
+
+struct vid_color {
+ int r, g, b;
+};
+
+struct vid_gfxops {
+ void (*pack)(uint32_t *pix, int r, int g, int b);
+ void (*unpack)(uint32_t pix, int *r, int *g, int *b);
+ void (*setpal)(int idx, int count, const struct vid_color *col);
+ void (*getpal)(int idx, int count, struct vid_color *col);
+ void (*vsync)(void);
+
+ void (*clear)(uint32_t color);
+ void (*blitfb)(void *fb, int pitch);
+ void (*flip)(int vsync);
+
+ void (*fill)(int x, int y, int w, int h, uint32_t color);
+ void (*blit)(int x, int y, int w, int h, void *img, int pitch);
+ void (*line)(int x0, int y0, int x1, int y1, uint32_t color);
+ void (*hline)(int x, int y, int len, uint32_t color);
+ void (*vline)(int x, int y, int len, uint32_t color);
+};
+
+struct vid_driver {
+ const char *name;
+ int prio;
+
+ struct vid_modeinfo *modes;
+ int num_modes;
+
+ struct vid_drvops *ops;
+};
+
+struct vid_modeinfo {
+ int modeno;
+ int width, height, bpp, pitch;
+ int ncolors;
+ uint32_t rmask, gmask, bmask;
+ int rshift, gshift, bshift;
+ int pages;
+ int win_size, win_gran, win_step;
+ uint32_t vmem_addr;
+ size_t vmem_size;
+ int lfb;
+
+ struct vid_driver *drv;
+ struct vid_gfxops ops;
+};
+
+int vid_init(void);
+void vid_cleanup(void);
+
+int vid_curmode(void);
+void *vid_setmode(int mode);
+int vid_findmode(int xsz, int ysz, int bpp);
+struct vid_modeinfo *vid_modeinfo(int mode);
+
+void vid_vsync(void); /* defined in drv_vga.c */
+int vid_setwin(int win, int pos); /* defined in drv_vbe.c */
+
+/* current mode functions */
+int vid_islinear(void);
+int vid_isbanked(void);
+
+void vid_setpal(int idx, int count, const struct vid_color *col);
+void vid_getpal(int idx, int count, struct vid_color *col);
+
+void vid_blit(int x, int y, int w, int h, void *src, int pitch);
+void vid_blitfb(void *fb, int pitch);
+void vid_blit32(int x, int y, int w, int h, uint32_t *src, int pitch);
+void vid_blitfb32(uint32_t *fb, int pitch);
+
+#endif /* VIDSYS_VIDEO_H_ */
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "logger.h"
+
+#if defined(__MSDOS__) || defined(MSDOS)
+#include "dosutil.h"
+
+static int setup_serial(int sdev);
+
+void ser_putchar(int c);
+void ser_puts(const char *s);
+void ser_printf(const char *fmt, ...);
+#else
+#define USE_STD
+#endif
+
+enum { LOG_FILE, LOG_STREAM, LOG_CON, LOG_CB };
+enum { LOG_DBG, LOG_INFO, LOG_WARN, LOG_ERR };
+
+struct log_callback {
+ void (*func)(const char*, void*);
+ void *cls;
+};
+
+struct log_output {
+ int type, level;
+ union {
+ FILE *fp;
+ int con;
+ struct log_callback cb;
+ } out;
+};
+
+#define MAX_OUTPUTS 8
+static struct log_output outputs[MAX_OUTPUTS];
+static int num_outputs;
+
+void init_logger(void)
+{
+ num_outputs = 0;
+}
+
+void cleanup_logger(void)
+{
+ int i;
+
+ for(i=0; i<num_outputs; i++) {
+ if(outputs[i].type == LOG_FILE) {
+ fclose(outputs[i].out.fp);
+ }
+ }
+ num_outputs = 0;
+}
+
+int add_log_file(const char *fname)
+{
+ FILE *fp;
+ int idx;
+
+ if(num_outputs >= MAX_OUTPUTS) {
+ return -1;
+ }
+ if(!(fp = fopen(fname, "w"))) {
+ return -1;
+ }
+ idx = num_outputs++;
+
+ outputs[idx].type = LOG_FILE;
+ outputs[idx].out.fp = fp;
+ return 0;
+}
+
+int add_log_stream(FILE *fp)
+{
+ int idx;
+
+ if(num_outputs >= MAX_OUTPUTS) {
+ return -1;
+ }
+ idx = num_outputs++;
+
+ outputs[idx].type = LOG_STREAM;
+ outputs[idx].out.fp = fp;
+ return 0;
+}
+
+int add_log_console(const char *devname)
+{
+#if defined(MSDOS) || defined(__MSDOS__)
+ int i, comport;
+ if(sscanf(devname, "COM%d", &comport) != 1 || comport < 1 || comport > 2) {
+ return -1;
+ }
+ comport--;
+
+ if(num_outputs >= MAX_OUTPUTS) {
+ return -1;
+ }
+ for(i=0; i<num_outputs; i++) {
+ if(outputs[i].type == LOG_CON && outputs[i].out.con == comport) {
+ return -1;
+ }
+ }
+ if(setup_serial(comport) == -1) {
+ return -1;
+ }
+
+ i = num_outputs++;
+ outputs[i].type = LOG_CON;
+ outputs[i].out.con = comport;
+ return 0;
+
+#elif defined(unix) || defined(__unix__)
+ /* TODO? */
+ return -1;
+#endif
+}
+
+int add_log_callback(void (*cbfunc)(const char*, void*), void *cls)
+{
+ int idx;
+
+ if(num_outputs >= MAX_OUTPUTS) {
+ return -1;
+ }
+ idx = num_outputs++;
+
+ outputs[idx].type = LOG_CB;
+ outputs[idx].out.cb.func = cbfunc;
+ outputs[idx].out.cb.cls = cls;
+ return 0;
+}
+
+#if defined(__WATCOMC__)
+#ifndef vsnprintf
+#define vsnprintf _vsnprintf
+#endif
+#endif
+
+static int logmsg(int type, const char *fmt, va_list ap)
+{
+ static char buf[2048];
+ int i, len;
+
+ len = vsnprintf(buf, sizeof buf, fmt, ap);
+
+ for(i=0; i<num_outputs; i++) {
+ switch(outputs[i].type) {
+ case LOG_FILE:
+ case LOG_STREAM:
+ fputs(buf, outputs[i].out.fp);
+ fflush(outputs[i].out.fp);
+ break;
+
+#if defined(MSDOS) || defined(__MSDOS__)
+ case LOG_CON:
+ ser_puts(buf);
+ break;
+#endif
+ case LOG_CB:
+ outputs[i].out.cb.func(buf, outputs[i].out.cb.cls);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return len;
+}
+
+int errormsg(const char *fmt, ...)
+{
+ int len;
+ va_list ap;
+ va_start(ap, fmt);
+ len = logmsg(LOG_ERR, fmt, ap);
+ va_end(ap);
+ return len;
+}
+
+int warnmsg(const char *fmt, ...)
+{
+ int len;
+ va_list ap;
+ va_start(ap, fmt);
+ len = logmsg(LOG_WARN, fmt, ap);
+ va_end(ap);
+ return len;
+}
+
+int infomsg(const char *fmt, ...)
+{
+ int len;
+ va_list ap;
+ va_start(ap, fmt);
+ len = logmsg(LOG_INFO, fmt, ap);
+ va_end(ap);
+ return len;
+}
+
+int dbgmsg(const char *fmt, ...)
+{
+ int len;
+ va_list ap;
+ va_start(ap, fmt);
+ len = logmsg(LOG_DBG, fmt, ap);
+ va_end(ap);
+ return len;
+}
+
+int verrormsg(const char *fmt, va_list ap)
+{
+ return logmsg(LOG_ERR, fmt, ap);
+}
+
+int vwarnmsg(const char *fmt, va_list ap)
+{
+ return logmsg(LOG_ERR, fmt, ap);
+}
+
+int vinfomsg(const char *fmt, va_list ap)
+{
+ return logmsg(LOG_ERR, fmt, ap);
+}
+
+int vdbgmsg(const char *fmt, va_list ap)
+{
+ return logmsg(LOG_ERR, fmt, ap);
+}
+
+
+#if defined(MSDOS) || defined(__MSDOS__)
+#include <conio.h>
+
+#define UART1_BASE 0x3f8
+#define UART2_BASE 0x2f8
+
+#define UART_DATA 0
+#define UART_DIVLO 0
+#define UART_DIVHI 1
+#define UART_FIFO 2
+#define UART_LCTL 3
+#define UART_MCTL 4
+#define UART_LSTAT 5
+
+#define DIV_9600 (115200 / 9600)
+#define DIV_38400 (115200 / 38400)
+#define LCTL_8N1 0x03
+#define LCTL_DLAB 0x80
+#define FIFO_ENABLE_CLEAR 0x07
+#define MCTL_DTR_RTS_OUT2 0x0b
+#define LST_TRIG_EMPTY 0x20
+
+static unsigned int iobase;
+
+static int setup_serial(int sdev)
+{
+ if(sdev < 0 || sdev > 1) {
+ return -1;
+ }
+ iobase = sdev == 0 ? UART1_BASE : UART2_BASE;
+
+ /* set clock divisor */
+ outp(iobase | UART_LCTL, LCTL_DLAB);
+ outp(iobase | UART_DIVLO, DIV_9600 & 0xff);
+ outp(iobase | UART_DIVHI, DIV_9600 >> 8);
+ /* set format 8n1 */
+ outp(iobase | UART_LCTL, LCTL_8N1);
+ /* assert RTS and DTR */
+ outp(iobase | UART_MCTL, MCTL_DTR_RTS_OUT2);
+ return 0;
+}
+
+void ser_putchar(int c)
+{
+ if(c == '\n') {
+ ser_putchar('\r');
+ }
+
+ while((inp(iobase | UART_LSTAT) & LST_TRIG_EMPTY) == 0);
+ outp(iobase | UART_DATA, c);
+}
+
+void ser_puts(const char *s)
+{
+ while(*s) {
+ ser_putchar(*s++);
+ }
+}
+
+void ser_printf(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[512];
+
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ va_end(ap);
+
+ ser_puts(buf);
+}
+#endif
--- /dev/null
+#ifndef LOGGER_H_
+#define LOGGER_H_
+
+#include <stdio.h>
+#include <stdarg.h>
+
+void init_logger(void);
+void cleanup_logger(void);
+
+int add_log_file(const char *fname);
+int add_log_stream(FILE *fp);
+int add_log_console(const char *devname);
+int add_log_callback(void (*cbfunc)(const char*, void*), void *cls);
+
+int errormsg(const char *fmt, ...);
+int warnmsg(const char *fmt, ...);
+int infomsg(const char *fmt, ...);
+int dbgmsg(const char *fmt, ...);
+
+int verrormsg(const char *fmt, va_list ap);
+int vwarnmsg(const char *fmt, va_list ap);
+int vinfomsg(const char *fmt, va_list ap);
+int vdbgmsg(const char *fmt, va_list ap);
+
+#endif /* LOGGER_H_ */
--- /dev/null
+#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)
+{
+}
--- /dev/null
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include "app.h"
+#include "rtk.h"
+#include "rtk_impl.h"
+#include "logger.h"
+
+static void on_any_nop();
+static void on_window_drag(rtk_widget *w, int dx, int dy, int total_dx, int total_dy);
+static void on_button_click(rtk_widget *w);
+static void on_textbox_key(rtk_widget *w, int key, int press);
+
+void inval_vport(void); /* scr_mod.c */
+
+
+void rtk_setup(rtk_draw_ops *drawop)
+{
+ rtk_gfx = *drawop;
+
+ rtk_init_drawing();
+}
+
+static int wsize(int type)
+{
+ switch(type) {
+ case RTK_LABEL:
+ case RTK_CHECKBOX:
+ case RTK_SEP:
+ case RTK_DRAWBOX:
+ return sizeof(rtk_widget);
+ case RTK_WIN:
+ return sizeof(rtk_window);
+ case RTK_BUTTON:
+ return sizeof(rtk_button);
+ case RTK_TEXTBOX:
+ return sizeof(rtk_textbox);
+ case RTK_SLIDER:
+ return sizeof(rtk_slider);
+ default:
+ break;
+ }
+ return 0;
+}
+
+rtk_widget *rtk_create_widget(int type)
+{
+ rtk_widget *w;
+
+ if(!(w = calloc(1, wsize(type)))) {
+ return 0;
+ }
+ w->type = type;
+ w->flags = VISIBLE | ENABLED | GEOMCHG | DIRTY;
+
+ w->on_key = on_any_nop;
+ w->on_mbutton = on_any_nop;
+ w->on_click = on_any_nop;
+ w->on_drag = on_any_nop;
+ w->on_drop = on_any_nop;
+
+ w->pad = 2;
+ return w;
+}
+
+void rtk_free_widget(rtk_widget *w)
+{
+ if(!w) return;
+
+ if(w->type == RTK_WIN) {
+ rtk_window *win = (rtk_window*)w;
+ while(win->clist) {
+ rtk_widget *c = win->clist;
+ win->clist = win->clist->next;
+ rtk_free_widget(c);
+ }
+ }
+
+ free(w->text);
+ free(w);
+}
+
+int rtk_type(rtk_widget *w)
+{
+ return w->type;
+}
+
+void rtk_padding(rtk_widget *w, int pad)
+{
+ w->pad = pad;
+}
+
+void rtk_move(rtk_widget *w, int x, int y)
+{
+ if(!w->par) {
+ rtk_clearfb(w);
+ rtk_invalfb(w);
+ }
+ w->x = x;
+ w->y = y;
+ w->flags |= GEOMCHG | DIRTY;
+ if(!w->par) {
+ rtk_invalfb(w);
+ inval_vport();
+ }
+}
+
+void rtk_pos(rtk_widget *w, int *xptr, int *yptr)
+{
+ *xptr = w->x;
+ *yptr = w->y;
+}
+
+void rtk_abspos(rtk_widget *w, int *xptr, int *yptr)
+{
+ *xptr = w->absx;
+ *yptr = w->absy;
+}
+
+void rtk_resize(rtk_widget *w, int xsz, int ysz)
+{
+ if(!w->par) {
+ rtk_clearfb(w);
+ rtk_invalfb(w);
+ }
+ w->width = xsz;
+ w->height = ysz;
+ w->flags |= GEOMCHG | DIRTY;
+ if(!w->par) {
+ rtk_invalfb(w);
+ inval_vport();
+ }
+}
+
+void rtk_size(rtk_widget *w, int *xptr, int *yptr)
+{
+ *xptr = w->width;
+ *yptr = w->height;
+}
+
+int rtk_get_width(rtk_widget *w)
+{
+ return w->width;
+}
+
+int rtk_get_height(rtk_widget *w)
+{
+ return w->height;
+}
+
+void rtk_get_rect(rtk_widget *w, rtk_rect *r)
+{
+ r->x = w->x;
+ r->y = w->y;
+ r->width = w->width;
+ r->height = w->height;
+}
+
+void rtk_get_absrect(rtk_widget *w, rtk_rect *r)
+{
+ r->x = w->absx;
+ r->y = w->absy;
+ r->width = w->width;
+ r->height = w->height;
+}
+
+void rtk_autosize(rtk_widget *w, unsigned int szopt)
+{
+ if(szopt & RTK_AUTOSZ_WIDTH) {
+ w->flags |= AUTOWIDTH;
+ } else {
+ w->flags &= ~AUTOWIDTH;
+ }
+
+ if(szopt & RTK_AUTOSZ_HEIGHT) {
+ w->flags |= AUTOHEIGHT;
+ } else {
+ w->flags &= ~AUTOHEIGHT;
+ }
+}
+
+int rtk_set_text(rtk_widget *w, const char *str)
+{
+ rtk_rect rect;
+ char *s = strdup(str);
+ if(!s) return -1;
+
+ free(w->text);
+ w->text = s;
+
+ if(w->type == RTK_TEXTBOX) {
+ rtk_textbox *tb = (rtk_textbox*)w;
+ tb->cursor = tb->scroll = 0;
+ tb->bufsz = strlen(str) + 1;
+ }
+
+ rtk_calc_widget_rect(w, &rect);
+ rtk_resize(w, rect.width, rect.height);
+ rtk_invalidate(w);
+ return 0;
+}
+
+const char *rtk_get_text(rtk_widget *w)
+{
+ return w->text;
+}
+
+void rtk_set_value(rtk_widget *w, int val)
+{
+ w->value = val;
+ rtk_invalidate(w);
+}
+
+int rtk_get_value(rtk_widget *w)
+{
+ return w->value;
+}
+
+void rtk_set_callback(rtk_widget *w, rtk_callback cbfunc, void *cls)
+{
+ w->cbfunc = cbfunc;
+ w->cbcls = cls;
+}
+
+void rtk_set_callback_closure(rtk_widget *w, void *cls)
+{
+ w->cbcls = cls;
+}
+
+rtk_callback rtk_get_callback(const rtk_widget *w)
+{
+ return w->cbfunc;
+}
+
+void *rtk_get_callback_closure(const rtk_widget *w)
+{
+ return w->cbcls;
+}
+
+void rtk_set_drawfunc(rtk_widget *w, rtk_callback drawfunc, void *cls)
+{
+ w->drawcb = drawfunc;
+ w->drawcls = cls;
+}
+
+void rtk_set_key_handler(rtk_widget *w, rtk_key_callback func)
+{
+ w->on_key = func;
+}
+
+void rtk_set_mbutton_handler(rtk_widget *w, rtk_mbutton_callback func)
+{
+ w->on_mbutton = func;
+}
+
+void rtk_set_click_handler(rtk_widget *w, rtk_click_callback func)
+{
+ w->on_click = func;
+}
+
+void rtk_set_drag_handler(rtk_widget *w, rtk_drag_callback func)
+{
+ w->on_drag = func;
+}
+
+void rtk_set_drop_handler(rtk_widget *w, rtk_drop_callback func)
+{
+ w->on_drop = func;
+}
+
+void rtk_show(rtk_widget *w)
+{
+ w->flags |= VISIBLE;
+ rtk_invalidate(w);
+}
+
+void rtk_show_modal(rtk_widget *w)
+{
+ rtk_show(w);
+ if(w->scr) {
+ w->scr->modal = w;
+ }
+}
+
+void rtk_hide(rtk_widget *w)
+{
+ w->flags &= ~VISIBLE;
+ if(w->scr && w->scr->modal == w) {
+ w->scr->modal = 0;
+ }
+ rtk_clearfb(w);
+ rtk_invalfb(w);
+ inval_vport();
+}
+
+int rtk_visible(const rtk_widget *w)
+{
+ return w->flags & VISIBLE;
+}
+
+void rtk_invalidate(rtk_widget *w)
+{
+ w->flags |= DIRTY;
+
+#if !defined(MSDOS) && !defined(__MSDOS__)
+ /* this is necessary because the GLUT backend will have to see a
+ * glutRedisplay in order for anything to be redrawn
+ */
+ rtk_invalfb(w);
+#endif
+}
+
+void rtk_validate(rtk_widget *w)
+{
+ w->flags &= ~DIRTY;
+}
+
+void rtk_win_layout(rtk_widget *w, int layout)
+{
+ ((rtk_window*)w)->layout = layout;
+}
+
+void rtk_win_clear(rtk_widget *w)
+{
+ rtk_widget *tmp;
+ rtk_window *win = (rtk_window*)w;
+
+ RTK_ASSERT_TYPE(w, RTK_WIN);
+
+ while(win->clist) {
+ tmp = win->clist;
+ win->clist = win->clist->next;
+ rtk_free_widget(tmp);
+ }
+
+ win->clist = win->ctail = 0;
+ rtk_invalidate(w);
+}
+
+void rtk_win_add(rtk_widget *par, rtk_widget *child)
+{
+ rtk_window *parwin = (rtk_window*)par;
+
+ RTK_ASSERT_TYPE(par, RTK_WIN);
+
+ if(rtk_win_has(par, child)) {
+ return;
+ }
+
+ if(child->par) {
+ rtk_win_rm((rtk_widget*)child->par, child);
+ }
+
+ if(parwin->clist) {
+ parwin->ctail->next = child;
+ parwin->ctail = child;
+ } else {
+ parwin->clist = parwin->ctail = child;
+ }
+ child->next = 0;
+
+ child->par = parwin;
+ rtk_invalidate(par);
+}
+
+void rtk_win_rm(rtk_widget *par, rtk_widget *child)
+{
+ rtk_widget *prev, dummy;
+ rtk_window *parwin = (rtk_window*)par;
+
+ RTK_ASSERT_TYPE(par, RTK_WIN);
+
+ dummy.next = parwin->clist;
+ prev = &dummy;
+ while(prev->next) {
+ if(prev->next == child) {
+ if(!child->next) {
+ parwin->ctail = prev;
+ }
+ prev->next = child->next;
+ break;
+ }
+ prev = prev->next;
+ }
+ parwin->clist = dummy.next;
+ rtk_invalidate(par);
+}
+
+int rtk_win_has(rtk_widget *par, rtk_widget *child)
+{
+ rtk_widget *w;
+ rtk_window *parwin = (rtk_window*)par;
+
+ RTK_ASSERT_TYPE(par, RTK_WIN);
+
+ w = parwin->clist;
+ while(w) {
+ if(w == child) {
+ return 1;
+ }
+ w = w->next;
+ }
+ return 0;
+}
+
+rtk_widget *rtk_win_child(const rtk_widget *par, int idx)
+{
+ rtk_widget *w;
+ rtk_window *win = (rtk_window*)par;
+
+ RTK_ASSERT_TYPE(par, RTK_WIN);
+
+ w = win->clist;
+ while(w && idx-- > 0) {
+ w = w->next;
+ }
+ return idx > 0 ? 0 : w;
+}
+
+int rtk_win_descendant(const rtk_widget *par, const rtk_widget *w)
+{
+ rtk_widget *c;
+ rtk_window *parwin = (rtk_window*)par;
+
+ RTK_ASSERT_TYPE(par, RTK_WIN);
+
+ c = parwin->clist;
+ while(c) {
+ if(c == w) {
+ return 1;
+ }
+ if(c->type == RTK_WIN) {
+ if(rtk_win_descendant(c, w)) {
+ return 1;
+ }
+ }
+ c = c->next;
+ }
+ return 0;
+}
+
+/* --- button functions --- */
+void rtk_bn_mode(rtk_widget *w, int mode)
+{
+ RTK_ASSERT_TYPE(w, RTK_BUTTON);
+ ((rtk_button*)w)->mode = mode;
+ rtk_invalidate(w);
+}
+
+void rtk_bn_set_icon(rtk_widget *w, rtk_icon *icon)
+{
+ rtk_rect rect;
+
+ RTK_ASSERT_TYPE(w, RTK_BUTTON);
+ ((rtk_button*)w)->icon = icon;
+
+ rtk_calc_widget_rect(w, &rect);
+ rtk_resize(w, rect.width, rect.height);
+ rtk_invalidate(w);
+}
+
+rtk_icon *rtk_bn_get_icon(rtk_widget *w)
+{
+ RTK_ASSERT_TYPE(w, RTK_BUTTON);
+ return ((rtk_button*)w)->icon;
+}
+
+/* --- slider functions --- */
+void rtk_slider_set_range(rtk_widget *w, int vmin, int vmax)
+{
+ rtk_slider *slider = (rtk_slider*)w;
+ RTK_ASSERT_TYPE(w, RTK_SLIDER);
+
+ slider->vmin = vmin;
+ slider->vmax = vmax;
+}
+
+void rtk_slider_get_range(const rtk_widget *w, int *vmin, int *vmax)
+{
+ rtk_slider *slider = (rtk_slider*)w;
+ RTK_ASSERT_TYPE(w, RTK_SLIDER);
+
+ *vmin = slider->vmin;
+ *vmax = slider->vmax;
+}
+
+
+/* --- constructors --- */
+
+rtk_widget *rtk_create_window(rtk_widget *par, const char *title, int x, int y,
+ int width, int height, unsigned int flags)
+{
+ rtk_widget *w;
+
+ if(!(w = rtk_create_widget(RTK_WIN))) {
+ return 0;
+ }
+ if(par) {
+ rtk_win_add(par, w);
+ }
+ w->on_drag = on_window_drag;
+ rtk_set_text(w, title);
+ rtk_move(w, x, y);
+ rtk_resize(w, width, height);
+ rtk_win_layout(w, RTK_VBOX);
+
+ w->flags |= flags << 16;
+ return w;
+}
+
+rtk_widget *rtk_create_button(rtk_widget *par, const char *str, rtk_callback cbfunc)
+{
+ rtk_widget *w;
+
+ if(!(w = rtk_create_widget(RTK_BUTTON))) {
+ return 0;
+ }
+ if(par) rtk_win_add(par, w);
+ rtk_autosize(w, RTK_AUTOSZ_SIZE);
+ w->on_click = on_button_click;
+ rtk_set_text(w, str);
+ rtk_set_callback(w, cbfunc, 0);
+ return w;
+}
+
+rtk_widget *rtk_create_iconbutton(rtk_widget *par, rtk_icon *icon, rtk_callback cbfunc)
+{
+ rtk_widget *w;
+
+ if(!(w = rtk_create_widget(RTK_BUTTON))) {
+ return 0;
+ }
+ if(par) rtk_win_add(par, w);
+ rtk_autosize(w, RTK_AUTOSZ_SIZE);
+ w->on_click = on_button_click;
+ rtk_bn_set_icon(w, icon);
+ rtk_set_callback(w, cbfunc, 0);
+ return w;
+}
+
+rtk_widget *rtk_create_label(rtk_widget *par, const char *text)
+{
+ rtk_widget *w;
+
+ if(!(w = rtk_create_widget(RTK_LABEL))) {
+ return 0;
+ }
+ if(par) rtk_win_add(par, w);
+ rtk_autosize(w, RTK_AUTOSZ_SIZE);
+ rtk_set_text(w, text);
+ return w;
+}
+
+rtk_widget *rtk_create_checkbox(rtk_widget *par, const char *text, int chk, rtk_callback cbfunc)
+{
+ rtk_widget *w;
+
+ if(!(w = rtk_create_widget(RTK_CHECKBOX))) {
+ return 0;
+ }
+ if(par) rtk_win_add(par, w);
+ rtk_autosize(w, RTK_AUTOSZ_SIZE);
+ rtk_set_text(w, text);
+ rtk_set_value(w, chk ? 1 : 0);
+ rtk_set_callback(w, cbfunc, 0);
+ return w;
+}
+
+rtk_widget *rtk_create_textbox(rtk_widget *par, const char *text, rtk_callback cbfunc)
+{
+ rtk_widget *w;
+
+ if(!(w = rtk_create_widget(RTK_TEXTBOX))) {
+ return 0;
+ }
+ if(par) rtk_win_add(par, w);
+ rtk_autosize(w, RTK_AUTOSZ_HEIGHT);
+ w->on_key = on_textbox_key;
+ if(text) {
+ rtk_set_text(w, text);
+ }
+ rtk_set_callback(w, cbfunc, 0);
+ rtk_resize(w, 40, 1);
+
+ w->flags |= CANFOCUS;
+ return w;
+}
+
+rtk_widget *rtk_create_slider(rtk_widget *par, int vmin, int vmax, int val, rtk_callback cbfunc)
+{
+ rtk_widget *w;
+
+ if(!(w = rtk_create_widget(RTK_SLIDER))) {
+ return 0;
+ }
+ if(par) rtk_win_add(par, w);
+ rtk_autosize(w, RTK_AUTOSZ_HEIGHT);
+ rtk_set_callback(w, cbfunc, 0);
+ rtk_slider_set_range(w, vmin, vmax);
+ rtk_set_value(w, val);
+ return w;
+}
+
+rtk_widget *rtk_create_separator(rtk_widget *par)
+{
+ rtk_widget *w;
+
+ if(!(w = rtk_create_widget(RTK_SEP))) {
+ return 0;
+ }
+ if(par) rtk_win_add(par, w);
+ rtk_autosize(w, RTK_AUTOSZ_SIZE);
+ return w;
+}
+
+rtk_widget *rtk_create_drawbox(rtk_widget *par, int width, int height, rtk_callback cbfunc)
+{
+ rtk_widget *w;
+
+ if(!(w = rtk_create_widget(RTK_DRAWBOX))) {
+ return 0;
+ }
+ if(par) rtk_win_add(par, w);
+ rtk_resize(w, width, height);
+ rtk_set_drawfunc(w, cbfunc, 0);
+ return w;
+}
+
+/* --- compound widgets --- */
+rtk_widget *rtk_create_hbox(rtk_widget *par)
+{
+ rtk_widget *box = rtk_create_window(par, "hbox", 0, 0, 0, 0, 0);
+ rtk_autosize(box, RTK_AUTOSZ_SIZE);
+ rtk_win_layout(box, RTK_HBOX);
+ return box;
+}
+
+rtk_widget *rtk_create_vbox(rtk_widget *par)
+{
+ rtk_widget *box = rtk_create_window(par, "vbox", 0, 0, 0, 0, 0);
+ rtk_autosize(box, RTK_AUTOSZ_SIZE);
+ rtk_win_layout(box, RTK_VBOX);
+ return box;
+}
+
+rtk_widget *rtk_create_field(rtk_widget *par, const char *lbtext, rtk_callback cbfunc)
+{
+ rtk_widget *hbox;
+ rtk_widget *lb, *tb;
+
+ if(!(hbox = rtk_create_window(par, "field", 0, 0, 0, 0, 0))) {
+ return 0;
+ }
+ rtk_win_layout(hbox, RTK_HBOX);
+ if(!(lb = rtk_create_label(hbox, lbtext))) {
+ rtk_free_widget(hbox);
+ return 0;
+ }
+ if(!(tb = rtk_create_textbox(hbox, 0, cbfunc))) {
+ rtk_free_widget(hbox);
+ return 0;
+ }
+ return tb;
+}
+
+/* --- icon functions --- */
+rtk_iconsheet *rtk_load_iconsheet(const char *fname)
+{
+ /*
+ rtk_iconsheet *is;
+
+ if(!(is = malloc(sizeof *is))) {
+ return 0;
+ }
+ is->icons = 0;
+
+ if(!(is->pixels = img_load_pixels(fname, &is->width, &is->height, IMG_FMT_RGBA32))) {
+ free(is);
+ return 0;
+ }
+ return is;
+ */
+ return 0;
+}
+
+void rtk_free_iconsheet(rtk_iconsheet *is)
+{
+ /*
+ rtk_icon *icon;
+
+ img_free_pixels(is->pixels);
+
+ while(is->icons) {
+ icon = is->icons;
+ is->icons = is->icons->next;
+ free(icon->name);
+ free(icon);
+ }
+ free(is);
+ */
+}
+
+rtk_icon *rtk_define_icon(rtk_iconsheet *is, const char *name, int x, int y, int w, int h)
+{
+ rtk_icon *icon;
+
+ if(!(icon = malloc(sizeof *icon))) {
+ return 0;
+ }
+ if(!(icon->name = strdup(name))) {
+ free(icon);
+ return 0;
+ }
+ icon->width = w;
+ icon->height = h;
+ icon->scanlen = is->width;
+ icon->pixels = is->pixels + y * is->width + x;
+
+ icon->next = is->icons;
+ is->icons = icon;
+ return icon;
+}
+
+rtk_icon *rtk_lookup_icon(rtk_iconsheet *is, const char *name)
+{
+ rtk_icon *icon = is->icons;
+ while(icon) {
+ if(strcmp(icon->name, name) == 0) {
+ return icon;
+ }
+ icon = icon->next;
+ }
+ return 0;
+}
+
+static void sethover(rtk_screen *scr, rtk_widget *w)
+{
+ if(scr->hover == w) return;
+
+ if(scr->hover) {
+ scr->hover->flags &= ~HOVER;
+
+ if(scr->hover->type != RTK_WIN) {
+ rtk_invalidate(scr->hover);
+ }
+ }
+ scr->hover = w;
+ if(w) {
+ w->flags |= HOVER;
+
+ if(w->type != RTK_WIN) {
+ rtk_invalidate(w);
+ }
+ }
+
+ /*dbgmsg("hover \"%s\"\n", w ? (w->text ? w->text : "?") : "-");*/
+}
+
+static void setpress(rtk_widget *w, int press)
+{
+ if(press) {
+ w->flags |= PRESS;
+ } else {
+ w->flags &= ~PRESS;
+ }
+ rtk_invalidate(w);
+}
+
+static void setfocus(rtk_screen *scr, rtk_widget *w)
+{
+ rtk_window *win;
+
+ if(scr->focus) {
+ scr->focus->flags &= ~FOCUS;
+ }
+
+ if(scr->focuswin) {
+ scr->focuswin->flags &= ~FOCUS;
+ rtk_invalidate((rtk_widget*)scr->focuswin);
+ }
+
+ if(!w) return;
+
+ if(w->flags & CANFOCUS) {
+ w->flags |= FOCUS;
+ scr->focus = w;
+ }
+
+ win = (rtk_window*)w;
+ while(win->par) {
+ win = win->par;
+ }
+ win->flags |= FOCUS;
+ scr->focuswin = win;
+
+ rtk_invalidate(w);
+ rtk_invalidate((rtk_widget*)win);
+}
+
+/* --- screen functions --- */
+rtk_screen *rtk_create_screen(void)
+{
+ rtk_screen *scr;
+
+ if(!(scr = calloc(1, sizeof *scr))) {
+ return 0;
+ }
+ return scr;
+}
+
+void rtk_free_screen(rtk_screen *scr)
+{
+ int i;
+
+ for(i=0; i<scr->num_win; i++) {
+ rtk_free_widget(scr->winlist[i]);
+ }
+ free(scr);
+}
+
+int rtk_add_window(rtk_screen *scr, rtk_widget *win)
+{
+ if(scr->num_win >= MAX_WINDOWS) {
+ return -1;
+ }
+ scr->winlist[scr->num_win++] = win;
+ win->scr = scr;
+ return 0;
+}
+
+static rtk_widget *find_widget_at(rtk_widget *w, int x, int y, unsigned int flags)
+{
+ rtk_widget *c, *res;
+ rtk_window *win;
+
+ if(!(w->flags & VISIBLE)) {
+ return 0;
+ }
+
+ if(w->type != RTK_WIN) {
+ if((w->flags & flags) && rtk_hittest(w, x, y)) {
+ return w;
+ }
+ return 0;
+ }
+
+ win = (rtk_window*)w;
+ c = win->clist;
+ while(c) {
+ if((res = find_widget_at(c, x, y, flags))) {
+ return res;
+ }
+ c = c->next;
+ }
+ return rtk_hittest(w, x, y) ? w : 0;
+}
+
+rtk_widget *rtk_find_widget_at(rtk_screen *scr, rtk_widget *win, int x, int y, unsigned int flags)
+{
+ int i;
+ rtk_widget *w;
+
+ if(!flags) flags = ~0;
+
+ if(win) {
+ RTK_ASSERT_TYPE(win, RTK_WIN);
+ return find_widget_at(win, x, y, flags);
+ }
+
+ for(i=0; i<scr->num_win; i++) {
+ if((w = find_widget_at(scr->winlist[i], x, y, flags))) {
+ return w;
+ }
+ }
+ return 0;
+}
+
+int rtk_input_key(rtk_screen *scr, int key, int press)
+{
+ if(scr->focus && (!scr->modal || rtk_win_descendant(scr->modal, scr->focus))) {
+ scr->focus->on_key(scr->focus, key, press);
+ return 1;
+ }
+
+ if(scr->modal) {
+ if(!press) return 1;
+
+ switch(key) {
+ case 27:
+ rtk_hide(scr->modal);
+ break;
+
+ case '\n':
+ /* TODO */
+ break;
+
+ default:
+ break;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+int rtk_input_mbutton(rtk_screen *scr, int bn, int press, int x, int y)
+{
+ int handled = 0;
+ rtk_widget *w;
+
+ if((w = rtk_find_widget_at(scr, scr->modal, x, y, 0))) {
+ int relx = x - w->absx;
+ int rely = y - w->absy;
+ w->on_mbutton(w, bn, press, relx, rely);
+ }
+
+ if(press) {
+ scr->prev_mx = x;
+ scr->prev_my = y;
+ if(bn == 0) {
+ scr->press = w;
+ scr->press_x = x;
+ scr->press_y = y;
+
+ if(w) setpress(w, 1);
+ }
+ handled = w ? 1 : 0;
+ } else {
+ if(!w && scr->modal) {
+ rtk_hide(scr->modal);
+ return 1;
+ }
+
+ if(bn == 0) {
+ rtk_widget *newfocus = 0;
+ if(w) {
+ if(scr->press == w) {
+ newfocus = rtk_find_widget_at(scr, scr->modal, x, y, 0);
+ w->on_click(w);
+ } else {
+ w->on_drop(scr->press, w);
+ }
+ handled = 1;
+ }
+
+ setfocus(scr, newfocus);
+
+ if(scr->press) {
+ setpress(scr->press, 0);
+ scr->press = 0;
+ }
+ }
+ }
+ return scr->modal ? 1 : handled;
+}
+
+int rtk_input_mmotion(rtk_screen *scr, int x, int y)
+{
+ int dx, dy;
+ rtk_widget *w;
+
+ dx = x - scr->prev_mx;
+ dy = y - scr->prev_my;
+ scr->prev_mx = x;
+ scr->prev_my = y;
+
+ if(scr->press) {
+ w = scr->press;
+ if((dx | dy)) {
+ w->on_drag(w, dx, dy, x - scr->press_x, y - scr->press_y);
+ }
+ return 1;
+ }
+
+ if(!(w = rtk_find_widget_at(scr, scr->modal, x, y, 0))) {
+ return scr->modal ? 1 : 0;
+ }
+ sethover(scr, w);
+ return 1;
+}
+
+void rtk_invalidate_screen(rtk_screen *scr)
+{
+ int i;
+ for(i=0; i<scr->num_win; i++) {
+ rtk_invalidate(scr->winlist[i]);
+ }
+}
+
+void rtk_draw_screen(rtk_screen *scr)
+{
+ int i;
+ for(i=0; i<scr->num_win; i++) {
+ rtk_draw_widget(scr->winlist[i]);
+ }
+}
+
+/* --- misc functions --- */
+void rtk_null_rect(rtk_rect *rect)
+{
+ rect->x = rect->y = INT_MAX / 2;
+ rect->width = rect->height = INT_MIN;
+}
+
+void rtk_fix_rect(rtk_rect *rect)
+{
+ int x, y, w, h;
+
+ x = rect->x;
+ y = rect->y;
+
+ if(rect->width < 0) {
+ w = -rect->width;
+ x += rect->width;
+ } else {
+ w = rect->width;
+ }
+ if(rect->height < 0) {
+ h = -rect->height;
+ y += rect->height;
+ } else {
+ h = rect->height;
+ }
+
+ rect->x = x;
+ rect->y = y;
+ rect->width = w;
+ rect->height = h;
+}
+
+void rtk_rect_union(rtk_rect *a, const rtk_rect *b)
+{
+ int x0, y0, x1, y1;
+
+ x0 = a->x;
+ y0 = a->y;
+ x1 = a->x + a->width;
+ y1 = a->y + a->height;
+
+ if(b->x < x0) x0 = b->x;
+ if(b->y < y0) y0 = b->y;
+ if(b->x + b->width > x1) x1 = b->x + b->width;
+ if(b->y + b->height > y1) y1 = b->y + b->height;
+
+ a->x = x0;
+ a->y = y0;
+ a->width = x1 - x0;
+ a->height = y1 - y0;
+}
+
+
+/* --- widget event handlers --- */
+static void on_any_nop()
+{
+}
+
+static void on_window_drag(rtk_widget *w, int dx, int dy, int total_dx, int total_dy)
+{
+ if(w->flags & MOVABLE) {
+ rtk_move(w, w->x + dx, w->y + dy);
+ }
+}
+
+static void on_button_click(rtk_widget *w)
+{
+ rtk_button *bn = (rtk_button*)w;
+
+ switch(w->type) {
+ case RTK_BUTTON:
+ if(bn->mode == RTK_TOGGLEBN) {
+ case RTK_CHECKBOX:
+ bn->value ^= 1;
+ }
+ if(bn->cbfunc) {
+ bn->cbfunc(w, bn->cbcls);
+ }
+ rtk_invalidate(w);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void on_textbox_key(rtk_widget *w, int key, int press)
+{
+ rtk_textbox *tb = (rtk_textbox*)w;
+
+ if(!press) return;
+
+ switch(key) {
+ case KEY_BACKSP:
+ if(tb->cursor > 0) {
+ if(tb->cursor < tb->len) {
+ memmove(tb->text + tb->cursor - 1, tb->text + tb->cursor, tb->len - tb->cursor + 1);
+ tb->cursor--;
+ } else {
+ tb->text[--tb->cursor] = 0;
+ }
+ tb->len--;
+ }
+ rtk_invalidate(w);
+ return;
+
+ case KEY_HOME:
+ tb->cursor = 0;
+ tb->scroll = 0;
+ rtk_invalidate(w);
+ return;
+
+ case KEY_END:
+ tb->cursor = tb->len;
+ rtk_invalidate(w);
+ return;
+
+ case KEY_LEFT:
+ if(tb->cursor > 0) {
+ tb->cursor--;
+ }
+ if(tb->cursor < tb->scroll) {
+ tb->scroll = tb->cursor;
+ }
+ rtk_invalidate(w);
+ return;
+
+ case KEY_RIGHT:
+ if(tb->cursor < tb->len) {
+ tb->cursor++;
+ }
+ rtk_invalidate(w);
+ return;
+
+ default:
+ if(!isprint(key)) {
+ return;
+ }
+ break;
+ }
+
+ /* we end up here if it's a printable character */
+ if(tb->len >= tb->bufsz - 1) {
+ int nsz = tb->bufsz ? tb->bufsz * 2 : 16;
+ void *tmp = realloc(tb->text, nsz);
+ if(!tmp) return;
+ tb->text = tmp;
+ tb->bufsz = nsz;
+ }
+
+ if(tb->len <= 0 || tb->cursor >= tb->len) {
+ tb->text[tb->len++] = key;
+ tb->text[tb->len] = 0;
+ tb->cursor++;
+ } else {
+ memmove(tb->text + tb->cursor + 1, tb->text + tb->cursor, tb->len - tb->cursor + 1);
+ tb->text[tb->cursor++] = key;
+ tb->len++;
+ }
+
+ rtk_invalidate(w);
+}
+
+void rtk_dbg_showrect(rtk_widget *w, int show)
+{
+ if(show) {
+ w->flags |= DBGRECT;
+ } else {
+ w->flags &= ~DBGRECT;
+ }
+}
--- /dev/null
+#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_ */
--- /dev/null
+#include <string.h>
+#include "app.h"
+#include "rtk.h"
+#include "rtk_impl.h"
+
+rtk_draw_ops rtk_gfx;
+#define gfx rtk_gfx
+
+static int fontheight;
+
+enum {
+ FRM_SOLID,
+ FRM_OUTSET,
+ FRM_INSET,
+ FRM_FILLBG = 0x100
+};
+
+static void widget_rect(rtk_widget *w, rtk_rect *rect);
+static void abs_widget_rect(rtk_widget *w, rtk_rect *rect);
+static void uicolor(uint32_t col, uint32_t lcol, uint32_t scol);
+static void draw_frame(rtk_rect *rect, int type, int sz);
+
+static void draw_window(rtk_widget *w);
+static void draw_label(rtk_widget *w);
+static void draw_button(rtk_widget *w);
+static void draw_checkbox(rtk_widget *w);
+static void draw_textbox(rtk_widget *w);
+static void draw_slider(rtk_widget *w);
+static void draw_separator(rtk_widget *w);
+static void draw_drawbox(rtk_widget *w);
+
+
+#define BEVELSZ 1
+#define PAD (w->pad)
+#define OFFS (BEVELSZ + PAD)
+#define CHKBOXSZ (BEVELSZ * 2 + 8)
+
+#define WINFRM_SZ 2
+#define WINFRM_TBAR 16
+
+void rtk_init_drawing(void)
+{
+ const char *s = "QI|9g/";
+ rtk_rect r;
+
+ gfx.textrect(s, &r);
+ fontheight = r.height;
+}
+
+
+void rtk_calc_widget_rect(rtk_widget *w, rtk_rect *rect)
+{
+ rtk_widget *child;
+ rtk_button *bn;
+ rtk_rect txrect = {0};
+
+ rect->x = w->x;
+ rect->y = w->y;
+ rect->width = w->width;
+ rect->height = w->height;
+
+ rtk_abs_pos(w, &w->absx, &w->absy);
+
+ if((w->flags & (AUTOWIDTH | AUTOHEIGHT)) == 0) {
+ return;
+ }
+
+ if(w->text) {
+ gfx.textrect(w->text, &txrect);
+ }
+
+ switch(w->type) {
+ case RTK_WIN:
+ if((w->flags & (AUTOWIDTH | AUTOHEIGHT))) {
+ rtk_rect subrect;
+ rtk_window *win = (rtk_window*)w;
+ rtk_null_rect(&subrect);
+ child = win->clist;
+ while(child) {
+ rtk_rect crect;
+ widget_rect(child, &crect);
+ rtk_rect_union(&subrect, &crect);
+ child = child->next;
+ }
+ if(w->flags & AUTOWIDTH) {
+ rect->width = subrect.width + PAD * 2;
+ }
+ if(w->flags & AUTOHEIGHT) {
+ rect->height = subrect.height + PAD * 2;
+ }
+ }
+ break;
+
+ case RTK_DRAWBOX:
+ if(w->flags & AUTOWIDTH) {
+ rect->width = w->width;
+ }
+ if(w->flags & AUTOHEIGHT) {
+ rect->height = w->height;
+ }
+ break;
+
+ case RTK_BUTTON:
+ bn = (rtk_button*)w;
+ if(bn->icon) {
+ if(w->flags & AUTOWIDTH) {
+ rect->width = bn->icon->width + OFFS * 2;
+ }
+ if(w->flags & AUTOHEIGHT) {
+ rect->height = bn->icon->height + OFFS * 2;
+ }
+ } else {
+ if(w->flags & AUTOWIDTH) {
+ rect->width = txrect.width + OFFS * 2;
+ }
+ if(w->flags & AUTOHEIGHT) {
+ rect->height = txrect.height + OFFS * 2;
+ }
+ }
+ break;
+
+ case RTK_CHECKBOX:
+ if(w->flags & AUTOWIDTH) {
+ rect->width = txrect.width + CHKBOXSZ + OFFS * 2 + PAD;
+ }
+ if(w->flags & AUTOHEIGHT) {
+ rect->height = txrect.height + OFFS * 2;
+ }
+ break;
+
+ case RTK_LABEL:
+ if(w->flags & AUTOWIDTH) {
+ rect->width = txrect.width + PAD * 2;
+ }
+ if(w->flags & AUTOHEIGHT) {
+ rect->height = txrect.height + PAD * 2;
+ }
+ break;
+
+ case RTK_TEXTBOX:
+ if(w->flags & AUTOHEIGHT) {
+ if(rect->height < fontheight + OFFS * 2) {
+ rect->height = fontheight + OFFS * 2;
+ }
+ }
+ break;
+
+ case RTK_SEP:
+ if(w->par->layout == RTK_VBOX) {
+ if(w->flags & AUTOWIDTH) {
+ rect->width = w->par->width - PAD * 2;
+ }
+ if(w->flags & AUTOHEIGHT) {
+ rect->height = PAD * 4 + BEVELSZ * 2;
+ }
+ } else if(w->par->layout == RTK_HBOX) {
+ if(w->flags & AUTOWIDTH) {
+ rect->width = PAD * 4 + BEVELSZ * 2;
+ }
+ if(w->flags & AUTOHEIGHT) {
+ rect->height = w->par->height - PAD * 2;
+ }
+ } else {
+ if(w->flags & AUTOWIDTH) {
+ rect->width = 0;
+ }
+ if(w->flags & AUTOHEIGHT) {
+ rect->height = 0;
+ }
+ }
+ break;
+
+ default:
+ if(w->flags & AUTOWIDTH) {
+ rect->width = 0;
+ }
+ if(w->flags & AUTOHEIGHT) {
+ rect->height = 0;
+ }
+ }
+}
+
+void rtk_abs_pos(rtk_widget *w, int *xpos, int *ypos)
+{
+ int x, y, px, py;
+
+ x = w->x;
+ y = w->y;
+
+ if(w->par) {
+ rtk_abs_pos((rtk_widget*)w->par, &px, &py);
+ x += px;
+ y += py;
+ }
+
+ *xpos = x;
+ *ypos = y;
+}
+
+
+int rtk_hittest(rtk_widget *w, int x, int y)
+{
+ int x0, y0, x1, y1;
+
+ if(!(w->flags & VISIBLE)) {
+ return 0;
+ }
+
+ x0 = w->absx;
+ y0 = w->absy;
+ x1 = x0 + w->width;
+ y1 = y0 + w->height;
+
+ if(w->type == RTK_WIN && (w->flags & FRAME)) {
+ x0 -= WINFRM_SZ;
+ y0 -= WINFRM_SZ + WINFRM_TBAR;
+ x1 += WINFRM_SZ;
+ y1 += WINFRM_SZ;
+ }
+
+ if(x < x0 || y < y0) return 0;
+ if(x >= x1 || y >= y1) return 0;
+ return 1;
+}
+
+
+void rtk_invalfb(rtk_widget *w)
+{
+ rtk_rect rect;
+
+ rect.x = w->x;
+ rect.y = w->y;
+ rect.width = w->width;
+ rect.height = w->height;
+
+ rtk_abs_pos(w, &rect.x, &rect.y);
+
+ if(w->type == RTK_WIN && (w->flags & FRAME)) {
+ rect.x -= WINFRM_SZ;
+ rect.y -= WINFRM_SZ + WINFRM_TBAR;
+ rect.width += WINFRM_SZ * 2;
+ rect.height += WINFRM_SZ * 2 + WINFRM_TBAR;
+ }
+
+ app_invalidate(rect.x, rect.y, rect.width, rect.height);
+}
+
+void rtk_clearfb(rtk_widget *w)
+{
+ rtk_rect rect;
+
+ rect.x = w->x;
+ rect.y = w->y;
+ rect.width = w->width;
+ rect.height = w->height;
+
+ rtk_abs_pos(w, &rect.x, &rect.y);
+
+ if(w->type == RTK_WIN && (w->flags & FRAME)) {
+ rect.x -= WINFRM_SZ;
+ rect.y -= WINFRM_SZ + WINFRM_TBAR;
+ rect.width += WINFRM_SZ * 2;
+ rect.height += WINFRM_SZ * 2 + WINFRM_TBAR;
+ }
+
+ app_clear_rect(rect.x, rect.y, rect.width, rect.height);
+}
+
+static int need_relayout(rtk_widget *w)
+{
+ rtk_widget *c;
+
+ if(w->flags & GEOMCHG) {
+ return 1;
+ }
+
+ if(w->type == RTK_WIN) {
+ c = ((rtk_window*)w)->clist;
+ while(c) {
+ if(need_relayout(c)) {
+ return 1;
+ }
+ c = c->next;
+ }
+ }
+ return 0;
+}
+
+static void calc_layout(rtk_widget *w)
+{
+ int x, y, dx, dy;
+ rtk_widget *c;
+ rtk_window *win = (rtk_window*)w;
+ rtk_rect rect;
+
+ if(w->type == RTK_WIN) {
+ x = y = PAD;
+
+ c = win->clist;
+ while(c) {
+ if(win->layout != RTK_NONE) {
+ rtk_move(c, x, y);
+ calc_layout(c);
+
+ if(win->layout == RTK_VBOX) {
+ y += c->height + PAD * 2;
+ } else if(win->layout == RTK_HBOX) {
+ x += c->width + PAD * 2;
+ }
+ } else {
+ calc_layout(c);
+ }
+
+ c = c->next;
+ }
+ }
+
+ rtk_calc_widget_rect(w, &rect);
+ dx = rect.width - w->width;
+ dy = rect.height - w->height;
+ w->width = rect.width;
+ w->height = rect.height;
+
+ if((dx | dy) == 0) {
+ w->flags &= ~GEOMCHG;
+ }
+ rtk_invalidate(w);
+}
+
+static int calc_substr_width(const char *str, int start, int end)
+{
+ rtk_rect rect;
+ int len;
+ char *buf;
+
+ if(end <= start) {
+ return 0;
+ }
+ if(end <= 0) {
+ end = strlen(str);
+ }
+
+ len = end - start;
+ buf = alloca(len + 1);
+
+ memcpy(buf, str + start, len);
+ buf[len] = 0;
+
+ gfx.textrect(buf, &rect);
+ return rect.width;
+}
+
+void rtk_draw_widget(rtk_widget *w)
+{
+ int dirty;
+
+ if(!(w->flags & VISIBLE)) {
+ return;
+ }
+
+ if(need_relayout(w)) {
+ calc_layout(w);
+ }
+
+ dirty = w->flags & DIRTY;
+ if(!dirty && w->type != RTK_WIN) {
+ return;
+ }
+
+ switch(w->type) {
+ case RTK_WIN:
+ draw_window(w);
+ break;
+
+ case RTK_LABEL:
+ draw_label(w);
+ break;
+
+ case RTK_BUTTON:
+ draw_button(w);
+ break;
+
+ case RTK_CHECKBOX:
+ draw_checkbox(w);
+ break;
+
+ case RTK_TEXTBOX:
+ draw_textbox(w);
+ break;
+
+ case RTK_SLIDER:
+ draw_slider(w);
+ break;
+
+ case RTK_SEP:
+ draw_separator(w);
+ break;
+
+ case RTK_DRAWBOX:
+ draw_drawbox(w);
+ break;
+
+ default:
+ break;
+ }
+
+ if(w->drawcb) {
+ w->drawcb(w, w->drawcls);
+ }
+
+ if(w->flags & DBGRECT) {
+ rtk_rect r;
+ abs_widget_rect(w, &r);
+ uicolor(0xffff0000, 0xffff0000, 0xffff0000);
+ draw_frame(&r, FRM_SOLID, 1);
+ }
+
+ if(dirty) {
+ rtk_validate(w);
+ rtk_invalfb(w);
+ }
+}
+
+static void widget_rect(rtk_widget *w, rtk_rect *rect)
+{
+ rect->x = w->x;
+ rect->y = w->y;
+ rect->width = w->width;
+ rect->height = w->height;
+}
+
+static void abs_widget_rect(rtk_widget *w, rtk_rect *rect)
+{
+ rect->x = w->absx;
+ rect->y = w->absy;
+ rect->width = w->width;
+ rect->height = w->height;
+}
+
+#define COL_BG 0xff666666
+#define COL_BGHL 0xff808080
+#define COL_LBEV 0xffaaaaaa
+#define COL_SBEV 0xff222222
+#define COL_TEXT 0xff000000
+#define COL_WINFRM_FOCUS 0xff6688cc
+#define COL_WINFRM_LIT_FOCUS 0xff88aaff
+#define COL_WINFRM_SHAD_FOCUS 0xff224466
+#define COL_WINFRM 0xff667788
+#define COL_WINFRM_LIT 0xff8899aa
+#define COL_WINFRM_SHAD 0xff224455
+#define COL_TBOX 0xffeeccbb
+
+static void hline(int x, int y, int sz, uint32_t col)
+{
+ rtk_rect rect;
+ rect.x = x;
+ rect.y = y;
+ rect.width = sz;
+ rect.height = 1;
+ gfx.fill(&rect, col);
+}
+
+static void vline(int x, int y, int sz, uint32_t col)
+{
+ rtk_rect rect;
+ rect.x = x;
+ rect.y = y;
+ rect.width = 1;
+ rect.height = sz;
+ gfx.fill(&rect, col);
+}
+
+enum {UICOL_BG, UICOL_LBEV, UICOL_SBEV};
+static uint32_t uicol[3];
+
+static void uicolor(uint32_t col, uint32_t lcol, uint32_t scol)
+{
+ uicol[UICOL_BG] = col;
+ uicol[UICOL_LBEV] = lcol;
+ uicol[UICOL_SBEV] = scol;
+}
+
+static void draw_frame(rtk_rect *rect, int type, int sz)
+{
+ int i, tlcol, brcol, fillbg;
+ rtk_rect r = *rect;
+
+ fillbg = type & FRM_FILLBG;
+ type &= ~FRM_FILLBG;
+
+ switch(type) {
+ case FRM_OUTSET:
+ tlcol = uicol[UICOL_LBEV];
+ brcol = uicol[UICOL_SBEV];
+ break;
+ case FRM_INSET:
+ tlcol = uicol[UICOL_SBEV];
+ brcol = uicol[UICOL_LBEV];
+ break;
+ case FRM_SOLID:
+ default:
+ tlcol = brcol = uicol[UICOL_BG];
+ }
+
+ for(i=0; i<sz; i++) {
+ if(r.width < 2 || r.height < 2) break;
+
+ hline(r.x, r.y, r.width, tlcol);
+ vline(r.x, r.y + 1, r.height - 2, tlcol);
+ hline(r.x, r.y + r.height - 1, r.width, brcol);
+ vline(r.x + r.width - 1, r.y + 1, r.height - 2, brcol);
+
+ r.x++;
+ r.y++;
+ r.width -= 2;
+ r.height -= 2;
+ }
+
+ if(fillbg) {
+ gfx.fill(&r, uicol[UICOL_BG]);
+ }
+}
+
+static void draw_window(rtk_widget *w)
+{
+ rtk_rect rect, frmrect, tbrect;
+ rtk_widget *c;
+ rtk_window *win = (rtk_window*)w;
+ int win_dirty = w->flags & DIRTY;
+
+ if(win_dirty) {
+ abs_widget_rect(w, &rect);
+
+ if(w->flags & FRAME) {
+ if(w->flags & FOCUS) {
+ uicolor(COL_WINFRM_FOCUS, COL_WINFRM_LIT_FOCUS, COL_WINFRM_SHAD_FOCUS);
+ } else {
+ uicolor(COL_WINFRM, COL_WINFRM_LIT, COL_WINFRM_SHAD);
+ }
+
+ frmrect = rect;
+ frmrect.width += WINFRM_SZ * 2;
+ frmrect.height += WINFRM_SZ * 2 + WINFRM_TBAR;
+ frmrect.x -= WINFRM_SZ;
+ frmrect.y -= WINFRM_SZ + WINFRM_TBAR;
+
+ tbrect.x = rect.x;
+ tbrect.y = rect.y - WINFRM_TBAR;
+ tbrect.width = rect.width;
+ tbrect.height = WINFRM_TBAR;
+
+ draw_frame(&frmrect, FRM_OUTSET, 1);
+ frmrect.x++;
+ frmrect.y++;
+ frmrect.width -= 2;
+ frmrect.height -= 2;
+ draw_frame(&frmrect, FRM_INSET, 1);
+
+ draw_frame(&tbrect, FRM_OUTSET | FRM_FILLBG, 1);
+ tbrect.x++;
+ tbrect.y++;
+ tbrect.width -= 2;
+ tbrect.height -= 2;
+
+ gfx.drawtext(tbrect.x, tbrect.y + tbrect.height - 1, w->text);
+ }
+
+ gfx.fill(&rect, COL_BG);
+ }
+
+ c = win->clist;
+ while(c) {
+ if(win_dirty) {
+ rtk_invalidate(c);
+ }
+ rtk_draw_widget(c);
+ c = c->next;
+ }
+}
+
+static void draw_label(rtk_widget *w)
+{
+ rtk_rect rect;
+
+ abs_widget_rect(w, &rect);
+
+ gfx.drawtext(rect.x + PAD, rect.y + rect.height - PAD, w->text);
+}
+
+static void draw_button(rtk_widget *w)
+{
+ int pressed;
+ rtk_rect rect;
+ rtk_button *bn = (rtk_button*)w;
+
+ abs_widget_rect(w, &rect);
+
+ if(bn->mode == RTK_TOGGLEBN) {
+ pressed = w->value;
+ } else {
+ pressed = w->flags & PRESS;
+ }
+
+ uicolor(w->flags & HOVER ? COL_BGHL : COL_BG, COL_LBEV, COL_SBEV);
+
+ draw_frame(&rect, (pressed ? FRM_INSET : FRM_OUTSET) | FRM_FILLBG, 1);
+ rect.x++;
+ rect.y++;
+ rect.width -= 2;
+ rect.height -= 2;
+
+ if(bn->icon) {
+ int offs = w->flags & PRESS ? PAD + 1 : PAD;
+ gfx.blit(rect.x + offs, rect.y + offs, bn->icon);
+ } else if(w->text) {
+ gfx.drawtext(rect.x + PAD, rect.y + rect.height - PAD, w->text);
+ }
+}
+
+static void draw_checkbox(rtk_widget *w)
+{
+}
+
+static void draw_textbox(rtk_widget *w)
+{
+ rtk_rect rect;
+ rtk_textbox *tb = (rtk_textbox*)w;
+ int curx = 0;
+
+ abs_widget_rect(w, &rect);
+
+ uicolor(COL_TBOX, COL_LBEV, COL_SBEV);
+
+ draw_frame(&rect, FRM_INSET | FRM_FILLBG, w->flags & FOCUS ? 2 : 1);
+
+ rect.x++;
+ rect.y++;
+ rect.width -= 2;
+ rect.height -= 2;
+
+ if(w->text) {
+ gfx.drawtext(rect.x + PAD, rect.y + rect.height - PAD, w->text);
+
+ if(w->flags & FOCUS) {
+ curx = calc_substr_width(w->text, tb->scroll, tb->cursor);
+ }
+ }
+
+ /* cursor */
+ if(w->flags & FOCUS) {
+ int x = rect.x + PAD + curx - 1;
+ int y = rect.y + rect.height - PAD - fontheight;
+ vline(x, y, fontheight, 0xff000000);
+ }
+
+ rtk_invalfb(w);
+}
+
+static void draw_slider(rtk_widget *w)
+{
+}
+
+static void draw_separator(rtk_widget *w)
+{
+ rtk_window *win = (rtk_window*)w->par;
+ rtk_rect rect;
+
+ if(!win) return;
+
+ uicolor(COL_BG, COL_LBEV, COL_SBEV);
+
+ abs_widget_rect(w, &rect);
+
+ switch(win->layout) {
+ case RTK_VBOX:
+ rect.y += PAD * 2;
+ rect.height = 2;
+ break;
+
+ case RTK_HBOX:
+ rect.x += PAD * 2;
+ rect.width = 2;
+ break;
+
+ default:
+ break;
+ }
+
+ draw_frame(&rect, FRM_INSET, 1);
+}
+
+static void draw_drawbox(rtk_widget *w)
+{
+ if(!w->cbfunc) {
+ rtk_rect r;
+ abs_widget_rect(w, &r);
+ gfx.fill(&r, 0xff000000);
+ return;
+ }
+
+ w->cbfunc(w, w->cbcls);
+}
--- /dev/null
+#ifndef RTK_IMPL_H_
+#define RTK_IMPL_H_
+
+#include <assert.h>
+#include "sizeint.h"
+#include "rtk.h"
+
+enum {
+ VISIBLE = 0x0001,
+ ENABLED = 0x0002,
+ HOVER = 0x0010,
+ PRESS = 0x0020,
+ FOCUS = 0x0040,
+ GEOMCHG = 0x0100,
+ DIRTY = 0x0200,
+ CANFOCUS = 0x0400,
+ AUTOWIDTH = 0x1000,
+ AUTOHEIGHT = 0x2000,
+
+ /* window flags */
+ FRAME = RTK_WIN_FRAME << 16,
+ MOVABLE = RTK_WIN_MOVABLE << 16,
+ RESIZABLE = RTK_WIN_RESIZABLE << 16,
+
+ DBGRECT = 0x40000000
+};
+
+#define WIDGET_COMMON \
+ int type; \
+ int x, y, width, height; \
+ int absx, absy; \
+ int pad; \
+ char *text; \
+ int value; \
+ unsigned int flags; \
+ struct rtk_window *par; \
+ rtk_widget *next; \
+ rtk_callback cbfunc, drawcb; \
+ void *cbcls, *drawcls; \
+ void *udata; \
+ rtk_key_callback on_key; \
+ rtk_mbutton_callback on_mbutton; \
+ rtk_click_callback on_click; \
+ rtk_drag_callback on_drag; \
+ rtk_drop_callback on_drop; \
+ rtk_screen *scr
+
+typedef struct rtk_widget {
+ WIDGET_COMMON;
+} rtk_widget;
+
+typedef struct rtk_window {
+ WIDGET_COMMON;
+ rtk_widget *clist, *ctail;
+ int layout;
+} rtk_window;
+
+typedef struct rtk_button {
+ WIDGET_COMMON;
+ int mode;
+ rtk_icon *icon;
+} rtk_button;
+
+typedef struct rtk_textbox {
+ WIDGET_COMMON;
+ int cursor, scroll;
+ int len, bufsz;
+} rtk_textbox;
+
+typedef struct rtk_slider {
+ WIDGET_COMMON;
+ int vmin, vmax;
+} rtk_slider;
+
+typedef struct rtk_iconsheet {
+ int width, height;
+ uint32_t *pixels;
+
+ struct rtk_icon *icons;
+} rtk_iconsheet;
+
+#define MAX_WINDOWS 64
+
+typedef struct rtk_screen {
+ rtk_widget *winlist[MAX_WINDOWS];
+ int num_win;
+ rtk_widget *hover, *focus;
+ rtk_window *focuswin;
+ int prev_mx, prev_my;
+
+ rtk_widget *press; /* currently pressed widget */
+ int press_x, press_y; /* position of last mouse press */
+
+ rtk_widget *modal; /* which window is currently modal (null if none) */
+} rtk_screen;
+
+#define RTK_ASSERT_TYPE(w, t) assert(w->type == t)
+
+extern rtk_draw_ops rtk_gfx;
+
+void rtk_init_drawing(void);
+void rtk_calc_widget_rect(rtk_widget *w, rtk_rect *rect);
+void rtk_abs_pos(rtk_widget *w, int *xpos, int *ypos);
+int rtk_hittest(rtk_widget *w, int x, int y);
+void rtk_invalfb(rtk_widget *w);
+void rtk_clearfb(rtk_widget *w);
+
+
+#endif /* RTK_IMPL_H_ */
--- /dev/null
+#ifndef SIZEINT_H_
+#define SIZEINT_H_
+
+/* for C99 or selected toolchain versions we can use stdint.h */
+#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199900) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1600) || \
+ (defined(__WATCOMC__) && __WATCOMC__ >= 1200)
+#include <stdint.h>
+
+#elif defined(__DOS__) && defined(__WATCOMC__) && __WATCOMC__ < 1200
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef short int16_t;
+typedef unsigned short uint16_t;
+typedef long int32_t;
+typedef unsigned long uint32_t;
+typedef long intptr_t;
+typedef unsigned long uintptr_t;
+
+#elif defined(_MSC_VER) && (_MSC_VER < 1600)
+typedef signed __int8 int8_t;
+typedef unsigned __int8 uint8_t;
+typedef __int16 int16_t;
+typedef unsigned __int16 uint16_t;
+typedef __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef long intptr_t;
+typedef unsigned long uintptr_t;
+
+#else
+
+#ifdef __sgi
+#include <inttypes.h>
+#else
+#include <sys/types.h>
+#endif
+#endif
+
+
+#endif /* SIZEINT_H_ */
--- /dev/null
+#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_ */