diff options
Diffstat (limited to 'third_party/spiro/x3')
-rw-r--r-- | third_party/spiro/x3/Makefile | 25 | ||||
-rw-r--r-- | third_party/spiro/x3/pyrex/Makefile | 39 | ||||
-rw-r--r-- | third_party/spiro/x3/pyrex/beztest.py | 44 | ||||
-rw-r--r-- | third_party/spiro/x3/pyrex/main.py | 40 | ||||
-rw-r--r-- | third_party/spiro/x3/pyrex/x3.pyx | 259 | ||||
-rw-r--r-- | third_party/spiro/x3/test.c | 158 | ||||
-rw-r--r-- | third_party/spiro/x3/x3.h | 293 | ||||
-rw-r--r-- | third_party/spiro/x3/x3carbon.c | 1002 | ||||
-rw-r--r-- | third_party/spiro/x3/x3common.c | 359 | ||||
-rw-r--r-- | third_party/spiro/x3/x3common.h | 16 | ||||
-rw-r--r-- | third_party/spiro/x3/x3gtk.c | 778 | ||||
-rw-r--r-- | third_party/spiro/x3/x3win32.c | 174 |
12 files changed, 3187 insertions, 0 deletions
diff --git a/third_party/spiro/x3/Makefile b/third_party/spiro/x3/Makefile new file mode 100644 index 0000000..392417b --- /dev/null +++ b/third_party/spiro/x3/Makefile @@ -0,0 +1,25 @@ +TARGET = gtk + +ifeq ($(TARGET),gtk) + X3_PLAT = X3_GTK + X3_INCL = `pkg-config --cflags gtk+-2.0` + X3_LIBS = `pkg-config --libs gtk+-2.0` +endif + +ifeq ($(TARGET),carbon) + X3_PLAT = X3_CARBON + X3_LIBS = -framework Carbon +endif + +ifeq ($(TARGET),win32) + X3_PLAT = X3_WIN32 + X3_LIBS = -lgdi32 +endif + +CFLAGS = -g -Wall -D$(X3_PLAT) $(X3_INCL) +LDFLAGS = -g +LDLIBS = $(X3_LIBS) + +test: test.o x3$(TARGET).o x3common.o + +x3lisp.o: x3lisp.c diff --git a/third_party/spiro/x3/pyrex/Makefile b/third_party/spiro/x3/pyrex/Makefile new file mode 100644 index 0000000..d0324f3 --- /dev/null +++ b/third_party/spiro/x3/pyrex/Makefile @@ -0,0 +1,39 @@ +TARGET = gtk + +ifeq ($(TARGET),gtk) + X3_PLAT = X3_GTK + X3_INCL = `pkg-config --cflags gtk+-2.0` + X3_LIBS = `pkg-config --libs gtk+-2.0` + SHARED_FLAG = -shared +endif + +ifeq ($(TARGET),carbon) + X3_PLAT = X3_CARBON + X3_LIBS = -framework Carbon + SHARED_FLAG = -dynamiclib -flat_namespace -undefined suppress +endif + +ifeq ($(TARGET),win32) + X3_PLAT = X3_WIN32 + X3_LIBS = -lgdi32 +endif + +PY_INCL := -I$(shell python -c "import distutils.sysconfig; print distutils.sysconfig.get_python_inc()") + +CFLAGS = -g -Wall -fPIC -I.. $(PY_INCL) -D$(X3_PLAT) $(X3_INCL) +LDFLAGS = -g +LDLIBS = $(X3_LIBS) + +x3.so: x3.o x3$(TARGET).o x3common.o + gcc $(SHARED_FLAG) $^ $(X3_LIBS) -o $@ + +x3.c: x3.pyx + python2.4-pyrexc $< + +x3$(TARGET).o: ../x3$(TARGET).c + $(CC) -c $(CFLAGS) -o $@ $< + +x3common.o: ../x3common.c + $(CC) -c $(CFLAGS) -o $@ $< + + diff --git a/third_party/spiro/x3/pyrex/beztest.py b/third_party/spiro/x3/pyrex/beztest.py new file mode 100644 index 0000000..effd0d5 --- /dev/null +++ b/third_party/spiro/x3/pyrex/beztest.py @@ -0,0 +1,44 @@ +import x3 +from math import * + +def my_callback(cmd, what, arg, more): + print cmd, what, arg + +class bez: + def __init__(self): + self.coords = [(10, 10), (200, 10), (300, 200), (400, 100)] + self.hit = None + def draw(self, dc): + coords = self.coords + dc.setrgba(0, 0, 0.5, 1) + dc.moveto(coords[0][0], coords[0][1]) + dc.curveto(coords[1][0], coords[1][1], coords[2][0], coords[2][1], + coords[3][0], coords[3][1]) + dc.stroke() + dc.setrgba(0, 0.5, 0, 0.5) + dc.moveto(coords[0][0], coords[0][1]) + dc.lineto(coords[1][0], coords[1][1]) + dc.stroke() + dc.moveto(coords[2][0], coords[2][1]) + dc.lineto(coords[3][0], coords[3][1]) + dc.stroke() + dc.setrgba(1, 0, 0, .5) + for x, y in coords: + dc.rectangle(x - 3, y - 3, 6, 6) + dc.fill() + def mouse(self, button, mods, x, y): + if button == 1: + for i in range(4): + if hypot(x - self.coords[i][0], y - self.coords[i][1]) < 4: + self.hit = i + elif button == -1: + self.hit = None + elif self.hit != None: + self.coords[self.hit] = (x, y) + self.view.dirty() + +win = x3.window(0, "beztest", my_callback) + +x3.view(win, 259, bez()) + +x3.main() diff --git a/third_party/spiro/x3/pyrex/main.py b/third_party/spiro/x3/pyrex/main.py new file mode 100644 index 0000000..e784f46 --- /dev/null +++ b/third_party/spiro/x3/pyrex/main.py @@ -0,0 +1,40 @@ +import x3 + +def my_callback(cmd, what, arg, more): + print cmd, what, arg + +class my_viewclient: + def key(self, name, mods, code): + print name, mods, code + return 1 + def mouse(self, buttons, mods, x, y): + print buttons, mods, x, y + def draw(self, dc): + print 'rect:', dc.rect + dc.moveto(0, 0) + dc.lineto(100, 100) + print dc.currentpoint() + dc.stroke() + dc.selectfont("Nimbus Sans L", 0, 0) + dc.setfontsize(12) + dc.moveto(50, 10) + dc.showtext(u"\u00a1hello, world!") + print dc.textextents(u"\u00a1hello, world!") + +win = x3.window(0, "foo", my_callback) + +m = x3.menu(win, "bar") + +x3.menuitem(m, "baz", "bazz", "<ctrl>b") +x3.menusep(m) +x3.menuitem(m, "Quit", "quit", "<ctrl>q") + +v = x3.vbox(win, 0, 12) + +v.setpacking(True, False, 0) +x3.button(v, "butt", u"\u00a1hello!") +x3.edittext(v, "quux") +v.setpacking(True, True, 0) +x3.view(v, 263, my_viewclient()) + +x3.main() diff --git a/third_party/spiro/x3/pyrex/x3.pyx b/third_party/spiro/x3/pyrex/x3.pyx new file mode 100644 index 0000000..917df94 --- /dev/null +++ b/third_party/spiro/x3/pyrex/x3.pyx @@ -0,0 +1,259 @@ +cdef extern from "Python.h": + void *PyMem_Malloc(int size) except NULL + void Py_INCREF(object) + int PyUnicode_Check(object str) + object PyUnicode_AsUTF8String(object str) + +cdef extern from "x3.h": + ctypedef struct x3widget + ctypedef struct x3dc: + int x + int y + int width + int height + cdef enum x3windowflags: + x3window_main = 1 + x3window_dialog = 2 + ctypedef struct x3viewclient: + void (*destroy)(x3viewclient *self) + void (*mouse)(x3viewclient *self, int buttons, int mods, + double x, double y) + int (*key)(x3viewclient *self, char *keyname, int mods, int key) + void (*draw)(x3viewclient *self, x3dc *dc) + + # client extension + void *py_client + ctypedef struct x3extents: + double x_bearing + double y_bearing + double width + double height + double x_advance + double y_advance + void x3init(int argc, char **argv) + void x3main() + x3widget *x3window(x3windowflags flags, char *name, + int (callback)(x3widget *w, void *data, char *cmd, char *what, char *arg, void *more), + void *data) + x3widget *x3menu(x3widget *parent, char *name) + x3widget *x3menuitem(x3widget *parent, char *name, char *cmd, char *shortcut) + x3widget *x3menusep(x3widget *parent) + x3widget *x3align(x3widget *parent, int alignment) + x3widget *x3pad(x3widget *parent, int t, int b, int l, int r) + x3widget *x3vbox(x3widget *parent, int homogeneous, int spacing) + x3widget *x3hpane(x3widget *parent) + x3widget *x3vpane(x3widget *parent) + x3widget *x3button(x3widget *parent, char *name, char *label) + x3widget *x3label(x3widget *parent, char *text) + x3widget *x3edittext(x3widget *parent, char *cmd) + x3widget *x3view(x3widget *parent, int flags, x3viewclient *vc) + void x3viewclient_init(x3viewclient *vc) + void x3view_dirty(x3widget *w) + void x3view_scrollto(x3widget *w, int x, int y, int width, int height) + + void x3window_setdefaultsize(x3widget *w, int x, int y) + void x3setactive(x3widget *w, int active) + void x3setpacking(x3widget *w, int fill, int expand, int padding) + void x3pane_setsizing(x3widget *w, int c1r, int c1s, int c2r, int c2s) + int x3hasfocus(x3widget *w) + + void x3moveto(x3dc *dc, double x, double y) + void x3lineto(x3dc *dc, double x, double y) + void x3curveto(x3dc *dc, + double x1, double y1, + double x2, double y2, + double x3, double y3) + void x3closepath(x3dc *dc) + void x3rectangle(x3dc *dc, double x, double y, double width, double height) + void x3getcurrentpoint(x3dc *dc, double *px, double *py) + void x3setrgba(x3dc *dc, unsigned int rgba) + void x3setlinewidth(x3dc *dc, double w) + void x3fill(x3dc *dc) + void x3stroke(x3dc *dc) + void x3selectfont(x3dc *dc, char *fontname, int slant, int weight) + void x3setfontsize(x3dc *dc, double size) + void x3showtext(x3dc *dc, char *text) + void x3textextents(x3dc *dc, char *text, x3extents *extents) + X3_GMW(g, m, w) + +cdef object strmaybenull(char *str): + if str == NULL: + return None + else: + return str + +cdef object utf8(object ustr): + if PyUnicode_Check(ustr): + return PyUnicode_AsUTF8String(ustr) + else: + return ustr + +ctypedef struct x3viewclient_py: + x3viewclient base + # client extension + void *py_client + +cdef class widget: + cdef x3widget *w + def hasfocus(self): + return x3hasfocus(self.w) + +cdef int x3py_callback(x3widget *window, void *data, char *cmd, char *what, + char *arg, void *more): + callback = <object>data + callback(cmd, strmaybenull(what), strmaybenull(arg), None) + +cdef class window(widget): + def __new__(self, int flags, char *name, callback): + self.w = x3window(flags, name, x3py_callback, <void *>callback) + def setdefaultsize(self, int width, int height): + x3window_setdefaultsize(self.w, width, height) + +cdef class menu(widget): + def __new__(self, widget parent, name): + uname = utf8(name) + self.w = x3menu(parent.w, uname) + +cdef class menuitem(widget): + def __new__(self, widget parent, name, char *cmd, char *shortcut = NULL): + uname = utf8(name) + self.w = x3menuitem(parent.w, uname, cmd, shortcut) + +cdef class menusep(widget): + def __new__(self, widget parent): + self.w = x3menusep(parent.w) + +cdef class align(widget): + def __new__(self, widget parent, int alignment): + self.w = x3align(parent.w, alignment) + +cdef class pad(widget): + def __new__(self, widget parent, int t, int b, int l, int r): + self.w = x3pad(parent.w, t, b, l, r) + +cdef class vbox(widget): + def __new__(self, widget parent, homogeneous, int spacing): + self.w = x3vbox(parent.w, not not homogeneous, spacing) + def setpacking(self, fill, expand, int padding): + x3setpacking(self.w, not not fill, not not expand, padding) + +cdef class hpane(widget): + def __new__(self, widget parent): + self.w = x3hpane(parent.w) + def setsizing(self, c1r, c1s, c2r, c2s): + x3pane_setsizing(self.w, not not c1r, not not c1s, not not c2r, not not c2s) + +cdef class vpane(widget): + def __new__(self, widget parent): + self.w = x3vpane(parent.w) + def setsizing(self, c1r, c1s, c2r, c2s): + x3pane_setsizing(self.w, not not c1r, not not c1s, not not c2r, not not c2s) + +cdef class button(widget): + def __new__(self, widget parent, char *name, label): + ulabel = utf8(label) + self.w = x3button(parent.w, name, ulabel) + +cdef class label(widget): + def __new__(self, widget parent, text): + ulabel = utf8(text) + self.w = x3label(parent.w, ulabel) + +cdef class edittext(widget): + def __new__(self, widget parent, char *cmd): + self.w = x3edittext(parent.w, cmd) + +cdef int x3py_key(x3viewclient *self, char *keyname, int mods, int key): + py_client = <object>(<x3viewclient_py *>self).py_client + return py_client.key(keyname, mods, key) + +cdef void x3py_mouse(x3viewclient *self, int button, int mods, double x, double y): + py_client = <object>(<x3viewclient_py *>self).py_client + py_client.mouse(button, mods, x, y) + +cdef int dbl_to_byte(double x): + if x <= 0: return 0 + if x >= 1: return 255 + return x * 255 + 0.5 + +cdef class x3dc_wrap: + cdef x3dc *dc + + property rect: + def __get__(self): + return (self.dc.x, self.dc.y, self.dc.width, self.dc.height) + + def moveto(self, double x, double y): + x3moveto(self.dc, x, y) + def lineto(self, double x, double y): + x3lineto(self.dc, x, y) + def curveto(self, double x1, double y1, double x2, double y2, double x3, double y3): + x3curveto(self.dc, x1, y1, x2, y2, x3, y3) + def closepath(self): + x3closepath(self.dc) + def rectangle(self, double x, double y, double width, double height): + x3rectangle(self.dc, x, y, width, height) + def currentpoint(self): + cdef double x + cdef double y + x3getcurrentpoint(self.dc, &x, &y) + return (x, y) + def setrgba(self, double r, double g, double b, double a): + x3setrgba(self.dc, (dbl_to_byte(r) << 24) | + (dbl_to_byte(g) << 16) | + (dbl_to_byte(b) << 8) | + dbl_to_byte(a)) + def setlinewidth(self, double w): + x3setlinewidth(self.dc, w) + def fill(self): + x3fill(self.dc) + def stroke(self): + x3stroke(self.dc) + def selectfont(self, char *name, int slant, int weight): + x3selectfont(self.dc, name, slant, weight) + def setfontsize(self, double size): + x3setfontsize(self.dc, size) + def showtext(self, text): + utext = utf8(text) + x3showtext(self.dc, utext) + def textextents(self, text): + cdef x3extents ext + utext = utf8(text) + x3textextents(self.dc, utext, &ext) + return (ext.x_bearing, ext.y_bearing, + ext.width, ext.height, + ext.x_advance, ext.y_advance) + + +cdef void x3py_draw(x3viewclient *self, x3dc *dc): + cdef x3dc_wrap dc_wrap + py_client = <object>(<x3viewclient_py *>self).py_client + dc_wrap = x3dc_wrap() + dc_wrap.dc = dc + py_client.draw(dc_wrap) + +cdef class view(widget): + def __new__(self, widget parent, int flags, viewclient): + cdef x3viewclient_py *vc + vc = <x3viewclient_py *>PyMem_Malloc(sizeof(x3viewclient_py)) + x3viewclient_init(&vc.base) + vc.py_client = <void *>viewclient + Py_INCREF(viewclient) + vc.base.key = x3py_key + vc.base.mouse = x3py_mouse + vc.base.draw = x3py_draw + self.w = x3view(parent.w, flags, &vc.base) + viewclient.view = self + def dirty(self): + x3view_dirty(self.w) + def scrollto(self, int x, int y, int width, int height): + x3view_scrollto(self.w, x, y, width, height) + +def main(): + x3main() + +def gmw(g, m, w): + return X3_GMW(g, m, w) +platform = gmw("gtk", "mac", "win32") + +x3init(0, NULL) diff --git a/third_party/spiro/x3/test.c b/third_party/spiro/x3/test.c new file mode 100644 index 0000000..53fae33 --- /dev/null +++ b/third_party/spiro/x3/test.c @@ -0,0 +1,158 @@ +/* A test app for X3. */ + +#include "x3.h" +#include <stdlib.h> + +int +mycallback(x3widget *w, void *data, + char *cmd, char *what, char *arg, void *more) +{ + printf("my callback: cmd=\"%s\", what=\"%s\", arg=\"%s\"\n", + cmd, what ? what : "(null)", arg ? arg : "(null)"); + return 1; +} + +#if defined(X3_GTK) || defined(X3_CARBON) +static void test_viewclient_draw(x3viewclient *self, x3dc *dc) +{ + if (dc->buf) { + int i, j; + + for (i = 0; i < dc->height; i++) { + for (j = 0; j < dc->width; j++) { + dc->buf[i * dc->rowstride + j * 3] = i; + dc->buf[i * dc->rowstride + j * 3 + 1] = 0x80; + dc->buf[i * dc->rowstride + j * 3 + 2] = j; + } + } + } else { + x3extents ext; + + x3moveto(dc, 10, 10); + x3curveto(dc, 200, 10, 100, 100, 490, 100); + x3stroke(dc); + x3selectfont(dc, "Nimbus Roman No9 L", 0, 0); + x3moveto(dc, 10, 50); + x3setfontsize(dc, 16); + x3showtext(dc, "hello, world!"); + x3textextents(dc, "hello, world!", &ext); + printf("text extents: %g %g %g %g %g %g\n", + ext.x_bearing, ext.y_bearing, + ext.width, ext.height, + ext.x_advance, ext.y_advance); + } +} + +static int test_viewclient_key(x3viewclient *self, + char *keyname, int mods, int key) +{ + printf("view key: %s %d %d\n", keyname, mods, key); + return 1; +} + +static void test_viewclient_mouse(x3viewclient *self, + int button, int mods, + double x, double y) +{ + printf("view button: %d %d %g %g\n", button, mods, x, y); +} + +x3viewclient *test_viewclient(void) +{ + x3viewclient *result = (x3viewclient *)malloc(sizeof(x3viewclient)); + x3viewclient_init(result); + result->draw = test_viewclient_draw; + result->key = test_viewclient_key; + result->mouse = test_viewclient_mouse; + + return result; +} + +#ifdef X3_USEMAIN +int +main(int argc, char **argv) +{ + x3widget *mainwin; + x3widget *pane; + x3widget *vbox; + x3widget *m; + x3viewflags viewflags = x3view_click | x3view_hover | x3view_key; + + x3init(&argc, &argv); + mainwin = x3window(x3window_main, "untitled", mycallback, NULL); +#if 1 + m = x3menu(mainwin, "F\xc2\xa1le"); + x3menuitem(m, "Save", "save", "<cmd>s"); + x3setactive(x3menuitem(m, "Save As...", "sava", "<cmd>S"), 0); + x3menusep(m); + x3menuitem(m, "Preferences...", "pref", "<cmd>,"); + + m = x3menu(mainwin, "Edit"); + + x3menuitem(m, "ctrl-delete", "cdel", "<ctl>Delete"); + x3menuitem(m, "ctrl-f1", "cf1", "<ctl>F1"); + if (0) { + int i, j; + + for (j = 0; j < 3; j++) { + char mname[16]; + mname[0] = '0' + j; + mname[1] = 0; + m = x3menu(mainwin, mname); + for (i = 0x20 + 0x20 * j; i < 0x40 + 0x20 * j; i++) { + char name[16]; + + name[0] = i; + name[1] = 0; + x3menuitem(m, name, name, name); + } + } + } +#endif + +#if 1 + pane = x3vpane(mainwin); + vbox = x3vbox(x3pad(pane, 10, 10, 10, 10), 0, 5); + + x3setpacking(vbox, FALSE, FALSE, 0); + x3button(x3align(vbox, x3center), "foo", "Click me!"); + x3button(vbox, "bar", "Click me too!"); + x3setactive(x3button(vbox, "bar", "But not me"), 0); + x3label(vbox, "I am a label."); + x3edittext(vbox, "etxt"); +#endif + + x3setpacking(vbox, TRUE, TRUE, 0); + viewflags |= x3view_2d; + viewflags |= x3view_scroll; + x3view(pane, viewflags, test_viewclient()); + +#if 0 + x3_window_show(mainwin); +#endif + + x3main(); + return 0; +} +#endif +#endif + +#ifdef X3_USEWINMAIN +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, + int iCmdShow) +{ + //printf("winmain\n"); + x3widget *mainwin; + x3widget *vbox; + + x3init_win32(hInstance); + + mainwin = x3window(x3window_main, "untitled", mycallback, NULL); + x3_window_show(mainwin); + vbox = x3vbox(mainwin, 0, 5); + x3button(vbox, "foo", "Click me!"); + x3button(vbox, "bar", "And me too!"); + x3main(); + return 0; +} +#endif diff --git a/third_party/spiro/x3/x3.h b/third_party/spiro/x3/x3.h new file mode 100644 index 0000000..a056583 --- /dev/null +++ b/third_party/spiro/x3/x3.h @@ -0,0 +1,293 @@ +/* X3 is a lightweight toolset for building cross-platform applications. */ + +typedef struct _x3widget x3widget; + +typedef struct _x3viewclient x3viewclient; + +typedef enum { + x3window_main = 1, + x3window_dialog = 2, +} x3windowflags; + +typedef enum { + x3center = 0, + x3left = 1, + x3right = 2, + x3hfill = 3, + x3top = 4, + x3bottom = 8, + x3vfill = 12, + + x3topleft = 5, + x3topright = 6, + x3bottomleft = 9, + x3bottomright = 10, +} x3alignment; + +typedef enum { + x3view_click = 1, + x3view_hover = 2, + x3view_key = 4, + x3view_2d = 0x100, + x3view_rgb = 0x200, + x3view_scroll = 0x10000 +} x3viewflags; + +typedef struct _x3dc x3dc; + +struct _x3viewclient { + void (*destroy)(x3viewclient *self); + void (*mouse)(x3viewclient *self, int buttons, int mods, + double x, double y); + int (*key)(x3viewclient *self, char *keyname, int mods, int key); + void (*draw)(x3viewclient *self, x3dc *dc); +}; + +/* Windows and Carbon both use left/top/right/bottom nomenclature, + at least for int rects, while gtk+ uses x/y/width/height. */ +typedef struct { + double x0; + double y0; + double x1; + double y1; +} x3rect; + +#ifdef X3_GTK + +#include <gtk/gtk.h> + +#define X3_GMW(g, m, w) g + +struct _x3dc { + /* move to x3rect structure? */ + int x; + int y; + int width; + int height; + + cairo_t *cr; + + /* for rgb drawing */ + unsigned char *buf; + int rowstride; +}; + +struct _x3widget { + char *name; + x3widget *parent; + GtkWidget *widget; +}; + +typedef cairo_text_extents_t x3extents; + +#define X3_SHIFT_MASK GDK_SHIFT_MASK +#define X3_CONTROL_MASK GDK_CONTROL_MASK +#define X3_ALT_MASK GDK_MOD1_MASK +/* there is no X3_CMD_MASK in gtk */ + +#define X3_USEMAIN + +#endif + +#ifdef X3_CARBON + +#define X3_GMW(g, m, w) m + +#include <Carbon/Carbon.h> + +struct _x3dc { + /* move to x3rect structure? */ + int x; + int y; + int width; + int height; + + CGContextRef ctx; + CGMutablePathRef path; + + /* for rgb drawing */ + char *buf; + int rowstride; +}; + +typedef struct _x3type x3type; + +struct _x3type { + void (*sizereq)(x3widget *w); + void (*sizealloc)(x3widget *w, x3rect *r); + void (*add)(x3widget *w, x3widget *child); +}; + +typedef enum { + x3carbonnone, + x3carbonhiview, + x3carbonwindow, + x3carbonmenu, + x3carbonmenuitem, +} x3carbonvar; + +typedef enum { + x3flag_needsizereq = 1, + x3flag_needsizealloc = 2 +} x3widgetflags; + +struct _x3widget { + const x3type *type; + x3widgetflags flags; + x3rect sizerequest; + char *name; + x3widget *parent; + x3carbonvar var; + union { + HIViewRef hiview; + WindowRef window; + MenuRef menu; + int menuitem; + } u; + int n_children; + x3widget **children; +}; + +typedef struct { + double x_bearing; + double y_bearing; + double width; + double height; + double x_advance; + double y_advance; +} x3extents; + +#define X3_SHIFT_MASK shiftKey +#define X3_CONTROL_MASK controlKey +#define X3_CMD_MASK cmdKey +#define X3_ALT_MASK optionKey + +/* todo: figure out how to plumb mouse event */ +#define X3_BUTTON1_MASK 0 +#define X3_2BUTTON_PRESS 0 +#define X3_3BUTTON_PRESS 0 + +#define X3_USEMAIN + +#endif + +#ifdef X3_WIN32 + +#include <windows.h> + +#define X3_GMW(g, m, w) w + +typedef struct _x3type x3type; + +struct _x3type { + void (*sizereq)(x3widget *w); + void (*sizealloc)(x3widget *w, x3rect *r); + void (*add)(x3widget *w, x3widget *child); +}; + +typedef enum { + x3winnone, + x3winhwnd, +} x3winvar; + +typedef enum { + x3flag_needsizereq = 1, + x3flag_needsizealloc = 2 +} x3widgetflags; + +typedef struct { + double x_bearing; + double y_bearing; + double width; + double height; + double x_advance; + double y_advance; +} x3extents; + +struct _x3widget { + const x3type *type; + char *name; + x3widget *parent; + x3widgetflags flags; + x3rect sizerequest; + x3winvar var; // should this be in the type? + union { + HWND hwnd; + } u; + int n_children; + x3widget **children; +}; + +#define X3_USEWINMAIN + +void x3init_win32(HINSTANCE hInstance); + +#endif + +typedef int (*x3window_callback)(x3widget *window, void *data, + char *cmd, char *what, char *arg, + void *more); + +/* Main loop */ +void x3init(int *pargc, char ***pargv); +void x3main(void); + +x3widget *x3window(x3windowflags flags, char *label, + x3window_callback callback, void *callback_data); +x3widget *x3menu(x3widget *parent, char *name); +x3widget *x3menuitem(x3widget *parent, char *name, char *cmd, char *shortcut); +x3widget *x3menusep(x3widget *parent); +x3widget *x3align(x3widget *parent, x3alignment alignment); +x3widget *x3pad(x3widget *parent, int t, int b, int l, int r); +x3widget *x3vbox(x3widget *parent, int homogeneous, int spacing); +x3widget *x3hpane(x3widget *parent); +x3widget *x3vpane(x3widget *parent); +x3widget *x3button(x3widget *parent, char *name, char *label); +x3widget *x3label(x3widget *parent, char *label); +x3widget *x3edittext(x3widget *parent, char *cmd); +x3widget *x3view(x3widget *parent, x3viewflags flags, x3viewclient *vc); +void x3view_dirty(x3widget *w); +void x3view_scrollto(x3widget *w, int x, int y, int width, int height); + +void x3viewclient_init(x3viewclient *vc); + +void x3window_setdefaultsize(x3widget *w, int width, int height); + +void x3pane_setsizing(x3widget *w, int child1_resize, int child1_shrink, + int child2_resize, int child2_shrink); + +void x3setactive(x3widget *w, int active); +int x3hasfocus(x3widget *w); +void x3setpacking(x3widget *w, int fill, int expand, int padding); + +extern int x3n_winopen; + +/* 2d drawing functions */ + +void x3moveto(x3dc *dc, double x, double y); +void x3lineto(x3dc *dc, double x, double y); +void x3curveto(x3dc *dc, + double x1, double y1, + double x2, double y2, + double x3, double y3); +void x3closepath(x3dc *dc); +void x3rectangle(x3dc *dc, double x, double y, double width, double height); +void x3getcurrentpoint(x3dc *dc, double *px, double *py); +void x3setrgba(x3dc *dc, unsigned int rgba); +void x3setlinewidth(x3dc *dc, double w); +void x3fill(x3dc *dc); +void x3stroke(x3dc *dc); + +/* This is an overly simple text api, based on cairo's. It may be phased + out in favor of a more capable one. */ +void x3selectfont(x3dc *dc, char *fontname, int slant, int weight); +void x3setfontsize(x3dc *dc, double size); +void x3showtext(x3dc *dc, char *text); +void x3textextents(x3dc *dc, char *text, x3extents *extents); + +#if defined(X3_CARBON) || defined(X3_WIN32) +/* Internals for carbon/win32 */ +void x3widget_init(x3widget *w, const x3type *type); +void x3add_default(x3widget *parent, x3widget *child); +void x3add(x3widget *parent, x3widget *child); +#endif diff --git a/third_party/spiro/x3/x3carbon.c b/third_party/spiro/x3/x3carbon.c new file mode 100644 index 0000000..7929e47 --- /dev/null +++ b/third_party/spiro/x3/x3carbon.c @@ -0,0 +1,1002 @@ +#include "x3.h" +#include "x3common.h" + +/* Globals for managing sync */ + +#define VERBOSE + +#define kX3ViewClassID CFSTR("com.levien.x3.X3View") +#define kX3ViewPrivate 'X3_v' + +/* Some utility-type functions. */ + +UInt32 x3mkmultichar(const char *s) +{ + int len = strlen(s); + int i; + UInt32 result = 0; + + for (i = 0; i < (len > 4 ? 4 : len); i++) + result = (result << 8) + (unsigned char)s[i]; + for (; i < 4; i++) + result = (result << 8) + ' '; + return result; +} + +char *x3multicharstr(UInt32 mc, char buf[5]) +{ + int i, len; + + for (i = 0; i < 4; i++) + if (((mc >> (8 * i)) & 0xff) != ' ') break; + len = 4 - i; + for (i = 0; i < len; i++) + buf[i] = (mc >> (24 - 8 * i)) & 0xff; + buf[len] = 0; + return buf; +} + +void x3widget_init(x3widget *w, const x3type *type) +{ + w->type = type; + w->name = NULL; + w->parent = NULL; + w->var = x3carbonnone; + w->u.window = NULL; + w->n_children = 0; + w->children = NULL; +} + +static x3widget *x3widget_new_container(x3widget *parent, char *name, + const x3type *type) +{ + x3widget *result = (x3widget *)malloc(sizeof(x3widget)); + x3widget_init(result, type); + result->name = name ? strdup(name) : NULL; + x3add(parent, result); + x3qsizereq(result); + return result; +} + +static x3widget *x3widget_new_hiview(x3widget *parent, char *name, + const x3type *type, + HIViewRef hiview) +{ + x3widget *result = (x3widget *)malloc(sizeof(x3widget)); + x3widget_init(result, type); + result->name = name ? strdup(name) : NULL; + result->var = x3carbonhiview; + result->u.hiview = hiview; + x3add(parent, result); + x3qsizereq(result); + return result; +} + +static x3widget *x3widget_new_menu(x3widget *parent, + const x3type *type, + MenuRef menu) +{ + x3widget *result = (x3widget *)malloc(sizeof(x3widget)); + x3widget_init(result, type); + result->var = x3carbonmenu; + result->u.menu = menu; + x3add(parent, result); + return result; +} + +static x3widget *x3widget_new_menuitem(x3widget *parent, + const x3type *type, int index) +{ + x3widget *result = (x3widget *)malloc(sizeof(x3widget)); + x3widget_init(result, type); + x3add(parent, result); + result->var = x3carbonmenuitem; + result->u.menuitem = index; + return result; +} + +void x3init(int *pargc, char ***pargv) +{ + ProcessSerialNumber psn; + + /* Most apps get this done from loading the menu bar from the NIB. + Prior to 10.3, there was an undocumented call. This one is official + for 10.3 and later. */ + GetCurrentProcess(&psn); + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + SetFrontProcess(&psn); + + x3initqs(); +} + +void x3main(void) +{ + x3sync(); + RunApplicationEventLoop(); +} + +void x3_window_show(x3widget *mainwin) +{ + ControlRef root; +#if 0 + TransitionWindow(mainwin->u.window, kWindowZoomTransitionEffect, + kWindowShowTransitionAction, NULL); +#endif + +#if 0 + CreateRootControl(mainwin->u.window, &root); +#endif + //RepositionWindow(mainwin->u.window, NULL, kWindowCascadeOnMainScreen); + ShowWindow(mainwin->u.window); + SelectWindow(mainwin->u.window); +} + +static WindowRef x3window_of(x3widget *w) +{ + while (w->parent) w = w->parent; + return w->var == x3carbonwindow ? w->u.window : NULL; +} + +typedef struct { + x3widget base; + x3window_callback callback; + void *callback_data; +} x3widget_window; + +pascal OSStatus x3carbonWindowEventHandler(EventHandlerCallRef cr, + EventRef inEvent, void *data) +{ + x3widget_window *z = (x3widget_window *)data; + OSStatus result = noErr; + UInt32 eclass = GetEventClass(inEvent); + UInt32 ekind = GetEventKind(inEvent); + char multicharbuf[5]; + + if (eclass == kEventClassCommand && ekind == kEventCommandProcess) { + HICommand command; + int status; + + GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, + NULL, sizeof(HICommand), NULL, &command); + status = z->callback(&z->base, z->callback_data, + x3multicharstr(command.commandID, multicharbuf), + "command", NULL, NULL); + if (status == 1) + result = eventNotHandledErr; + } else if (eclass = kEventClassWindow && ekind == kEventWindowBoundsChanged) { + /* todo: only queue size request when size changes, not just pos */ + x3qsizereq(&z->base); + x3sync(); + } else { + printf("My handler is getting called %s %d!\n", + x3multicharstr(eclass, multicharbuf), + GetEventKind(inEvent)); + } + result = eventNotHandledErr; + return result; +} + +void x3window_sizereq(x3widget *w) +{ +} + +void x3window_sizealloc(x3widget *w, x3rect *r) +{ + int i; + Rect bounds; + x3rect child_r; + + GetWindowBounds(w->u.window, kWindowContentRgn, &bounds); + child_r.x0 = 0; + child_r.x1 = bounds.right - bounds.left; + child_r.y0 = 0; + child_r.y1 = bounds.bottom - bounds.top; + printf("x3window_sizealloc (%d, %d) - (%d, %d)\n", + bounds.left, bounds.top, bounds.right, bounds.bottom); + for (i = 0; i < w->n_children; i++) { + x3widget *child = w->children[i]; + if (child->type->sizealloc) + child->type->sizealloc(child, &child_r); + child->flags &= ~x3flag_needsizealloc; + } +} + +x3type x3windowtype = { x3window_sizereq, + x3window_sizealloc, + x3add_default }; + +x3widget *x3window(x3windowflags flags, char *label, + x3window_callback callback, void *data) +{ + WindowRef window; + Rect bounds = { 100, 100, 400, 600 }; + EventHandlerRef handlerRef; + WindowAttributes attrs = + kWindowCompositingAttribute | + kWindowLiveResizeAttribute | + kWindowInWindowMenuAttribute | + kWindowFrameworkScaledAttribute | + kWindowStandardHandlerAttribute; + EventTypeSpec windowEvents[] = { + { kEventClassCommand, kEventCommandProcess }, + //{ kEventClassCommand, kEventCommandUpdateStatus }, + + //{ kEventClassMouse, kEventMouseDown }, + + //{ kEventClassWindow, kEventWindowClose }, + //{ kEventClassWindow, kEventWindowGetIdealSize }, + { kEventClassWindow, kEventWindowBoundsChanged }, + //{ kEventClassWindow, kEventWindowGetClickActivation }, + //{ kEventClassWindow, kEventWindowContextualMenuSelect } + }; + CFStringRef cflabel = CFStringCreateWithCString(NULL, label, + kCFStringEncodingUTF8); + x3widget *result = (x3widget *)malloc(sizeof(x3widget_window)); + + if (flags & x3window_main) + attrs |= kWindowStandardDocumentAttributes; + + OSStatus err = CreateNewWindow(kDocumentWindowClass, attrs, + &bounds, &window); + + SetWindowTitleWithCFString(window, cflabel); + CFRelease(cflabel); + x3widget_init(result, &x3windowtype); + result->var = x3carbonwindow; + result->u.window = window; + ((x3widget_window *)result)->callback = callback; + ((x3widget_window *)result)->callback_data = data; + + InstallWindowEventHandler(window, NewEventHandlerUPP(x3carbonWindowEventHandler), + sizeof(windowEvents)/sizeof(EventTypeSpec), + windowEvents, result, &handlerRef); + + x3qshow(result); + return result; +} + +x3type x3menutype = { NULL, NULL, x3add_default }; + +x3widget *x3menu(x3widget *parent, char *name) +{ + static int id = 1; /* Note: menu id should probably be kept per-window */ + MenuRef menu; + CFStringRef cflabel = CFStringCreateWithCString(NULL, name, + kCFStringEncodingUTF8); + + CreateNewMenu(id++, 0, &menu); + SetMenuTitleWithCFString(menu, cflabel); + CFRelease(cflabel); + InsertMenu(menu, 0); + return x3widget_new_menu(parent, &x3menutype, menu); +} + +int x3parseshortcut(const char *shortcut, UInt16 *pkey, UInt8 *pmods) +{ + UInt16 key; + UInt8 mods = kMenuNoCommandModifier; + int i = 0; + + while (shortcut[i] == '<') { + if (!strncmp(shortcut + i, "<cmd>", 5)) { + mods &= ~kMenuNoCommandModifier; + i += 5; + } else if (!strncmp(shortcut + i, "<shift>", 7)) { + mods |= kMenuShiftModifier; + i += 7; + } else if (!strncmp(shortcut + i, "<option>", 8)) { + mods |= kMenuOptionModifier; + i += 8; + } else if (!strncmp(shortcut + i, "<ctrl>", 6)) { + mods |= kMenuControlModifier; + i += 6; + } else + return false; + } + if (shortcut[i] && shortcut[i + 1] == 0) { + key = shortcut[i]; + if (key >= 'a' && key <= 'z') key -= 'a' - 'A'; + else if (key >= 'A' && key <= 'Z') mods |= kMenuShiftModifier; + } else + return false; + + *pkey = key; + *pmods = mods; + return true; +} + +x3type x3menuitemtype = { NULL, NULL, x3add_default }; + +x3widget *x3menuitem(x3widget *parent, char *name, char *cmd, char *shortcut) +{ + CFStringRef cflabel = CFStringCreateWithCString(NULL, name, + kCFStringEncodingUTF8); + MenuItemIndex index; + + AppendMenuItemTextWithCFString(parent->u.menu, cflabel, + 0, + x3mkmultichar(cmd), &index); + if (shortcut) { + UInt16 key; + UInt8 mods; + + if (x3parseshortcut(shortcut, &key, &mods)) { + SetMenuItemCommandKey(parent->u.menu, index, false, key); + SetMenuItemModifiers(parent->u.menu, index, mods); + } + } + CFRelease(cflabel); + return x3widget_new_menuitem(parent, &x3menuitemtype, index); +} + +x3widget *x3menusep(x3widget *parent) +{ + Str255 str = {1, '-'}; + + AppendMenu(parent->u.menu, str); + return x3widget_new_menuitem(parent, &x3menuitemtype, -1); +} + +void x3button_sizereq(x3widget *w) +{ + SInt16 offset; + Rect r = { 0, 0, 0, 0 }; + + GetBestControlRect(w->u.hiview, &r, &offset); + w->sizerequest.x0 = r.left; + w->sizerequest.y0 = r.top; + w->sizerequest.x1 = r.right; + w->sizerequest.y1 = r.bottom; +#ifdef VERBOSE + printf("button sizereq = (%g, %g) - (%g, %g)\n", + w->sizerequest.x0, w->sizerequest.y0, + w->sizerequest.x1, w->sizerequest.y1); +#endif +} + +void x3button_sizealloc(x3widget *w, x3rect *r) +{ + Rect bounds; + + bounds.left = r->x0; + bounds.top = r->y0; + bounds.right = r->x1; + bounds.bottom = r->y1; + /* TODO probably want to use HIViewSetFrame instead */ + printf("button sizealloc = (%g, %g) - (%g, %g)\n", + r->x0, r->y0, r->x1, r->y1); + SetControlBounds(w->u.hiview, &bounds); +} + +x3type x3buttontype = { x3button_sizereq, + x3button_sizealloc, + x3add_default }; + +x3widget *x3button(x3widget *parent, char *cmd, char *label) +{ + WindowRef window = x3window_of(parent); + Rect r = {10, 10, 30, 100}; + ControlRef control; + OSStatus err; + CFStringRef cflabel = CFStringCreateWithCString(NULL, label, + kCFStringEncodingUTF8); + + err = CreatePushButtonControl(window, &r, cflabel, &control); + CFRelease(cflabel); + SetControlCommandID(control, x3mkmultichar(cmd)); + //SetWindowDefaultButton(window, control); + return x3widget_new_hiview(parent, cmd, &x3buttontype, control); +} + +x3widget *x3label(x3widget *parent, char *text) +{ + WindowRef window = x3window_of(parent); + Rect r = {10, 10, 30, 100}; + ControlRef control; + OSStatus err; + Boolean singleline = true; + CFStringRef cftext = CFStringCreateWithCString(NULL, text, + kCFStringEncodingUTF8); + + err = CreateStaticTextControl(window, &r, cftext, + NULL, &control); + CFRelease(cftext); +#if 0 + SetControlData(control, kControlEntireControl, + kControlEditTextSingleLineTag, sizeof(Boolean), + &singleline); +#endif + return x3widget_new_hiview(parent, NULL, &x3buttontype, control); +} + +x3widget *x3edittext(x3widget *parent, char *cmd) +{ + WindowRef window = x3window_of(parent); + Rect r = {10, 10, 30, 100}; + ControlRef control; + OSStatus err; + Boolean singleline = true; + + err = CreateEditUnicodeTextControl(window, &r, CFSTR(""), + false, NULL, &control); + SetControlCommandID(control, x3mkmultichar(cmd)); +#if 0 + SetControlData(control, kControlEntireControl, + kControlEditTextSingleLineTag, sizeof(Boolean), + &singleline); +#endif + return x3widget_new_hiview(parent, cmd, &x3buttontype, control); +} + +x3widget *x3hpane(x3widget *parent) +{ + return NULL; +} + +x3widget *x3vpane(x3widget *parent) +{ + return NULL; +} + +typedef struct +{ + HIViewRef view; + x3viewflags flags; + x3viewclient *vc; +} x3view_data; + +static OSStatus +x3view_construct(EventRef inEvent) +{ + OSStatus err; + x3view_data *data; + + data = (x3view_data *)malloc(sizeof(x3view_data)); + require_action(data != NULL, CantMalloc, err = memFullErr); + err = GetEventParameter(inEvent, kEventParamHIObjectInstance, + typeHIObjectRef, NULL, sizeof(HIObjectRef), NULL, + (HIObjectRef *)&data->view); + require_noerr(err, ParameterMissing); + err = SetEventParameter(inEvent, kEventParamHIObjectInstance, + typeVoidPtr, sizeof(x3view_data *), &data); + + data->vc = NULL; + + ParameterMissing: + if (err != noErr) + free(data); + + CantMalloc: + return err; +} + +static OSStatus +x3view_destruct(EventRef inEvent, x3view_data *inData) +{ + free(inData); + return noErr; +} + +static OSStatus +x3view_initialize(EventHandlerCallRef inCallRef, EventRef inEvent, + x3view_data *inData) +{ + OSStatus err; + HIRect bounds; + + err = CallNextEventHandler(inCallRef, inEvent); + require_noerr(err, TroubleInSuperClass); + + err = GetEventParameter(inEvent, 'Boun', typeHIRect, + NULL, sizeof(HIRect), NULL, &bounds); + require_noerr(err, ParameterMissing); + + HIViewSetFrame(inData->view, &bounds); + + ParameterMissing: + TroubleInSuperClass: + return err; +} + +static OSStatus +x3view_draw(EventRef inEvent, x3view_data *inData) +{ + OSStatus err; + CGContextRef ctx; + + err = GetEventParameter(inEvent, kEventParamCGContextRef, typeCGContextRef, + NULL, sizeof(CGContextRef), NULL, &ctx); + require_noerr(err, ParameterMissing); + +#ifdef VERBOSE + printf("x3view_draw!\n"); +#endif + + if (inData->vc && inData->vc->draw) { + x3dc dc; + + /* set up bounds */ + if (inData->flags & x3view_2d) { + dc.ctx = ctx; + dc.path = NULL; + dc.buf = NULL; + + inData->vc->draw(inData->vc, &dc); + if (dc.path) { + CGPathRelease(dc.path); + } + } else if (inData->flags & x3view_rgb) { + /* todo */ + } + } + + ParameterMissing: + return err; +} + +static OSStatus +x3view_get_data(EventRef inEvent, x3view_data *inData) +{ + OSStatus err; + OSType tag; + Ptr ptr; + Size outSize; + + /* Probably could use a bit more error checking here, for type + and size match. Also, just returning an x3view_data seems a + little hacky. */ + err = GetEventParameter(inEvent, kEventParamControlDataTag, typeEnumeration, + NULL, sizeof(OSType), NULL, &tag); + require_noerr(err, ParameterMissing); + + err = GetEventParameter(inEvent, kEventParamControlDataBuffer, typePtr, + NULL, sizeof(Ptr), NULL, &ptr); + + if (tag == kX3ViewPrivate) { + *((x3view_data **)ptr) = inData; + outSize = sizeof(x3view_data *); + } else + err = errDataNotSupported; + + if (err == noErr) + err = SetEventParameter(inEvent, kEventParamControlDataBufferSize, typeLongInteger, + sizeof(Size), &outSize); + + ParameterMissing: + return err; +} + +static OSStatus +x3view_set_data(EventRef inEvent, x3view_data *inData) +{ + OSStatus err; + Ptr ptr; + OSType tag; + + err = GetEventParameter(inEvent, kEventParamControlDataTag, typeEnumeration, + NULL, sizeof(OSType), NULL, &tag); + require_noerr(err, ParameterMissing); + + err = GetEventParameter(inEvent, kEventParamControlDataBuffer, typePtr, + NULL, sizeof(Ptr), NULL, &ptr); + require_noerr(err, ParameterMissing); + + if (tag == 'X3vc') { + inData->vc = *(x3viewclient **)ptr; + } else if (tag == 'X3vf') { + inData->flags = *(x3viewflags *)ptr; + } else + err = errDataNotSupported; + + ParameterMissing: + return err; +} + +static OSStatus +x3view_hittest(EventRef inEvent, x3view_data *inData) +{ + OSStatus err; + HIPoint where; + HIRect bounds; + ControlPartCode part; + + err = GetEventParameter(inEvent, kEventParamMouseLocation, typeHIPoint, + NULL, sizeof(HIPoint), NULL, &where); + require_noerr(err, ParameterMissing); + + err = HIViewGetBounds(inData->view, &bounds); + require_noerr(err, ParameterMissing); + + if (CGRectContainsPoint(bounds, where)) + part = 1; + else + part = kControlNoPart; + err = SetEventParameter(inEvent, kEventParamControlPart, + typeControlPartCode, sizeof(ControlPartCode), + &part); + printf("hittest %g, %g!\n", where.x, where.y); + + ParameterMissing: + return err; +} + +/* + If we need more sophisticated tracking (like mixing key events), there's + a good discussion here: + http://lists.apple.com/archives/Carbon-development/2001/Apr/msg01688.html +*/ +static OSStatus +x3view_track(EventRef inEvent, x3view_data *inData) +{ + OSStatus err; + HIPoint where; + MouseTrackingResult mouseStatus; + HIRect bounds; + Rect windBounds; + Point theQDPoint; + UInt32 mods; + + err = GetEventParameter(inEvent, kEventParamMouseLocation, typeHIPoint, + NULL, sizeof(HIPoint), NULL, &where); + require_noerr(err, ParameterMissing); + + err = GetEventParameter(inEvent, kEventParamKeyModifiers, typeUInt32, + NULL, sizeof(UInt32), NULL, &mods); + require_noerr(err, ParameterMissing); + + err = HIViewGetBounds(inData->view, &bounds); + require_noerr(err, ParameterMissing); + + GetWindowBounds(GetControlOwner(inData->view), kWindowStructureRgn, &windBounds); + + if (inData->vc && inData->vc->mouse) + inData->vc->mouse(inData->vc, 1, mods, where.x, where.y); + +#ifdef VERBOSE + printf("press: %g, %g!\n", where.x, where.y); +#endif + //pe_view_button_press(inData, where.x, where.y, 0); + + mouseStatus = kMouseTrackingMouseDown; + while (mouseStatus != kMouseTrackingMouseUp) { + TrackMouseLocation(NULL, &theQDPoint, &mouseStatus); + where.x = theQDPoint.h - windBounds.left; + where.y = theQDPoint.v - windBounds.top; + HIViewConvertPoint(&where, NULL, inData->view); +#ifdef VERBOSE + printf("track %d: %g, %g!\n", mouseStatus, where.x, where.y); +#endif + if (mouseStatus == kMouseTrackingMouseUp) { + if (inData->vc && inData->vc->mouse) + inData->vc->mouse(inData->vc, -1, mods, where.x, where.y); + } else if (mouseStatus == kMouseTrackingKeyModifiersChanged) { + mods = GetCurrentEventKeyModifiers(); + if (inData->vc && inData->vc->mouse) + inData->vc->mouse(inData->vc, 0, mods, where.x, where.y); + } else { + if (inData->vc && inData->vc->mouse) + inData->vc->mouse(inData->vc, 0, mods, where.x, where.y); + } + } + + ParameterMissing: + return err; +} + +pascal OSStatus +x3view_handler(EventHandlerCallRef inCallRef, + EventRef inEvent, + void* inUserData ) +{ + OSStatus err = eventNotHandledErr; + UInt32 eventClass = GetEventClass(inEvent); + UInt32 eventKind = GetEventKind(inEvent); + x3view_data *data = (x3view_data *)inUserData; + + printf("view handler %c%c%c%c %d\n", + (eventClass >> 24) & 0xff, + (eventClass >> 16) & 0xff, + (eventClass >> 8) & 0xff, + (eventClass >> 0) & 0xff, + eventKind); + switch (eventClass) { + case kEventClassHIObject: + switch (eventKind) { + case kEventHIObjectConstruct: + err = x3view_construct(inEvent); + break; + case kEventHIObjectInitialize: + err = x3view_initialize(inCallRef, inEvent, data); + break; + case kEventHIObjectDestruct: + err = x3view_destruct(inEvent, data); + break; + } + break; + case kEventClassControl: + switch (eventKind) { + case kEventControlInitialize: + err = noErr; + break; + case kEventControlDraw: + err = x3view_draw(inEvent, data); + break; + case kEventControlGetData: + err = x3view_get_data(inEvent, data); + break; + case kEventControlSetData: + err = x3view_set_data(inEvent, data); + break; + case kEventControlTrack: + err = x3view_track(inEvent, data); + break; + case kEventControlHitTest: + err = x3view_hittest(inEvent, data); + break; + case kEventControlClick: + printf("click event\n"); + break; + /*...*/ + } + break; + } + return err; +} + +static OSStatus +x3view_register(void) +{ + OSStatus err = noErr; + static HIObjectClassRef x3view_ClassRef = NULL; + + if (x3view_ClassRef == NULL) { + EventTypeSpec eventList[] = { + { kEventClassHIObject, kEventHIObjectConstruct }, + { kEventClassHIObject, kEventHIObjectInitialize }, + { kEventClassHIObject, kEventHIObjectDestruct }, + + { kEventClassControl, kEventControlActivate }, + { kEventClassControl, kEventControlDeactivate }, + { kEventClassControl, kEventControlDraw }, + { kEventClassControl, kEventControlHiliteChanged }, + { kEventClassControl, kEventControlHitTest }, + { kEventClassControl, kEventControlInitialize }, + { kEventClassControl, kEventControlGetData }, + { kEventClassControl, kEventControlSetData }, + { kEventClassControl, kEventControlTrack }, + { kEventClassControl, kEventControlClick } + }; + err = HIObjectRegisterSubclass(kX3ViewClassID, + kHIViewClassID, + 0, + x3view_handler, + GetEventTypeCount(eventList), + eventList, + NULL, + &x3view_ClassRef); + } + return err; +} + +OSStatus x3view_create( + WindowRef inWindow, + const HIRect* inBounds, + HIViewRef* outView) +{ + OSStatus err; + EventRef event; + + err = x3view_register(); + require_noerr(err, CantRegister); + + err = CreateEvent(NULL, kEventClassHIObject, kEventHIObjectInitialize, + GetCurrentEventTime(), 0, &event); + require_noerr(err, CantCreateEvent); + + if (inBounds != NULL) { + err = SetEventParameter(event, 'Boun', typeHIRect, sizeof(HIRect), + inBounds); + require_noerr(err, CantSetParameter); + } + + err = HIObjectCreate(kX3ViewClassID, event, (HIObjectRef*)outView); + require_noerr(err, CantCreate); + + if (inWindow != NULL) { + HIViewRef root; + err = GetRootControl(inWindow, &root); + require_noerr(err, CantGetRootView); + err = HIViewAddSubview(root, *outView); + } + CantCreate: + CantGetRootView: + CantSetParameter: + CantCreateEvent: + ReleaseEvent(event); + CantRegister: + return err; +} + +x3widget *x3view(x3widget *parent, x3viewflags flags, x3viewclient *vc) +{ + WindowRef window = x3window_of(parent); + HIRect r = { {10, 10}, {30, 100} }; + OSStatus err; + HIViewRef view; + + err = x3view_create(window, &r, &view); + err = SetControlData(view, 1, 'X3vc', sizeof(x3view_data *), &vc); + err = SetControlData(view, 1, 'X3vf', sizeof(x3viewflags), &flags); + HIViewSetVisible(view, true); + return x3widget_new_hiview(parent, NULL, &x3buttontype, view); +} + +void +x3view_dirty(x3widget *w) +{ + if (w->var == x3carbonhiview) + HIViewSetNeedsDisplay(w->u.hiview, true); +} + +void x3view_scrollto(x3widget *w, int x, int y, int width, int height) +{ + /* todo */ +} + +void x3viewclient_init(x3viewclient *vc) +{ + vc->destroy = NULL; + vc->mouse = NULL; + vc->key = NULL; + vc->draw = NULL; +} + +/* Functions for manipulating widget state - some fairly polymorphic. */ + +void x3setactive(x3widget *w, int active) +{ + if (w->var == x3carbonmenuitem) { + MenuRef menu = w->parent->u.menu; + if (active) + EnableMenuItem(menu, w->u.menuitem); + else + DisableMenuItem(menu, w->u.menuitem); + /* According to Carbon docs, we need to redraw menu. */ + } else if (w->var == x3carbonhiview) { + if (active) + ActivateControl(w->u.hiview); + else + DeactivateControl(w->u.hiview); + } +} + +int x3hasfocus(x3widget *w) +{ + if (w->var == x3carbonhiview) { + return HIViewSubtreeContainsFocus(w->u.hiview); + } else + return 0; +} + +/* 2d drawing functions, implemented using Quartz */ + +void +x3moveto(x3dc *dc, double x, double y) +{ + if (dc->path == NULL) { + dc->path = CGPathCreateMutable(); + } + CGPathMoveToPoint(dc->path, NULL, x, y); +} + +void +x3lineto(x3dc *dc, double x, double y) +{ + CGPathAddLineToPoint(dc->path, NULL, x, y); +} + +void +x3curveto(x3dc *dc, + double x1, double y1, + double x2, double y2, + double x3, double y3) +{ + CGPathAddCurveToPoint(dc->path, NULL, x1, y1, x2, y2, x3, y3); +} + +void +x3closepath(x3dc *dc) +{ + CGPathCloseSubpath(dc->path); +} + +void +x3rectangle(x3dc *dc, double x, double y, double width, double height) +{ + CGRect rect; + + if (dc->path == NULL) { + dc->path = CGPathCreateMutable(); + } + rect.origin.x = x; + rect.origin.y = y; + rect.size.width = width; + rect.size.height = height; + CGPathAddRect(dc->path, NULL, rect); +} + +void +x3getcurrentpoint(x3dc *dc, double *px, double *py) +{ + CGPoint point = CGPathGetCurrentPoint(dc->path); + + *px = point.x; + *py = point.y; +} + +void +x3setrgba(x3dc *dc, unsigned int rgba) +{ + CGContextSetRGBFillColor(dc->ctx, + ((rgba >> 24) & 0xff) * (1.0/255), + ((rgba >> 16) & 0xff) * (1.0/255), + ((rgba >> 8) & 0xff) * (1.0/255), + (rgba & 0xff) * (1.0/255)); + CGContextSetRGBStrokeColor(dc->ctx, + ((rgba >> 24) & 0xff) * (1.0/255), + ((rgba >> 16) & 0xff) * (1.0/255), + ((rgba >> 8) & 0xff) * (1.0/255), + (rgba & 0xff) * (1.0/255)); +} + +void +x3setlinewidth(x3dc *dc, double w) +{ + CGContextSetLineWidth(dc->ctx, w); +} + +void +x3fill(x3dc *dc) +{ + CGContextAddPath(dc->ctx, dc->path); + CGPathRelease(dc->path); + dc->path = NULL; + CGContextFillPath(dc->ctx); +} + +void +x3stroke(x3dc *dc) +{ + CGContextAddPath(dc->ctx, dc->path); + CGPathRelease(dc->path); + dc->path = NULL; + CGContextStrokePath(dc->ctx); +} + +void +x3selectfont(x3dc *dc, char *fontname, int slant, int weight) +{ + CGContextSelectFont(dc->ctx, fontname, 8.0, kCGEncodingMacRoman); +} + +void +x3setfontsize(x3dc *dc, double size) +{ + CGContextSetFontSize(dc->ctx, size); +} + +void +x3showtext(x3dc *dc, char *text) +{ + CGPoint point = CGPathGetCurrentPoint(dc->path); + CGAffineTransform textmat; + + textmat = CGAffineTransformMakeScale(1, -1); + CGContextSetTextMatrix(dc->ctx, textmat); + CGContextShowTextAtPoint(dc->ctx, point.x, point.y, text, strlen(text)); +} + +void x3textextents(x3dc *dc, char *text, x3extents *extents) +{ + /* todo */ +} diff --git a/third_party/spiro/x3/x3common.c b/third_party/spiro/x3/x3common.c new file mode 100644 index 0000000..e4845ff --- /dev/null +++ b/third_party/spiro/x3/x3common.c @@ -0,0 +1,359 @@ +/* Functions common to more than one platform. */ + +#include <stdlib.h> + +#include "x3.h" +#include "x3common.h" + +int n_x3needshow = 0; +int n_x3needshow_max = 0; +x3widget **x3needshow = NULL; + +#if defined(X3_GTK) || defined(X3_WIN32) +int x3n_winopen = 0; +#endif + +#if defined(X3_CARBON) || defined(X3_WIN32) + +int n_x3needsizereqs = 0; +int n_x3needsizereqs_max = 0; +x3widget **x3needsizereqs = NULL; + +int n_x3needsizeallocs = 0; +int n_x3needsizeallocs_max = 0; +x3widget **x3needsizeallocs = NULL; + +void x3qsizereq(x3widget *w) +{ + if (w && !(w->flags & x3flag_needsizereq)) { + x3qsizereq(w->parent); + if (n_x3needsizereqs == n_x3needsizereqs_max) + x3needsizereqs = (x3widget **)realloc(x3needsizereqs, + sizeof(x3widget *) * + (n_x3needsizereqs_max <<= 1)); + x3needsizereqs[n_x3needsizereqs++] = w; + w->flags |= x3flag_needsizereq; + } +} + +void x3add_default(x3widget *parent, x3widget *child) +{ + const int n_children_init = 4; + + child->parent = parent; + if (parent->n_children == 0) + parent->children = (x3widget **)malloc(sizeof(x3widget *) * + n_children_init); + else if (parent->n_children >= n_children_init && + !(parent->n_children & (parent->n_children - 1))) + parent->children = (x3widget **)realloc(parent->children, + sizeof(x3widget *) * + (parent->n_children << 1)); + parent->children[parent->n_children++] = child; +} + +void x3add(x3widget *parent, x3widget *child) +{ + parent->type->add(parent, child); +} + +/* Widgets common to carbon and win32 platforms */ + +typedef struct { + x3widget base; + int homogeneous; + int spacing; + int cur_child_prop; + int *child_props; /* bit 0=expand, bit 1=fill, bits 2:31=padding */ +} x3widget_box; + +void x3vbox_sizereq(x3widget *w) +{ + x3widget_box *wb = (x3widget_box *)w; + int i; + int spacing = wb->spacing; + + w->sizerequest.x0 = 0; + w->sizerequest.y0 = 0; + w->sizerequest.x1 = 0; + w->sizerequest.y1 = 0; + for (i = 0; i < w->n_children; i++) { + x3widget *child = w->children[i]; + int childw = child->sizerequest.x1 - child->sizerequest.x0; + int childh = child->sizerequest.y1 - child->sizerequest.y0; + int padding = wb->child_props[i] >> 2; + + if (i < w->n_children - 1) + childh += spacing; + w->sizerequest.y1 += childh + 2 * padding; + if (childw > w->sizerequest.x1) + w->sizerequest.x1 = childw; + } +} + +void x3vbox_sizealloc(x3widget *w, x3rect *r) +{ + x3widget_box *wb = (x3widget_box *)w; + int i; + x3rect child_r = *r; + int spacing = wb->spacing; + int n_extend = 0; + int n_stretch, i_stretch = 0; + int extra; + + /* todo: impl padding & homog, factor hbox/vbox common */ + printf("vbox sizealloc = (%g, %g) - (%g, %g), req was %g x %g\n", + r->x0, r->y0, r->x1, r->y1, + w->sizerequest.x1, w->sizerequest.y1); + extra = r->y1 - r->y0 - w->sizerequest.y1; + for (i = 0; i < w->n_children; i++) + if (wb->child_props[i] & 1) + n_extend++; + n_stretch = n_extend ? n_extend : w->n_children; + printf("extra = %d, n_stretch = %d\n", extra, n_stretch); + for (i = 0; i < w->n_children; i++) { + x3widget *child = w->children[i]; + int childh = child->sizerequest.y1 - child->sizerequest.y0; + int my_extra; + int next_top; + + if (n_extend == 0 || (wb->child_props[i] & 1)) { + my_extra = (extra * (i_stretch + 1)) / n_stretch - + (extra * i_stretch) / n_stretch; + i_stretch++; + } else + my_extra = 0; + next_top = child_r.y0 + childh + spacing + my_extra; + + if (wb->child_props[i] & 2) { + childh += my_extra; + } else { + child_r.y0 += my_extra >> 1; + } + child_r.y1 = child_r.y0 + childh; + + child->type->sizealloc(child, &child_r); + child->flags &= ~x3flag_needsizealloc; + + child_r.y0 = next_top; + } +} + +void x3vbox_add(x3widget *w, x3widget *child) +{ + x3widget_box *wb = (x3widget_box *)w; + const int n_children_init = 4; + + if (w->n_children == 0) + wb->child_props = (int *)malloc(sizeof(int) * n_children_init); + else if (w->n_children >= n_children_init && + !(w->n_children & (w->n_children - 1))) + wb->child_props = (int *)realloc(wb->child_props, + sizeof(int) * (w->n_children << 1)); + wb->child_props[w->n_children] = wb->cur_child_prop; + x3add_default(w, child); +} + +x3type x3vboxtype = { x3vbox_sizereq, + x3vbox_sizealloc, + x3vbox_add }; + +x3widget *x3vbox(x3widget *parent, int homogeneous, int spacing) +{ + x3widget_box *result = (x3widget_box *)malloc(sizeof(x3widget_box)); + x3widget_init(&result->base, &x3vboxtype); + x3add(parent, &result->base); + result->homogeneous = homogeneous; + result->spacing = spacing; + result->cur_child_prop = 3; + x3qsizereq(&result->base); + return &result->base; +} + +void x3setpacking(x3widget *w, int fill, int expand, int padding) +{ + if (w->type == &x3vboxtype) { + x3widget_box *wb = (x3widget_box *)w; + int child_props = padding << 2; + + if (fill) child_props |= 1; + if (expand) child_props |= 2; + wb->cur_child_prop = child_props; + } +} + +typedef struct { + x3widget base; + x3alignment alignment; +} x3widget_align; + +void x3align_sizereq(x3widget *w) +{ + w->sizerequest.x0 = 0; + w->sizerequest.y0 = 0; + w->sizerequest.x1 = 0; + w->sizerequest.y1 = 0; + if (w->n_children) { + x3widget *child = w->children[0]; + int childw = child->sizerequest.x1 - child->sizerequest.x0; + int childh = child->sizerequest.y1 - child->sizerequest.y0; + w->sizerequest.x1 = childw; + w->sizerequest.y1 = childh; + } +} + +void x3align_sizealloc(x3widget *w, x3rect *r) +{ + x3widget_align *z = (x3widget_align *)w; + x3alignment a = z->alignment; + int xa = a & 3; + int ya = (a >> 2) & 3; + x3rect child_r = *r; + + printf("align sizealloc = (%g, %g) - (%g, %g)\n", + r->x0, r->y0, r->x1, r->y1); + if (w->n_children) { + x3widget *child = w->children[0]; + if (xa < 3) { + int childw = child->sizerequest.x1 - child->sizerequest.x0; + int pad = r->x1 - r->x0 - childw; + child_r.x0 += (pad * (1 + (xa >> 1) - (xa & 1)) + 1) >> 1; + child_r.x1 = child_r.x0 + childw; + } + if (ya < 3) { + int childh = child->sizerequest.y1 - child->sizerequest.y0; + int pad = r->y1 - r->y0 - childh; + child_r.y0 += (pad * (1 + (ya >> 1) - (ya & 1)) + 1) >> 1; + child_r.y1 = child_r.y0 + childh; + } + + child->type->sizealloc(child, &child_r); + child->flags &= ~x3flag_needsizealloc; + } +} + +x3type x3aligntype = { x3align_sizereq, + x3align_sizealloc, + x3add_default }; + +x3widget *x3align(x3widget *parent, x3alignment alignment) +{ + x3widget *result = (x3widget *)malloc(sizeof(x3widget_align)); + x3widget_init(result, &x3aligntype); + x3add(parent, result); + x3qsizereq(result); + ((x3widget_align *)result)->alignment = alignment; + return result; +} + +typedef struct { + x3widget base; + int t, b, l, r; +} x3widget_pad; + +void x3pad_sizereq(x3widget *w) +{ + x3widget_pad *z = (x3widget_pad *)w; + w->sizerequest.x0 = 0; + w->sizerequest.y0 = 0; + w->sizerequest.x1 = 0; + w->sizerequest.y1 = 0; + if (w->n_children) { + x3widget *child = w->children[0]; + int childw = child->sizerequest.x1 - child->sizerequest.x0; + int childh = child->sizerequest.y1 - child->sizerequest.y0; + w->sizerequest.x1 = childw + z->l + z->r; + w->sizerequest.y1 = childh + z->t + z->b; + } +} + +void x3pad_sizealloc(x3widget *w, x3rect *r) +{ + x3widget_pad *z = (x3widget_pad *)w; + x3rect child_r = *r; + + printf("pad sizealloc = (%g, %g) - (%g, %g)\n", + r->x0, r->y0, r->x1, r->y1); + if (w->n_children) { + x3widget *child = w->children[0]; + child_r.x0 += z->l; + child_r.x1 -= z->r; + child_r.y0 += z->t; + child_r.y1 -= z->b; + + child->type->sizealloc(child, &child_r); + child->flags &= ~x3flag_needsizealloc; + } +} + +x3type x3padtype = { x3pad_sizereq, + x3pad_sizealloc, + x3add_default }; + +x3widget *x3pad(x3widget *parent, int t, int b, int l, int r) +{ + x3widget *result = (x3widget *)malloc(sizeof(x3widget_pad)); + x3widget_init(result, &x3padtype); + x3add(parent, result); + x3qsizereq(result); + ((x3widget_pad *)result)->t = t; + ((x3widget_pad *)result)->b = b; + ((x3widget_pad *)result)->l = l; + ((x3widget_pad *)result)->r = r; + return result; +} + +#endif + +void x3initqs(void) +{ + n_x3needshow = 0; + x3needshow = (x3widget **)malloc(sizeof(x3widget *) * + (n_x3needshow_max = 16)); + +#if defined(X3_CARBON) || defined(X3_WIN32) + n_x3needsizereqs = 0; + x3needsizereqs = (x3widget **)malloc(sizeof(x3widget *) * + (n_x3needsizereqs_max = 16)); + + n_x3needsizeallocs = 0; + x3needsizeallocs = (x3widget **)malloc(sizeof(x3widget *) * + (n_x3needsizeallocs_max = 16)); +#endif +} + +void x3qshow(x3widget *w) +{ + if (n_x3needshow == n_x3needshow_max) + x3needshow = (x3widget **)realloc(x3needshow, + sizeof(x3widget *) * + (n_x3needshow_max <<= 1)); + x3needshow[n_x3needshow++] = w; +} + +void x3sync(void) +{ + int i; + +#if defined(X3_CARBON) || defined(X3_WIN32) + + for (i = n_x3needsizereqs - 1; i >= 0; i--) { + x3widget *w = x3needsizereqs[i]; + w->type->sizereq(w); + w->flags &= ~x3flag_needsizereq; + w->flags |= x3flag_needsizealloc; + } + for (i = 0; i < n_x3needsizereqs; i++) { + x3widget *w = x3needsizereqs[i]; + if (w->flags & x3flag_needsizealloc) { + w->type->sizealloc(w, NULL); + w->flags &= ~x3flag_needsizealloc; + } + } + n_x3needsizereqs = 0; +#endif + + for (i = 0; i < n_x3needshow; i++) + x3_window_show(x3needshow[i]); + n_x3needshow = 0; +} diff --git a/third_party/spiro/x3/x3common.h b/third_party/spiro/x3/x3common.h new file mode 100644 index 0000000..fc616a2 --- /dev/null +++ b/third_party/spiro/x3/x3common.h @@ -0,0 +1,16 @@ +#if defined(X3_CARBON) || defined(X3_WIN32) + +void x3qsizereq(x3widget *w); + +void x3add_default(x3widget *parent, x3widget *child); +void x3add(x3widget *parent, x3widget *child); + +#endif + +void x3initqs(void); +void x3qshow(x3widget *w); +void x3sync(void); + + +/* provided by impls to common routines */ +void x3_window_show(x3widget *w); diff --git a/third_party/spiro/x3/x3gtk.c b/third_party/spiro/x3/x3gtk.c new file mode 100644 index 0000000..162f496 --- /dev/null +++ b/third_party/spiro/x3/x3gtk.c @@ -0,0 +1,778 @@ +#include <stdlib.h> +#include <string.h> + +#include "x3.h" +#include "x3common.h" + +void x3init(int *pargc, char ***pargv) +{ + gtk_init(pargc, pargv); + x3initqs(); +} + +static void +x3_getfirst_callback(GtkWidget *widget, gpointer data) +{ + GtkWidget **pwidget = (GtkWidget **)data; + + if (*pwidget == NULL) + *pwidget = widget; +} + +static GtkWidget *x3_gtkwidget_getchild(GtkWidget *w) +{ + GtkWidget *child = NULL; + gtk_container_foreach(GTK_CONTAINER(w), + x3_getfirst_callback, + (gpointer)&child); + return child; +} + +typedef struct { + x3widget base; + gboolean expand; + gboolean fill; + guint padding; +} x3widget_box; + +static void x3widget_init(x3widget *w, x3widget *parent, char *name, + GtkWidget *widget) +{ + w->name = g_strdup(name); + w->widget = widget; + w->parent = parent; + if (parent) { + if (GTK_IS_WINDOW(parent->widget)) { + GtkWidget *vbox = x3_gtkwidget_getchild(parent->widget); + + if (GTK_IS_MENU_ITEM(widget)) { + GtkWidget *first_child = x3_gtkwidget_getchild(vbox); + GtkWidget *menubar; + + if (first_child == NULL || !GTK_IS_MENU_BAR(first_child)) { + menubar = gtk_menu_bar_new(); + gtk_box_pack_start(GTK_BOX(vbox), menubar, + FALSE, FALSE, 0); + gtk_widget_show(menubar); + } else + menubar = first_child; + gtk_menu_bar_append(GTK_MENU_BAR(menubar), widget); + } else { + gtk_container_add(GTK_CONTAINER(vbox), widget); + } + } else if (GTK_IS_MENU_ITEM(parent->widget)) { + GtkWidget *menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(parent->widget)); + + gtk_menu_shell_append(GTK_MENU_SHELL(menu), widget); + } else if (GTK_IS_BOX(parent->widget)) { + x3widget_box *pwb = (x3widget_box *)parent; + gtk_box_pack_start(GTK_BOX(parent->widget), widget, + pwb->expand, pwb->fill, pwb->padding); + } else { + gtk_container_add(GTK_CONTAINER(parent->widget), widget); + } + } +} + +static x3widget *x3widget_new(x3widget *parent, char *name, GtkWidget *widget) +{ + x3widget *result = (x3widget *)malloc(sizeof(x3widget)); + + x3widget_init(result, parent, name, widget); + return result; +} + +static x3widget *x3box_new(x3widget *parent, GtkWidget *widget) +{ + x3widget_box *result = (x3widget_box *)malloc(sizeof(x3widget_box)); + + x3widget_init(&result->base, parent, NULL, widget); + result->expand = TRUE; + result->fill = TRUE; + result->padding = 0; + return &result->base; +} + +void x3_window_show(x3widget *w) +{ + gtk_widget_show(w->widget); +} + +void x3window_setdefaultsize(x3widget *w, int width, int height) +{ + gtk_window_set_default_size(GTK_WINDOW(w->widget), width, height); +} + +void x3main(void) +{ + x3sync(); + gtk_main(); +} + +/* some constructors */ + +typedef struct { + x3widget base; + x3window_callback callback; + void *callback_data; + + GtkAccelGroup *accel_group; +} x3widget_window; + +gboolean x3window_delete(GtkWidget *window, GdkEvent *event, gpointer data) +{ + /* todo: pass this as a command callback */ + if (--x3n_winopen <= 0) + gtk_main_quit(); + return FALSE; +} + +x3widget *x3window(x3windowflags flags, char *label, + x3window_callback callback, void *callback_data) +{ + GtkWidget *window; + GtkWidget *vbox; + x3widget_window *result; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), label); + vbox = gtk_vbox_new(FALSE, 0); + gtk_widget_show(vbox); + gtk_container_add(GTK_CONTAINER(window), vbox); + result = (x3widget_window *)malloc(sizeof(x3widget_window)); + x3widget_init(&result->base, NULL, "mainwin", window); + result->callback = callback; + result->callback_data = callback_data; + result->accel_group = gtk_accel_group_new(); + gtk_window_add_accel_group(GTK_WINDOW(window), result->accel_group); + g_signal_connect(G_OBJECT(window), "delete-event", + G_CALLBACK(x3window_delete), result); + x3qshow(&result->base); + x3n_winopen++; + return &result->base; +} + +x3widget *x3menu(x3widget *parent, char *name) +{ + GtkWidget *item; + GtkWidget *menu; + + menu = gtk_menu_new(); + + item = gtk_menu_item_new_with_label(name); + gtk_widget_show(item); + + gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu); + return x3widget_new(parent, NULL, item); +} + +typedef struct { + x3widget base; + char *cmd; +} x3widget_cmdable; + +static void x3doevent(x3widget_cmdable *wc, char *str) +{ + char *cmd = wc->cmd; + x3widget *w = &wc->base; + x3widget_window *ww; + + while (w->parent) w = w->parent; + ww = (x3widget_window *)w; + ww->callback(w, ww->callback_data, cmd, str, NULL, NULL); + x3sync(); +} + +static void x3cmdable_clicked(GtkWidget *widget, gpointer data) +{ + x3widget_cmdable *wc = (x3widget_cmdable *)data; + + x3doevent(wc, "command"); +} + +static const char *asciinames[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "space", + "exclam", + "quotedbl", + "numbersign", + "dollar", + "percent", + "ampersand", + "apostrophe", + "parenleft", + "parenright", + "asterisk", + "plus", + "comma", + "minus", + "period", + "slash", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "colon", + "semicolon", + "less", + "equal", + "greater", + "question", + "at", + "<shift>a", + "<shift>b", + "<shift>c", + "<shift>d", + "<shift>e", + "<shift>f", + "<shift>g", + "<shift>h", + "<shift>i", + "<shift>j", + "<shift>k", + "<shift>l", + "<shift>m", + "<shift>n", + "<shift>o", + "<shift>p", + "<shift>q", + "<shift>r", + "<shift>s", + "<shift>t", + "<shift>u", + "<shift>v", + "<shift>w", + "<shift>x", + "<shift>y", + "<shift>z", + "bracketleft", + "backslash", + "bracketright", + "asciicircum", + "underscore", + "grave", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "braceleft", + "bar", + "braceright", + "asciitilde" +}; + +/* return 1 on success */ +static int +x3parseshortcut(const char *shortcut, + guint *accelerator_key, GdkModifierType *accelerator_mods) +{ + int len; + char tmp[256]; + int i; + + if (shortcut == NULL) return 0; + len = strlen(shortcut); + if (len >= sizeof(tmp) - 1) return 0; + strcpy(tmp, shortcut); + for (i = 0; i < len - 5; i++) + if (!memcmp(tmp + i, "<cmd>", 5)) + memcpy(tmp + i, "<ctl>", 5); + if (len == 1 || tmp[len - 2] == '>') { + unsigned char c = (unsigned char)tmp[len-1]; + if (c < sizeof(asciinames) / sizeof(asciinames[0]) && asciinames[c] && + len + strlen(asciinames[c]) < sizeof(tmp)) + strcpy(tmp + len - 1, asciinames[c]); + } + gtk_accelerator_parse(tmp, accelerator_key, accelerator_mods); + return *accelerator_key != 0 || *accelerator_mods != 0; +} + +static GtkAccelGroup * +x3getaccelgroup(x3widget *w) +{ + while (w->parent) w = w->parent; + if (!GTK_IS_WINDOW(w->widget)) return NULL; + return ((x3widget_window *)w)->accel_group; +} + +x3widget *x3menuitem(x3widget *parent, char *name, char *cmd, char *shortcut) +{ + GtkWidget *item; + x3widget_cmdable *result = (x3widget_cmdable *)malloc(sizeof(x3widget_cmdable)); + guint accel_key; + GdkModifierType accel_mods; + + item = gtk_menu_item_new_with_label(name); + x3widget_init(&result->base, parent, cmd, item); + result->cmd = g_strdup(cmd); + g_signal_connect(G_OBJECT(item), "activate", + G_CALLBACK(x3cmdable_clicked), result); + + if (x3parseshortcut(shortcut, &accel_key, &accel_mods)) { + gtk_widget_add_accelerator(item, "activate", x3getaccelgroup(parent), + accel_key, accel_mods, GTK_ACCEL_VISIBLE); + } + + gtk_widget_show(item); + return &result->base; +} + +x3widget *x3menusep(x3widget *parent) +{ + GtkWidget *item; + + item = gtk_separator_menu_item_new(); + gtk_widget_show(item); + return x3widget_new(parent, NULL, item); +} + +x3widget *x3vbox(x3widget *parent, int homogeneous, int spacing) +{ + GtkWidget *vbox = gtk_vbox_new(homogeneous, spacing); + + gtk_widget_show(vbox); + return x3box_new(parent, vbox); +} + +x3widget *x3hpane(x3widget *parent) +{ + GtkWidget *hpane = gtk_hpaned_new(); + + gtk_widget_show(hpane); + return x3widget_new(parent, NULL, hpane); +} + +x3widget *x3vpane(x3widget *parent) +{ + GtkWidget *vpane = gtk_vpaned_new(); + + gtk_widget_show(vpane); + return x3widget_new(parent, NULL, vpane); +} + +x3widget *x3align(x3widget *parent, x3alignment alignment) +{ + int xa = alignment & 3; + int ya = (alignment >> 2) & 3; + float xalign = .5 * (1 + (xa >> 1) - (xa & 1)); + float yalign = .5 * (1 + (ya >> 1) - (ya & 1)); + float xscale = (xa == 3); + float yscale = (ya == 3); + GtkWidget *align = gtk_alignment_new(xalign, yalign, xscale, yscale); + + gtk_widget_show(align); + return x3widget_new(parent, NULL, align); +} + +x3widget *x3pad(x3widget *parent, int t, int b, int l, int r) +{ + GtkWidget *align = gtk_alignment_new(0, 0, 1, 1); + + gtk_alignment_set_padding(GTK_ALIGNMENT(align), t, b, l, r); + gtk_widget_show(align); + return x3widget_new(parent, NULL, align); +} + +x3widget *x3button(x3widget *parent, char *cmd, char *label) +{ + GtkWidget *button = gtk_button_new_with_label(label); + x3widget_cmdable *result = (x3widget_cmdable *)malloc(sizeof(x3widget_cmdable)); + + x3widget_init(&result->base, parent, cmd, button); + result->cmd = g_strdup(cmd); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(x3cmdable_clicked), result); + + gtk_widget_show(button); + return &result->base; +} + +x3widget *x3label(x3widget *parent, char *text) +{ + GtkWidget *label = gtk_label_new(text); + + gtk_widget_show(label); + return x3widget_new(parent, NULL, label); +} + +x3widget *x3edittext(x3widget *parent, char *cmd) +{ + GtkWidget *entry = gtk_entry_new(); + + gtk_widget_show(entry); + return x3widget_new(parent, cmd, entry); +} + +typedef struct { + x3widget base; + x3viewflags flags; + x3viewclient *vc; +} x3widget_view; + +static gboolean x3view_expose(GtkWidget *widget, GdkEventExpose *event, + gpointer data) +{ + x3widget_view *w = (x3widget_view *)data; + GdkWindow *window = GTK_IS_LAYOUT(widget) ? + GTK_LAYOUT(widget)->bin_window : + widget->window; + + if (w->vc && w->vc->draw) { + x3dc dc; + + dc.x = event->area.x; + dc.y = event->area.y; + dc.width = event->area.width; + dc.height = event->area.height; + if (w->flags & x3view_rgb) { + dc.rowstride = (event->area.width * 3 + 3) & -4; + dc.buf = (guchar *)malloc(event->area.height * dc.rowstride); + dc.cr = NULL; + + w->vc->draw(w->vc, &dc); + gdk_draw_rgb_image(window, widget->style->black_gc, + event->area.x, event->area.y, + event->area.width, event->area.height, + GDK_RGB_DITHER_NORMAL, + dc.buf, dc.rowstride); + free(dc.buf); + } else if (w->flags & x3view_2d) { + dc.cr = gdk_cairo_create(window); + dc.buf = NULL; + + w->vc->draw(w->vc, &dc); + cairo_destroy(dc.cr); + } + } + +#if 1 + /* experimental code for managing cairo dynamics */ + if (event->count == 0) + gdk_flush(); +#endif + + return TRUE; +} + +static gboolean x3view_button_press(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + x3widget_view *w = (x3widget_view *)data; + guint button = event->button; + + if (event->type == GDK_BUTTON_RELEASE) button = -button; + + if (w->vc && w->vc->mouse) { + w->vc->mouse(w->vc, button, event->state, event->x, event->y); + return TRUE; + } + x3sync(); + return FALSE; +} + +static gboolean x3view_pointer_motion(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + x3widget_view *w = (x3widget_view *)data; + + if (w->vc && w->vc->mouse) { + w->vc->mouse(w->vc, 0, event->state, + event->x, event->y); + return TRUE; + } + x3sync(); + return FALSE; +} + +static gboolean x3view_key_press(GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + x3widget_view *w = (x3widget_view *)data; + + if (w->vc && w->vc->key) + return w->vc->key(w->vc, gdk_keyval_name(event->keyval), + event->state, event->keyval); + x3sync(); + return FALSE; +} + +x3widget *x3view(x3widget *parent, x3viewflags flags, x3viewclient *vc) +{ + GtkWidget *container; + GtkWidget *event_target; + GtkWidget *drawing_area; + x3widget_view *result = (x3widget_view *)malloc(sizeof(x3widget_view)); + GdkEventMask eventmask = 0; + + if (flags & x3view_scroll) { + container = gtk_scrolled_window_new(NULL, NULL); + drawing_area = gtk_layout_new(NULL, NULL); + event_target = drawing_area; + /* todo: more intelligent size requesting of view */ + gtk_widget_set_size_request(drawing_area, 1500, 1500); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(container), + drawing_area); + } else { + container = gtk_event_box_new(); + drawing_area = gtk_drawing_area_new(); + event_target = container; + gtk_container_add(GTK_CONTAINER(container), drawing_area); + } + gtk_widget_show(container); + + if (flags & x3view_key) { + g_object_set(GTK_OBJECT(event_target), "can-focus", TRUE, NULL); + eventmask |= GDK_KEY_PRESS_MASK; + g_signal_connect(G_OBJECT(event_target), "key_press_event", + G_CALLBACK(x3view_key_press), result); + } + if (flags & x3view_click) { + eventmask |= GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK; + g_signal_connect(G_OBJECT(event_target), "button_press_event", + G_CALLBACK(x3view_button_press), result); + g_signal_connect(G_OBJECT(event_target), "button_release_event", + G_CALLBACK(x3view_button_press), result); + } + if (flags & x3view_hover) { + eventmask |= GDK_POINTER_MOTION_MASK; + g_signal_connect(G_OBJECT(event_target), "motion_notify_event", + G_CALLBACK(x3view_pointer_motion), result); + } + gtk_widget_add_events(event_target, eventmask); + + g_signal_connect(G_OBJECT(drawing_area), "expose_event", + G_CALLBACK(x3view_expose), result); + gtk_widget_show(drawing_area); + if (flags & x3view_rgb) + gtk_widget_set_double_buffered(drawing_area, FALSE); + + x3widget_init(&result->base, parent, NULL, container); + result->flags = flags; + result->vc = vc; + return &result->base; +} + +void x3view_dirty(x3widget *w) +{ + gtk_widget_queue_draw(w->widget); +} + +static void +x3scrollto_adj(GtkAdjustment *adj, int v, int size) +{ + if (adj && v != -1) { + if (size >= adj->page_size) { + /* target is bigger than adj; center as best as possible */ + gtk_adjustment_set_value(adj, v - 0.5 * (size - adj->page_size)); + } else if (adj->value > v) { + gtk_adjustment_set_value(adj, v); + } else if (adj->value + adj->page_size < v + size) { + gtk_adjustment_set_value(adj, v + size - adj->page_size); + } + } +} + +void x3view_scrollto(x3widget *w, int x, int y, int width, int height) +{ + if (GTK_IS_SCROLLED_WINDOW(w->widget)) { + GtkScrolledWindow *sw = GTK_SCROLLED_WINDOW(w->widget); + x3scrollto_adj(gtk_scrolled_window_get_hadjustment(sw), x, width); + x3scrollto_adj(gtk_scrolled_window_get_vadjustment(sw), y, height); + } +} + +void x3viewclient_init(x3viewclient *vc) +{ + vc->destroy = NULL; + vc->mouse = NULL; + vc->key = NULL; + vc->draw = NULL; +} + +/* An argument can be made against the "fill" flag, because the same effect + as turning off fill can be achieved with the align widget. */ +void x3setpacking(x3widget *w, int fill, int expand, int padding) +{ + if (GTK_IS_BOX(w->widget)) { + x3widget_box *wb= (x3widget_box *)w; + wb->fill = fill; + wb->expand = expand; + wb->padding = padding; + } +} + +typedef struct { + GtkWidget *parent; + int resize[2]; + int shrink[2]; + int i; +} x3pane_setsizing_ctx; + +static void +x3pane_setsizing_callback(GtkWidget *child, gpointer data) +{ + x3pane_setsizing_ctx *ctx = (x3pane_setsizing_ctx *)data; + gtk_container_child_set(GTK_CONTAINER(ctx->parent), + child, + "resize", ctx->resize[ctx->i], + "shrink", ctx->shrink[ctx->i], + NULL); + ctx->i++; +} + +/* This implementation only works if the sizing is set _after_ the + * children are added. It wouldn't be too hard to fix, by putting the + * info in the pane's x3widget struct. */ +void x3pane_setsizing(x3widget *w, int child1_resize, int child1_shrink, + int child2_resize, int child2_shrink) +{ + x3pane_setsizing_ctx ctx; + + ctx.parent = w->widget; + ctx.resize[0] = child1_resize; + ctx.shrink[0] = child1_shrink; + ctx.resize[1] = child2_resize; + ctx.shrink[1] = child2_shrink; + ctx.i = 0; + if (GTK_IS_PANED(w->widget)) { + gtk_container_foreach(GTK_CONTAINER(w->widget), + x3pane_setsizing_callback, + (gpointer)&ctx); + } +} + + +void x3setactive(x3widget *w, int active) +{ + gtk_widget_set_sensitive(w->widget, active != 0); +} + +int x3hasfocus(x3widget *w) +{ + GtkWidget *widget = w->widget; + while (GTK_IS_CONTAINER(widget) && !GTK_IS_LAYOUT(widget)) + widget = x3_gtkwidget_getchild(widget); + return GTK_WIDGET_HAS_FOCUS(widget); +} + +/* 2d drawing functions, implemented using cairo */ + +void +x3moveto(x3dc *dc, double x, double y) +{ + cairo_move_to(dc->cr, x, y); +} + +void +x3lineto(x3dc *dc, double x, double y) +{ + cairo_line_to(dc->cr, x, y); +} + +void +x3curveto(x3dc *dc, + double x1, double y1, + double x2, double y2, + double x3, double y3) +{ + cairo_curve_to(dc->cr, x1, y1, x2, y2, x3, y3); +} + +void +x3closepath(x3dc *dc) +{ + cairo_close_path(dc->cr); +} + +void +x3rectangle(x3dc *dc, double x, double y, double width, double height) +{ + cairo_rectangle(dc->cr, x, y, width, height); +} + +void +x3getcurrentpoint(x3dc *dc, double *px, double *py) +{ + cairo_get_current_point(dc->cr, px, py); +} + +void +x3setrgba(x3dc *dc, unsigned int rgba) +{ + cairo_set_source_rgba(dc->cr, + ((rgba >> 24) & 0xff) * (1.0/255), + ((rgba >> 16) & 0xff) * (1.0/255), + ((rgba >> 8) & 0xff) * (1.0/255), + (rgba & 0xff) * (1.0/255)); +} + +void +x3setlinewidth(x3dc *dc, double w) +{ + cairo_set_line_width(dc->cr, w); +} + +void +x3fill(x3dc *dc) +{ + cairo_fill(dc->cr); +} + +void +x3stroke(x3dc *dc) +{ + cairo_stroke(dc->cr); +} + +void +x3selectfont(x3dc *dc, char *fontname, int slant, int weight) +{ + cairo_select_font_face(dc->cr, fontname, slant, weight); +} + +void +x3setfontsize(x3dc *dc, double size) +{ + cairo_set_font_size(dc->cr, size); +} + +void +x3showtext(x3dc *dc, char *text) +{ + cairo_show_text(dc->cr, text); +} + +void +x3textextents(x3dc *dc, char *text, x3extents *extents) +{ + cairo_text_extents(dc->cr, text, extents); +} diff --git a/third_party/spiro/x3/x3win32.c b/third_party/spiro/x3/x3win32.c new file mode 100644 index 0000000..45e7d3c --- /dev/null +++ b/third_party/spiro/x3/x3win32.c @@ -0,0 +1,174 @@ +#include "x3.h"
+#include "x3common.h"
+#include <stdio.h> /* for printf only, probably remove in production */
+
+HINSTANCE theInstance = NULL;
+
+void x3init_win32(HINSTANCE hInstance)
+{
+ theInstance = hInstance;
+}
+
+void x3widget_init(x3widget *w, const x3type *type)
+{
+ w->type = type;
+ w->name = NULL;
+ w->parent = NULL;
+ w->var = x3winnone;
+ w->u.hwnd = NULL;
+ w->n_children = 0;
+ w->children = NULL;
+}
+
+LRESULT CALLBACK x3WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (iMsg) {
+ case WM_DESTROY:
+ if (--x3n_winopen <= 0) {
+ PostQuitMessage(0);
+ return 0;
+ }
+ break;
+ }
+ return DefWindowProc(hwnd, iMsg, wParam, lParam);
+}
+
+void x3window_sizereq(x3widget *w)
+{
+}
+
+void x3window_sizealloc(x3widget *w, x3rect *r)
+{
+ int i;
+ RECT rect;
+ x3rect child_r;
+
+ GetClientRect(w->u.hwnd, &rect);
+ child_r.x0 = 0;
+ child_r.x1 = rect.right - rect.left;
+ child_r.y0 = 0;
+ child_r.y1 = rect.bottom - rect.top;
+ printf("x3window_sizealloc (%ld, %ld) - (%ld, %ld)\n",
+ rect.left, rect.top, rect.right, rect.bottom);
+ for (i = 0; i < w->n_children; i++) {
+ x3widget *child = w->children[i];
+ if (child->type->sizealloc)
+ child->type->sizealloc(child, &child_r);
+ child->flags &= ~x3flag_needsizealloc;
+ }
+}
+
+x3type x3windowtype = { x3window_sizereq,
+ x3window_sizealloc,
+ x3add_default };
+
+x3widget *x3window(x3windowflags flags, char *label,
+ x3window_callback callback, void *data)
+{
+ HWND hwnd;
+ DWORD style = WS_OVERLAPPEDWINDOW;
+ x3widget *result = (x3widget *)malloc(sizeof(x3widget));
+ WNDCLASSEX wndclass;
+
+ wndclass.cbSize = sizeof(wndclass);
+ wndclass.style = CS_HREDRAW | CS_VREDRAW;
+ wndclass.lpfnWndProc = x3WndProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = 0;
+ wndclass.hInstance = theInstance;
+ wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
+ wndclass.lpszMenuName = NULL;
+ wndclass.lpszClassName = "x3win";
+ wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
+
+ RegisterClassEx(&wndclass);
+
+ hwnd = CreateWindowEx(0, "x3win", "My window",
+ style, 100, 100, 300, 300,
+ NULL, NULL,
+ theInstance, NULL);
+ x3widget_init(result, &x3windowtype);
+ result->var = x3winhwnd;
+ result->u.hwnd = hwnd;
+ x3_nwinopen++;
+ //ShowWindow(hwnd, SW_SHOWNORMAL);
+ return result;
+
+}
+
+void x3_window_show(x3widget *w)
+{
+ ShowWindow(w->u.hwnd, SW_SHOW);
+}
+
+static HWND x3hwnd_of(x3widget *w)
+{
+ while (w->parent) w = w->parent;
+ return w->var == x3winhwnd ? w->u.hwnd : NULL;
+}
+
+static x3widget *x3widget_new_hwnd(x3widget *parent, char *name,
+ const x3type *type,
+ HWND hwnd)
+{
+ x3widget *result = (x3widget *)malloc(sizeof(x3widget));
+ x3widget_init(result, type);
+ result->name = name ? strdup(name) : NULL;
+ result->var = x3winhwnd;
+ result->u.hwnd = hwnd;
+ x3add(parent, result);
+ x3qsizereq(result);
+ return result;
+}
+
+void x3button_sizereq(x3widget *w)
+{
+ w->sizerequest.x0 = 0;
+ w->sizerequest.y0 = 0;
+ w->sizerequest.x1 = 100;
+ w->sizerequest.y1 = 20;
+#ifdef VERBOSE
+ printf("button sizereq = (%d, %d) - (%d, %d)\n",
+ w->sizerequest.x0, w->sizerequest.y0,
+ w->sizerequest.x1, w->sizerequest.y1);
+#endif
+}
+
+void x3button_sizealloc(x3widget *w, x3rect *r)
+{
+ printf("button sizealloc = (%g, %g) - (%g, %g)\n",
+ r->x0, r->y0, r->x1, r->y1);
+ if (w->var == x3winhwnd) {
+ SetWindowPos(w->u.hwnd, HWND_TOP,
+ r->x0, r->y0, r->x1 - r->x0, r->y1 - r->y0,
+ SWP_NOZORDER);
+ }
+}
+
+x3type x3buttontype = { x3button_sizereq,
+ x3button_sizealloc,
+ x3add_default };
+
+x3widget *x3button(x3widget *parent, char *cmd, char *label)
+{
+ HWND hwnd;
+
+ hwnd = CreateWindow("button", label,
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
+ 10, 10, 100, 20, x3hwnd_of(parent), NULL,
+ theInstance, NULL);
+ return x3widget_new_hwnd(parent, cmd, &x3buttontype, hwnd);
+}
+
+void x3main(void)
+{
+ MSG msg;
+
+ x3sync();
+ while (GetMessage(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+}
|