diff --git a/Makefile b/Makefile index bf1e041d..44473eea 100644 --- a/Makefile +++ b/Makefile @@ -5,20 +5,25 @@ LIBUI_COMMON:=$(filter-out %OLD_table.o,$(LIBUI_COMMON)) CFLAGS=-I$(LIBUI) +all: + echo "see Makefile" + ide/demo.h: ide/test.lua cd ide && xxd -i test.lua > demo.h ide/test.c: ide/demo.h ifeq ($(TARGET),w) # --------------------------- -LIBUI_COMMON+=extras/favicon-win.o +LIBUI_COMMON+=extras/favicon/win.o include win.mk endif # ------------------------------ ifeq ($(TARGET),l) # ------------------------------ -LIBUI_COMMON+=extras/favicon.o +LIBUI_COMMON+=extras/favicon/linux.o extras/label.o LIBUI_UNIX:=$(patsubst %.c,%.o,$(wildcard $(LIBUI)/unix/*.c)) LIBUI_UNIX:=$(filter-out %OLD_table.o,$(LIBUI_UNIX)) +LIBUI_UNIX:=$(filter-out %image.o,$(LIBUI_UNIX)) +LIBUI_UNIX+=extras/image/image.o O_FILES:=$(LIBUI_COMMON) $(LIBUI_UNIX) O_FILES:=$(O_FILES:.o=.$(TARGET).o) @@ -33,8 +38,8 @@ install: libui.so sudo rm -rf /usr/local/lib/x86_64-linux-gnu/libui.so sudo cp libui.so /usr/local/lib/x86_64-linux-gnu/libui.so -ex.out: example/main.c - $(CC) $(CFLAGS) example/main.c -lui -o ex.out +ex.out: example/main.c libui.so + $(CC) $(CFLAGS) example/main.c -L. -Wl,-rpath,. -lui -o ex.out ide.out: ide/libuilua.c ide/test.c $(CC) ide/libuilua.c ide/test.c -lui -ldl $(shell pkg-config --libs --cflags lua-5.3) -o ide.out @@ -46,7 +51,7 @@ ide.AppImage: endif # ------------------------------------ ifeq ($(TARGET),m) # ------------------ -LIBUI_COMMON+=extras/favicon.o +LIBUI_COMMON+=extras/favicon/darwin.o LIBUI_DARWIN:=$(patsubst %.m,%.o,$(wildcard $(LIBUI)/darwin/*.m)) LIBUI_DARWIN:=$(filter-out %OLD_table.o,$(LIBUI_DARWIN)) @@ -59,9 +64,13 @@ LDFLAGS=-framework Foundation -framework Appkit libui.dylib: $(O_FILES) $(CC) -shared $(O_FILES) $(LDFLAGS) -o libui.dylib -ex-mac: +ex.out: $(CC) $(CFLAGS) example/main.c -L. -lui -o ex.out +install: libui.dylib + cp libui.dylib /usr/local/lib + cp libui-ng/ui.h /usr/local/include + endif # --------------------- %.$(TARGET).o: %.m diff --git a/README.md b/README.md index 6178aa37..49116eda 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,20 @@ # libui-cross Prebuilt binaries for cross-compiling software using libui. +Builds will contain a few experimental extensions. -- [x] X86_64 Linux -- [x] X86_64 Windows -- [ ] X86_64 MacOS +``` +# Compile for MinGW from Linux +make TARGET=w libui_win64.a install + +# Compile for linux from linux +make TARGET=l libui.so install + +# Compile for Mac from Darling/Mac +make TARGET=m libui.dylib +``` + +- [x] X86_64 Linux (.so, .AppImage) +- [x] X86_64 Windows (.a, .exe) +- [ ] X86_64 MacOS (.dylib, .app) - [ ] ARM64 Linux - [ ] ARM64 MacOS diff --git a/example/main.c b/example/main.c index 37890552..80036e55 100644 --- a/example/main.c +++ b/example/main.c @@ -2,6 +2,8 @@ #include #include +_UI_EXTERN void uiLabelSetAttribute(uiLabel *label, uiAttribute *attr); + static int onClosing(uiWindow *w, void *data) { uiQuit(); @@ -37,8 +39,23 @@ static uiControl *makeBasicControlsPage(void) uiControl(uiNewCheckbox("Checkbox")), 0); + //char *data = malloc(64 * 64 * 4); + //memset(data, 0x111111ff, 64 * 64 * 4); + //uiImageAppend(img, data, 64, 64, 4); + + uiImage *img = uiNewImage(64, 64); + extern void uiImageFromFile(uiImage *i, char *file); + uiImageFromFile(img, "ide/libui.png"); + + uiBoxAppend(vbox, uiControl(img), 0); + + uiLabel *label = uiNewLabel("This is a label.\nLabels can span multiple lines."); + + uiLabelSetAttribute(label, uiNewSizeAttribute(20)); + uiLabelSetAttribute(label, uiNewItalicAttribute(uiTextItalicOblique)); + uiBoxAppend(vbox, - uiControl(uiNewLabel("This is a label.\nLabels can span multiple lines.")), + uiControl(label), 0); uiBoxAppend(vbox, @@ -425,16 +442,6 @@ int main(void) return 1; } - uiBox *box = uiNewVerticalBox(); - - uiButton *btn = uiNewButton("Hello"); - uiBoxAppend(box, uiControl(btn), 0); - uiButtonOnClicked(btn, clickevent, 0); - - btn = uiNewButton("fart"); - uiBoxAppend(box, uiControl(btn), 0); - - uiControlShow(uiControl(mainwin)); uiMain(); uiUninit(); diff --git a/extras/favicon/darwin.c b/extras/favicon/darwin.c new file mode 100644 index 00000000..58d5d508 --- /dev/null +++ b/extras/favicon/darwin.c @@ -0,0 +1,6 @@ +#include +#include + +_UI_EXTERN void uiWindowSetIcon(uiWindow *w, const void *data, size_t length) { + // ... +} diff --git a/extras/favicon.c b/extras/favicon/linux.c similarity index 100% rename from extras/favicon.c rename to extras/favicon/linux.c diff --git a/extras/favicon-win.cpp b/extras/favicon/win.cpp similarity index 100% rename from extras/favicon-win.cpp rename to extras/favicon/win.cpp diff --git a/extras/image/image.c b/extras/image/image.c new file mode 100644 index 00000000..6f66c144 --- /dev/null +++ b/extras/image/image.c @@ -0,0 +1,153 @@ +// 27 june 2016 +#include "unix/uipriv_unix.h" + +struct uiImage { + uiUnixControl c; + GtkWidget *widget; + + double width; + double height; + GPtrArray *images; +}; + +#define uiImageSignature 123 + +#define uiImage(this) ((uiImage *) (this)) + +uiUnixControlAllDefaults(uiImage) + +static void freeImageRep(gpointer item) +{ + cairo_surface_t *cs = (cairo_surface_t *) item; + + cairo_surface_destroy(cs); +} + +uiImage *uiNewImage(double width, double height) +{ + uiImage *i; + + uiUnixNewControl(uiImage, i); + + i->widget = gtk_image_new(); + + i->width = width; + i->height = height; + i->images = g_ptr_array_new_with_free_func(freeImageRep); + return i; +} + +void uiFreeImage(uiImage *i) +{ + g_ptr_array_free(i->images, TRUE); + uiprivFree(i); +} + +void uiImageFromFile(uiImage *i, char *file) { + i->widget = gtk_image_new_from_file(file); +} + +void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int byteStride) +{ + cairo_surface_t *cs; + uint8_t *data, *pix; + int realStride; + int x, y; + + // note that this is native-endian + cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + pixelWidth, pixelHeight); + if (cairo_surface_status(cs) != CAIRO_STATUS_SUCCESS) { + /* TODO */ + } + cairo_surface_flush(cs); + + pix = (uint8_t *) pixels; + data = (uint8_t *) cairo_image_surface_get_data(cs); + realStride = cairo_image_surface_get_stride(cs); + for (y = 0; y < pixelHeight; y++) { + for (x = 0; x < pixelWidth * 4; x += 4) { + union { + uint32_t v32; + uint8_t v8[4]; + } v; + + v.v32 = ((uint32_t) (pix[x + 3])) << 24; + v.v32 |= ((uint32_t) (pix[x])) << 16; + v.v32 |= ((uint32_t) (pix[x + 1])) << 8; + v.v32 |= ((uint32_t) (pix[x + 2])); + data[x] = v.v8[0]; + data[x + 1] = v.v8[1]; + data[x + 2] = v.v8[2]; + data[x + 3] = v.v8[3]; + } + pix += byteStride; + data += realStride; + } + + gtk_image_set_from_surface(i->widget, cs); + + cairo_surface_mark_dirty(cs); + g_ptr_array_add(i->images, cs); +} + +struct matcher { + cairo_surface_t *best; + int distX; + int distY; + int targetX; + int targetY; + gboolean foundLarger; +}; + +// TODO is this the right algorithm? +static void match(gpointer surface, gpointer data) +{ + cairo_surface_t *cs = (cairo_surface_t *) surface; + struct matcher *m = (struct matcher *) data; + int x, y; + int x2, y2; + + x = cairo_image_surface_get_width(cs); + y = cairo_image_surface_get_height(cs); + if (m->best == NULL) + goto writeMatch; + + if (x < m->targetX && y < m->targetY) + if (m->foundLarger) + // always prefer larger ones + return; + if (x >= m->targetX && y >= m->targetY && !m->foundLarger) + // we set foundLarger below + goto writeMatch; + + x2 = abs(m->targetX - x); + y2 = abs(m->targetY - y); + if (x2 < m->distX && y2 < m->distY) + goto writeMatch; + + // TODO weight one dimension? threshhold? + return; + +writeMatch: + // must set this here too; otherwise the first image will never have ths set + if (x >= m->targetX && y >= m->targetY && !m->foundLarger) + m->foundLarger = TRUE; + m->best = cs; + m->distX = abs(m->targetX - x); + m->distY = abs(m->targetY - y); +} + +cairo_surface_t *uiprivImageAppropriateSurface(uiImage *i, GtkWidget *w) +{ + struct matcher m; + + m.best = NULL; + m.distX = G_MAXINT; + m.distY = G_MAXINT; + m.targetX = i->width * gtk_widget_get_scale_factor(w); + m.targetY = i->height * gtk_widget_get_scale_factor(w); + m.foundLarger = FALSE; + g_ptr_array_foreach(i->images, match, &m); + return m.best; +} diff --git a/extras/label.c b/extras/label.c new file mode 100644 index 00000000..3a662f8c --- /dev/null +++ b/extras/label.c @@ -0,0 +1,71 @@ +#include +#include + +#ifdef __unix__ + +#include +#include +#include + +struct uiLabel { + uiUnixControl c; + GtkWidget *widget; + GtkMisc *misc; + GtkLabel *label; +}; + +PangoAttrList *uiprivAttributedStringToPangoAttrList(uiDrawTextLayoutParams *p); + +struct attr { + uiAttribute *val; + size_t start; + size_t end; + struct attr *prev; + struct attr *next; +}; + +struct uiprivAttrList { + struct attr *first; + struct attr *last; +}; + +struct uiAttributedString { + char *s; + size_t len; + + uiprivAttrList *attrs; + + // indiscriminately keep a UTF-16 copy of the string on all platforms so we can hand this off to the grapheme calculator + // this ensures no one platform has a speed advantage (sorry GTK+) + uint16_t *u16; + size_t u16len; + + size_t *u8tou16; + size_t *u16tou8; + + // this is lazily created to keep things from getting *too* slow + uiprivGraphemes *graphemes; +}; + + +_UI_EXTERN void uiLabelSetAttribute(uiLabel *label, uiAttribute *attr) +{ + struct uiDrawTextLayoutParams params; + params.String = uiNewAttributedString(""); + + uiAttributedStringSetAttribute(params.String, attr, 0, -1); + + PangoAttrList *pango_list = uiprivAttributedStringToPangoAttrList(¶ms); + + PangoAttrList *old_list = gtk_label_get_attributes(GTK_LABEL(label->widget)); + + GSList *attr_list = pango_attr_list_get_attributes(old_list); + + for (; attr_list != NULL; attr_list = attr_list->next) { + pango_attr_list_insert(pango_list, attr_list->data); + } + + gtk_label_set_attributes(GTK_LABEL(label->widget), pango_list); +} + +#endif diff --git a/win.mk b/win.mk index bac80185..33dbc644 100644 --- a/win.mk +++ b/win.mk @@ -18,6 +18,10 @@ LDFLAGS=$(LIBS) libui_win64.a: $(O_FILES) x86_64-w64-mingw32-ar rsc libui_win64.a $(O_FILES) +install: libui_win64.a + cp libui_win64.a /usr/x86_64-w64-mingw32/lib/libui.a + cp $(LIBUI)/ui.h /usr/x86_64-w64-mingw32/include/ + build: libui_win64.a -mkdir build cd $(LIBUI) && meson setup build && ninja -C build