From 6fd280bb5b27842a0ef2977798566c37bd4e1d0e Mon Sep 17 00:00:00 2001 From: Ashish Gupta Date: Wed, 5 Apr 2017 21:39:01 +0200 Subject: Add kolibrios/ dir : Step 1 towards porting this to Kolibri OS --- frontends/kolibrios/fb/Makefile | 184 +++ frontends/kolibrios/fb/Makefile.defaults | 51 + frontends/kolibrios/fb/bitmap.c | 352 +++++ frontends/kolibrios/fb/bitmap.h | 26 + frontends/kolibrios/fb/clipboard.c | 105 ++ frontends/kolibrios/fb/clipboard.h | 24 + frontends/kolibrios/fb/convert_font.c | 1215 ++++++++++++++++ frontends/kolibrios/fb/convert_image.c | 304 ++++ frontends/kolibrios/fb/fb_search.c | 74 + frontends/kolibrios/fb/fbtk.h | 618 +++++++++ frontends/kolibrios/fb/fbtk/bitmap.c | 136 ++ frontends/kolibrios/fb/fbtk/event.c | 349 +++++ frontends/kolibrios/fb/fbtk/fbtk.c | 830 +++++++++++ frontends/kolibrios/fb/fbtk/fill.c | 81 ++ frontends/kolibrios/fb/fbtk/osk.c | 199 +++ frontends/kolibrios/fb/fbtk/scroll.c | 589 ++++++++ frontends/kolibrios/fb/fbtk/text.c | 654 +++++++++ frontends/kolibrios/fb/fbtk/user.c | 63 + frontends/kolibrios/fb/fbtk/widget.h | 259 ++++ frontends/kolibrios/fb/fbtk/window.c | 91 ++ frontends/kolibrios/fb/fetch.c | 100 ++ frontends/kolibrios/fb/fetch.h | 25 + frontends/kolibrios/fb/findfile.c | 56 + frontends/kolibrios/fb/findfile.h | 32 + frontends/kolibrios/fb/font.h | 68 + frontends/kolibrios/fb/font_freetype.c | 566 ++++++++ frontends/kolibrios/fb/font_freetype.h | 30 + frontends/kolibrios/fb/font_internal.c | 492 +++++++ frontends/kolibrios/fb/font_internal.h | 49 + frontends/kolibrios/fb/framebuffer.c | 651 +++++++++ frontends/kolibrios/fb/framebuffer.h | 40 + frontends/kolibrios/fb/gui.c | 2226 ++++++++++++++++++++++++++++++ frontends/kolibrios/fb/gui.h | 84 ++ frontends/kolibrios/fb/image_data.h | 60 + frontends/kolibrios/fb/localhistory.c | 144 ++ frontends/kolibrios/fb/options.h | 70 + frontends/kolibrios/fb/schedule.c | 254 ++++ frontends/kolibrios/fb/schedule.h | 45 + 38 files changed, 11196 insertions(+) create mode 100644 frontends/kolibrios/fb/Makefile create mode 100644 frontends/kolibrios/fb/Makefile.defaults create mode 100644 frontends/kolibrios/fb/bitmap.c create mode 100644 frontends/kolibrios/fb/bitmap.h create mode 100644 frontends/kolibrios/fb/clipboard.c create mode 100644 frontends/kolibrios/fb/clipboard.h create mode 100644 frontends/kolibrios/fb/convert_font.c create mode 100644 frontends/kolibrios/fb/convert_image.c create mode 100644 frontends/kolibrios/fb/fb_search.c create mode 100644 frontends/kolibrios/fb/fbtk.h create mode 100644 frontends/kolibrios/fb/fbtk/bitmap.c create mode 100644 frontends/kolibrios/fb/fbtk/event.c create mode 100644 frontends/kolibrios/fb/fbtk/fbtk.c create mode 100644 frontends/kolibrios/fb/fbtk/fill.c create mode 100644 frontends/kolibrios/fb/fbtk/osk.c create mode 100644 frontends/kolibrios/fb/fbtk/scroll.c create mode 100644 frontends/kolibrios/fb/fbtk/text.c create mode 100644 frontends/kolibrios/fb/fbtk/user.c create mode 100644 frontends/kolibrios/fb/fbtk/widget.h create mode 100644 frontends/kolibrios/fb/fbtk/window.c create mode 100644 frontends/kolibrios/fb/fetch.c create mode 100644 frontends/kolibrios/fb/fetch.h create mode 100644 frontends/kolibrios/fb/findfile.c create mode 100644 frontends/kolibrios/fb/findfile.h create mode 100644 frontends/kolibrios/fb/font.h create mode 100644 frontends/kolibrios/fb/font_freetype.c create mode 100644 frontends/kolibrios/fb/font_freetype.h create mode 100644 frontends/kolibrios/fb/font_internal.c create mode 100644 frontends/kolibrios/fb/font_internal.h create mode 100644 frontends/kolibrios/fb/framebuffer.c create mode 100644 frontends/kolibrios/fb/framebuffer.h create mode 100644 frontends/kolibrios/fb/gui.c create mode 100644 frontends/kolibrios/fb/gui.h create mode 100644 frontends/kolibrios/fb/image_data.h create mode 100644 frontends/kolibrios/fb/localhistory.c create mode 100644 frontends/kolibrios/fb/options.h create mode 100644 frontends/kolibrios/fb/schedule.c create mode 100644 frontends/kolibrios/fb/schedule.h (limited to 'frontends/kolibrios/fb') diff --git a/frontends/kolibrios/fb/Makefile b/frontends/kolibrios/fb/Makefile new file mode 100644 index 000000000..6d2acb079 --- /dev/null +++ b/frontends/kolibrios/fb/Makefile @@ -0,0 +1,184 @@ +# ---------------------------------------------------------------------------- +# Framebuffer target setup +# ---------------------------------------------------------------------------- + +CFLAGS += -Dnsframebuffer + +#resource path +CFLAGS += '-DNETSURF_FB_RESPATH="$(NETSURF_FB_RESPATH)"' + +# compile time font locations +CFLAGS += '-DNETSURF_FB_FONTPATH="$(NETSURF_FB_FONTPATH)"' +CFLAGS += '-DNETSURF_FB_FONT_SANS_SERIF="$(NETSURF_FB_FONT_SANS_SERIF)"' +CFLAGS += '-DNETSURF_FB_FONT_SANS_SERIF_BOLD="$(NETSURF_FB_FONT_SANS_SERIF_BOLD)"' +CFLAGS += '-DNETSURF_FB_FONT_SANS_SERIF_ITALIC="$(NETSURF_FB_FONT_SANS_SERIF_ITALIC)"' +CFLAGS += '-DNETSURF_FB_FONT_SANS_SERIF_ITALIC_BOLD="$(NETSURF_FB_FONT_SANS_SERIF_ITALIC_BOLD)"' +CFLAGS += '-DNETSURF_FB_FONT_SERIF="$(NETSURF_FB_FONT_SERIF)"' +CFLAGS += '-DNETSURF_FB_FONT_SERIF_BOLD="$(NETSURF_FB_FONT_SERIF_BOLD)"' +CFLAGS += '-DNETSURF_FB_FONT_MONOSPACE="$(NETSURF_FB_FONT_MONOSPACE)"' +CFLAGS += '-DNETSURF_FB_FONT_MONOSPACE_BOLD="$(NETSURF_FB_FONT_MONOSPACE_BOLD)"' +CFLAGS += '-DNETSURF_FB_FONT_CURSIVE="$(NETSURF_FB_FONT_CURSIVE)"' +CFLAGS += '-DNETSURF_FB_FONT_FANTASY="$(NETSURF_FB_FONT_FANTASY)"' + +CFLAGS += -std=c99 -g -Dsmall \ + -D_BSD_SOURCE \ + -D_DEFAULT_SOURCE \ + -D_XOPEN_SOURCE=600 \ + -D_POSIX_C_SOURCE=200809L + +LDFLAGS += -lm + +# non optional pkg-configed libs +LDFLAGS += -Wl,--whole-archive +$(eval $(call pkg_config_find_and_add,libnsfb,libnsfb)) +LDFLAGS += -Wl,--no-whole-archive + +# freetype is optional but does not use pkg-config +ifeq ($(NETSURF_FB_FONTLIB),freetype) + CFLAGS += -DFB_USE_FREETYPE $(shell freetype-config --cflags) + LDFLAGS += $(shell freetype-config --libs) +endif + + +# ---------------------------------------------------------------------------- +# built-in resource setup +# ---------------------------------------------------------------------------- + +FB_IMAGE_left_arrow := icons/back.png +FB_IMAGE_right_arrow := icons/forward.png +FB_IMAGE_reload := icons/reload.png +FB_IMAGE_stop_image := icons/stop.png +FB_IMAGE_history_image := icons/history.png + +FB_IMAGE_left_arrow_g := icons/back_g.png +FB_IMAGE_right_arrow_g := icons/forward_g.png +FB_IMAGE_reload_g := icons/reload_g.png +FB_IMAGE_stop_image_g := icons/stop_g.png +FB_IMAGE_history_image_g := icons/history_g.png + +FB_IMAGE_scrolll := icons/scrolll.png +FB_IMAGE_scrollr := icons/scrollr.png +FB_IMAGE_scrollu := icons/scrollu.png +FB_IMAGE_scrolld := icons/scrolld.png + +FB_IMAGE_osk_image := icons/osk.png + +FB_IMAGE_pointer_image := pointers/default.png +FB_IMAGE_hand_image := pointers/point.png +FB_IMAGE_caret_image := pointers/caret.png +FB_IMAGE_menu_image := pointers/menu.png +FB_IMAGE_progress_image := pointers/progress.png +FB_IMAGE_move_image := pointers/move.png + +FB_IMAGE_throbber0 := throbber/throbber0.png +FB_IMAGE_throbber1 := throbber/throbber1.png +FB_IMAGE_throbber2 := throbber/throbber2.png +FB_IMAGE_throbber3 := throbber/throbber3.png +FB_IMAGE_throbber4 := throbber/throbber4.png +FB_IMAGE_throbber5 := throbber/throbber5.png +FB_IMAGE_throbber6 := throbber/throbber6.png +FB_IMAGE_throbber7 := throbber/throbber7.png +FB_IMAGE_throbber8 := throbber/throbber8.png + +# local compiler flags +ifeq ($(HOST),OpenBSD) + HOST_CFLAGS += $(shell $(PKG_CONFIG) --cflags libpng) + HOST_LDFLAGS += $(shell $(PKG_CONFIG) --libs libpng) +else + HOST_CFLAGS += + HOST_LDFLAGS += -lpng +endif + +# Host tool to convert image bitmaps to source code. +# +# convert_image dependd on fb_bitmap.h so that if we change that +# header, we get new images built. +$(TOOLROOT)/convert_image: $(TOOLROOT)/created $(FRONTEND_SOURCE_DIR)/convert_image.c $(FRONTEND_SOURCE_DIR)/fbtk.h + $(VQ)echo " HOST CC: $@" + $(Q)$(HOST_CC) $(HOST_CFLAGS) -o $@ $(FRONTEND_SOURCE_DIR)/convert_image.c $(HOST_LDFLAGS) + +# 1: input file +# 2: output file +# 3: bitmap name +define convert_image + +S_IMAGES += $(2) + +$(2): $(1) $(TOOLROOT)/convert_image + $(Q)$(TOOLROOT)/convert_image $(1) $(2) $(3) + +endef + +S_IMAGES := + +$(eval $(foreach V,$(filter FB_IMAGE_%,$(.VARIABLES)),$(call convert_image,$(FRONTEND_RESOURCES_DIR)/$($(V)),$(OBJROOT)/image-$(patsubst FB_IMAGE_%,%,$(V)).c,$(patsubst FB_IMAGE_%,%,$(V))))) + + +# Internal fonts to generate +FB_FONT_internal_ns-sans := fonts/glyph_data + +# Internal font conversion +$(TOOLROOT)/convert_font: $(TOOLROOT)/created $(FRONTEND_SOURCE_DIR)/convert_font.c + $(VQ)echo " HOST CC: $@" + $(Q)$(HOST_CC) -o $@ $(FRONTEND_SOURCE_DIR)/convert_font.c + +# 1: input file +# 2: output source code file +# 3: output header file +# 4: font name +define convert_font + +S_FONTS += $(2) + +$(2): $(1) $(TOOLROOT)/convert_font + $(VQ)echo " FONT: $(1) ($(4))" + $(Q)$(TOOLROOT)/convert_font -H $(3) $(1) $(2) + +endef + +S_FONTS := + +$(eval $(foreach V,$(filter FB_FONT_$(NETSURF_FB_FONTLIB)_%,$(.VARIABLES)),$(call convert_font,$(FRONTEND_RESOURCES_DIR)/$($(V)),$(OBJROOT)/font-$(patsubst FB_FONT_$(NETSURF_FB_FONTLIB)_%,%,$(V)).c,$(OBJROOT)/font-$(patsubst FB_FONT_$(NETSURF_FB_FONTLIB)_%,%,$(V)).h,$(patsubst FB_FONT_$(NETSURF_FB_FONTLIB)_%,%,$(V))))) + +# ---------------------------------------------------------------------------- +# Source file setup +# ---------------------------------------------------------------------------- + +# S_FRONTEND are sources purely for the framebuffer build +S_FRONTEND := gui.c framebuffer.c schedule.c bitmap.c fetch.c \ + findfile.c localhistory.c clipboard.c + +# toolkit sources +S_FRAMEBUFFER_FBTK := fbtk.c event.c fill.c bitmap.c user.c window.c \ + text.c scroll.c osk.c + +S_FRONTEND += font_$(NETSURF_FB_FONTLIB).c + +S_FRONTEND += $(addprefix fbtk/,$(S_FRAMEBUFFER_FBTK)) + +# This is the final source build list +# Note this is deliberately *not* expanded here as common and image +# are not yet available +SOURCES = $(S_COMMON) $(S_IMAGE) $(S_BROWSER) $(S_FRONTEND) $(S_IMAGES) $(S_FONTS) +EXETARGET := nsfb + +# ---------------------------------------------------------------------------- +# Install target +# ---------------------------------------------------------------------------- + +NETSURF_FRAMEBUFFER_RESOURCE_LIST := adblock.css credits.html \ + default.css internal.css licence.html \ + netsurf.png quirks.css welcome.html maps.html Messages + +install-framebuffer: + $(Q)$(MKDIR) -p $(DESTDIR)$(NETSURF_FRAMEBUFFER_BIN) + $(Q)$(MKDIR) -p $(DESTDIR)$(NETSURF_FRAMEBUFFER_RESOURCES) + $(Q)cp -v $(EXETARGET) $(DESTDIR)/$(NETSURF_FRAMEBUFFER_BIN)netsurf-fb + $(Q)for F in $(NETSURF_FRAMEBUFFER_RESOURCE_LIST); do cp -vL $(FRONTEND_RESOURCES_DIR)/$$F $(DESTDIR)/$(NETSURF_FRAMEBUFFER_RESOURCES); done + $(Q)$(SPLIT_MESSAGES) -l en -p fb -f messages resources/FatMessages | gzip -9n > $(DESTDIR)$(NETSURF_FRAMEBUFFER_RESOURCES)messages + +# ---------------------------------------------------------------------------- +# Package target +# ---------------------------------------------------------------------------- + +package-framebuffer: diff --git a/frontends/kolibrios/fb/Makefile.defaults b/frontends/kolibrios/fb/Makefile.defaults new file mode 100644 index 000000000..cc712e992 --- /dev/null +++ b/frontends/kolibrios/fb/Makefile.defaults @@ -0,0 +1,51 @@ +# ---------------------------------------------------------------------------- +# Framebuffer-target-specific options +# ---------------------------------------------------------------------------- + +# Optimisation levels +CFLAGS += -O2 + +# Framebuffer default surface provider. +# Valid values are: x, sdl, linux, vnc, able, +NETSURF_FB_FRONTEND := sdl + +# Use libharu to enable PDF export and GTK printing support. +# Valid options: YES, NO +NETSURF_USE_HARU_PDF := NO + +# Enable NetSurf's use of libsvgtiny for displaying SVGs +# Valid options: YES, NO, AUTO +NETSURF_USE_NSSVG := AUTO + +# Enable NetSurf's use of librosprite for displaying RISC OS Sprites +# Valid options: YES, NO, AUTO +NETSURF_USE_ROSPRITE := AUTO + +# Library to use for font plotting +# Valid options: internal, freetype +NETSURF_FB_FONTLIB := internal + +# Default freetype font files +NETSURF_FB_FONT_SANS_SERIF := DejaVuSans.ttf +NETSURF_FB_FONT_SANS_SERIF_BOLD := DejaVuSans-Bold.ttf +NETSURF_FB_FONT_SANS_SERIF_ITALIC := DejaVuSans-Oblique.ttf +NETSURF_FB_FONT_SANS_SERIF_ITALIC_BOLD := DejaVuSans-BoldOblique.ttf +NETSURF_FB_FONT_SERIF := DejaVuSerif.ttf +NETSURF_FB_FONT_SERIF_BOLD := DejaVuSerif-Bold.ttf +NETSURF_FB_FONT_MONOSPACE := DejaVuSansMono.ttf +NETSURF_FB_FONT_MONOSPACE_BOLD := DejaVuSansMono-Bold.ttf +NETSURF_FB_FONT_CURSIVE := Comic_Sans_MS.ttf +NETSURF_FB_FONT_FANTASY := Impact.ttf + +# Default binary install path +NETSURF_FRAMEBUFFER_BIN := $(PREFIX)/bin/ + +# Default resource install path +NETSURF_FRAMEBUFFER_RESOURCES := $(PREFIX)/share/netsurf/ + +# Default framebuffer search path +NETSURF_FB_RESPATH := $${HOME}/.netsurf/:$${NETSURFRES}:$(NETSURF_FRAMEBUFFER_RESOURCES):./frontends/framebuffer/res + +# freetype compiled in font serch path +NETSURF_FB_FONTPATH := /usr/share/fonts/truetype/dejavu:/usr/share/fonts/truetype/msttcorefonts + diff --git a/frontends/kolibrios/fb/bitmap.c b/frontends/kolibrios/fb/bitmap.c new file mode 100644 index 000000000..027e0122b --- /dev/null +++ b/frontends/kolibrios/fb/bitmap.c @@ -0,0 +1,352 @@ +/* + * Copyright 2008 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf 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 . + */ + +/** + * \file + * Framebuffer implementation of generic bitmap interface. + */ + +#include +#include +#include +#include +#include +#include + +#include "utils/log.h" +#include "utils/utils.h" +#include "netsurf/bitmap.h" +#include "netsurf/plotters.h" +#include "netsurf/content.h" + +#include "framebuffer/gui.h" +#include "framebuffer/fbtk.h" +#include "framebuffer/framebuffer.h" +#include "framebuffer/bitmap.h" + +/** + * Create a bitmap. + * + * \param width width of image in pixels + * \param height width of image in pixels + * \param state a flag word indicating the initial state + * \return an opaque struct bitmap, or NULL on memory exhaustion + */ +static void *bitmap_create(int width, int height, unsigned int state) +{ + nsfb_t *bm; + + LOG("width %d, height %d, state %u", width, height, state); + + bm = nsfb_new(NSFB_SURFACE_RAM); + if (bm == NULL) { + return NULL; + } + + if ((state & BITMAP_OPAQUE) == 0) { + nsfb_set_geometry(bm, width, height, NSFB_FMT_ABGR8888); + } else { + nsfb_set_geometry(bm, width, height, NSFB_FMT_XBGR8888); + } + + if (nsfb_init(bm) == -1) { + nsfb_free(bm); + return NULL; + } + + LOG("bitmap %p", bm); + + return bm; +} + + +/** + * Return a pointer to the pixel data in a bitmap. + * + * \param bitmap a bitmap, as returned by bitmap_create() + * \return pointer to the pixel buffer + * + * The pixel data is packed as BITMAP_FORMAT, possibly with padding at the end + * of rows. The width of a row in bytes is given by bitmap_get_rowstride(). + */ +static unsigned char *bitmap_get_buffer(void *bitmap) +{ + nsfb_t *bm = bitmap; + unsigned char *bmpptr; + + assert(bm != NULL); + + nsfb_get_buffer(bm, &bmpptr, NULL); + + return bmpptr; +} + + +/** + * Find the width of a pixel row in bytes. + * + * \param bitmap a bitmap, as returned by bitmap_create() + * \return width of a pixel row in the bitmap + */ +static size_t bitmap_get_rowstride(void *bitmap) +{ + nsfb_t *bm = bitmap; + int bmpstride; + + assert(bm != NULL); + + nsfb_get_buffer(bm, NULL, &bmpstride); + + return bmpstride; +} + + +/** + * Free a bitmap. + * + * \param bitmap a bitmap, as returned by bitmap_create() + */ +static void bitmap_destroy(void *bitmap) +{ + nsfb_t *bm = bitmap; + + assert(bm != NULL); + + nsfb_free(bm); +} + + +/** + * Save a bitmap in the platform's native format. + * + * \param bitmap a bitmap, as returned by bitmap_create() + * \param path pathname for file + * \param flags flags controlling how the bitmap is saved. + * \return true on success, false on error and error reported + */ +static bool bitmap_save(void *bitmap, const char *path, unsigned flags) +{ + return true; +} + + +/** + * The bitmap image has changed, so flush any persistant cache. + * + * \param bitmap a bitmap, as returned by bitmap_create() + */ +static void bitmap_modified(void *bitmap) { +} + +/** + * Sets wether a bitmap should be plotted opaque + * + * \param bitmap a bitmap, as returned by bitmap_create() + * \param opaque whether the bitmap should be plotted opaque + */ +static void bitmap_set_opaque(void *bitmap, bool opaque) +{ + nsfb_t *bm = bitmap; + + assert(bm != NULL); + + if (opaque) { + nsfb_set_geometry(bm, 0, 0, NSFB_FMT_XBGR8888); + } else { + nsfb_set_geometry(bm, 0, 0, NSFB_FMT_ABGR8888); + } +} + + +/** + * Tests whether a bitmap has an opaque alpha channel + * + * \param bitmap a bitmap, as returned by bitmap_create() + * \return whether the bitmap is opaque + */ +static bool bitmap_test_opaque(void *bitmap) +{ + int tst; + nsfb_t *bm = bitmap; + unsigned char *bmpptr; + int width; + int height; + + assert(bm != NULL); + + nsfb_get_buffer(bm, &bmpptr, NULL); + + nsfb_get_geometry(bm, &width, &height, NULL); + + tst = width * height; + + while (tst-- > 0) { + if (bmpptr[(tst << 2) + 3] != 0xff) { + LOG("bitmap %p has transparency", bm); + return false; + } + } + LOG("bitmap %p is opaque", bm); + return true; +} + + +/** + * Gets weather a bitmap should be plotted opaque + * + * \param bitmap a bitmap, as returned by bitmap_create() + */ +bool framebuffer_bitmap_get_opaque(void *bitmap) +{ + nsfb_t *bm = bitmap; + enum nsfb_format_e format; + + assert(bm != NULL); + + nsfb_get_geometry(bm, NULL, NULL, &format); + + if (format == NSFB_FMT_ABGR8888) + return false; + + return true; +} + +static int bitmap_get_width(void *bitmap) +{ + nsfb_t *bm = bitmap; + int width; + + assert(bm != NULL); + + nsfb_get_geometry(bm, &width, NULL, NULL); + + return(width); +} + +static int bitmap_get_height(void *bitmap) +{ + nsfb_t *bm = bitmap; + int height; + + assert(bm != NULL); + + nsfb_get_geometry(bm, NULL, &height, NULL); + + return(height); +} + +/* get bytes per pixel */ +static size_t bitmap_get_bpp(void *bitmap) +{ + return 4; +} + +/** + * Render content into a bitmap. + * + * \param bitmap the bitmap to draw to + * \param content content structure to render + * \return true on success and bitmap updated else false + */ +static nserror +bitmap_render(struct bitmap *bitmap, + struct hlcache_handle *content) +{ + nsfb_t *tbm = (nsfb_t *)bitmap; /* target bitmap */ + nsfb_t *bm; /* temporary bitmap */ + nsfb_t *current; /* current main fb */ + int width, height; /* target bitmap width height */ + int cwidth, cheight;/* content width /height */ + nsfb_bbox_t loc; + + struct redraw_context ctx = { + .interactive = false, + .background_images = true, + .plot = &fb_plotters + }; + + nsfb_get_geometry(tbm, &width, &height, NULL); + + LOG("width %d, height %d", width, height); + + /* Calculate size of buffer to render the content into */ + /* We get the width from the content width, unless it exceeds 1024, + * in which case we use 1024. This means we never create excessively + * large render buffers for huge contents, which would eat memory and + * cripple performance. */ + cwidth = min(content_get_width(content), 1024); + /* The height is set in proportion with the width, according to the + * aspect ratio of the required thumbnail. */ + cheight = ((cwidth * height) + (width / 2)) / width; + + /* create temporary surface */ + bm = nsfb_new(NSFB_SURFACE_RAM); + if (bm == NULL) { + return NSERROR_NOMEM; + } + + nsfb_set_geometry(bm, cwidth, cheight, NSFB_FMT_XBGR8888); + + if (nsfb_init(bm) == -1) { + nsfb_free(bm); + return NSERROR_NOMEM; + } + + current = framebuffer_set_surface(bm); + + /* render the content into temporary surface */ + content_scaled_redraw(content, cwidth, cheight, &ctx); + + framebuffer_set_surface(current); + + loc.x0 = 0; + loc.y0 = 0; + loc.x1 = width; + loc.y1 = height; + + nsfb_plot_copy(bm, NULL, tbm, &loc); + + nsfb_free(bm); + + return NSERROR_OK; +} + +static struct gui_bitmap_table bitmap_table = { + .create = bitmap_create, + .destroy = bitmap_destroy, + .set_opaque = bitmap_set_opaque, + .get_opaque = framebuffer_bitmap_get_opaque, + .test_opaque = bitmap_test_opaque, + .get_buffer = bitmap_get_buffer, + .get_rowstride = bitmap_get_rowstride, + .get_width = bitmap_get_width, + .get_height = bitmap_get_height, + .get_bpp = bitmap_get_bpp, + .save = bitmap_save, + .modified = bitmap_modified, + .render = bitmap_render, +}; + +struct gui_bitmap_table *framebuffer_bitmap_table = &bitmap_table; + + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/kolibrios/fb/bitmap.h b/frontends/kolibrios/fb/bitmap.h new file mode 100644 index 000000000..0a72f19c8 --- /dev/null +++ b/frontends/kolibrios/fb/bitmap.h @@ -0,0 +1,26 @@ +/* + * Copyright 2015 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NS_FB_BITMAP_H +#define NS_FB_BITMAP_H + +extern struct gui_bitmap_table *framebuffer_bitmap_table; + +bool framebuffer_bitmap_get_opaque(void *bitmap); + +#endif /* NS_FB_BITMAP_H */ diff --git a/frontends/kolibrios/fb/clipboard.c b/frontends/kolibrios/fb/clipboard.c new file mode 100644 index 000000000..1254c36f3 --- /dev/null +++ b/frontends/kolibrios/fb/clipboard.c @@ -0,0 +1,105 @@ +/* + * Copyright 2012 Michael Drake + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf 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 . + */ + +/** \file + * nsfb internal clipboard handling + */ + +#include +#include +#include +#include + +#include "utils/log.h" +#include "netsurf/browser_window.h" +#include "netsurf/clipboard.h" + +#include "framebuffer/gui.h" +#include "framebuffer/clipboard.h" + + +static struct gui_clipboard { + char *buffer; + size_t buffer_len; + size_t length; +} gui_clipboard; + + +/** + * Core asks front end for clipboard contents. + * + * \param buffer UTF-8 text, allocated by front end, ownership yeilded to core + * \param length Byte length of UTF-8 text in buffer + */ +static void gui_get_clipboard(char **buffer, size_t *length) +{ + *buffer = NULL; + *length = 0; + + if (gui_clipboard.length > 0) { + assert(gui_clipboard.buffer != NULL); + LOG("Pasting %zd bytes: \"%s\"\n", + gui_clipboard.length, gui_clipboard.buffer); + + *buffer = malloc(gui_clipboard.length); + + if (*buffer != NULL) { + memcpy(*buffer, gui_clipboard.buffer, + gui_clipboard.length); + *length = gui_clipboard.length; + } + } +} + + +/** + * Core tells front end to put given text in clipboard + * + * \param buffer UTF-8 text, owned by core + * \param length Byte length of UTF-8 text in buffer + * \param styles Array of styles given to text runs, owned by core, or NULL + * \param n_styles Number of text run styles in array + */ +static void gui_set_clipboard(const char *buffer, size_t length, + nsclipboard_styles styles[], int n_styles) +{ + if (gui_clipboard.buffer_len < length + 1) { + /* Make buffer big enough */ + char *new_buff; + + new_buff = realloc(gui_clipboard.buffer, length + 1); + if (new_buff == NULL) + return; + + gui_clipboard.buffer = new_buff; + gui_clipboard.buffer_len = length + 1; + } + + gui_clipboard.length = 0; + + memcpy(gui_clipboard.buffer, buffer, length); + gui_clipboard.length = length; + gui_clipboard.buffer[gui_clipboard.length] = '\0'; +} + +static struct gui_clipboard_table clipboard_table = { + .get = gui_get_clipboard, + .set = gui_set_clipboard, +}; + +struct gui_clipboard_table *framebuffer_clipboard_table = &clipboard_table; diff --git a/frontends/kolibrios/fb/clipboard.h b/frontends/kolibrios/fb/clipboard.h new file mode 100644 index 000000000..b5f7b0f29 --- /dev/null +++ b/frontends/kolibrios/fb/clipboard.h @@ -0,0 +1,24 @@ +/* + * Copyright 2008 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NETSURF_FB_CLIPBOARD_H +#define NETSURF_FB_CLIPBOARD_H + +extern struct gui_clipboard_table *framebuffer_clipboard_table; + +#endif diff --git a/frontends/kolibrios/fb/convert_font.c b/frontends/kolibrios/fb/convert_font.c new file mode 100644 index 000000000..010af857a --- /dev/null +++ b/frontends/kolibrios/fb/convert_font.c @@ -0,0 +1,1215 @@ +/* + * Copyright 2014 Michael Drake + * Copyright 2014 Vincent Sanders + * + * This file is part of the convert_font tool used to convert font + * glyph data into a compilable representation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define GLYPH_LEN 16 +#define BUCKETS 512 +#define CHUNK_SIZE (64 * 1024) +#define HEADER_MAX 2000 + +#define SECTION_SIZE (sizeof(uint16_t) * 256) + +const char *labels[4] = { + " Regular", + " Italic", + " Bold", + "Bold & Italic" +}; + +const char *var_lables[4] = { + "fb_regular", + "fb_italic", + "fb_bold", + "fb_bold_italic" +}; + +const char *short_labels[4] = { + " ", + " i", + "b ", + "bi" +}; + +enum font_style { + REGULAR = 0, + ITALIC = (1 << 0), + BOLD = (1 << 1), + ITALIC_BOLD = (1 << 2) +}; + +enum log_level { + LOG_DEBUG, + LOG_INFO, + LOG_RESULT, + LOG_WARNING, + LOG_ERROR +}; + +enum log_level level; + +typedef struct glyph_entry { + union { + uint32_t u32[GLYPH_LEN / 4]; + uint8_t u8[GLYPH_LEN]; + } data; + uint32_t index; + struct glyph_entry *next; +} glyph_entry; + +/** Scratch glyph for generated code points */ +uint8_t code_point[GLYPH_LEN]; + +/** Hash table */ +glyph_entry *ht[BUCKETS]; + +#define LOG(lev, fmt, ...) \ + if (lev >= level) \ + printf(fmt, ##__VA_ARGS__); + +/** + * Get hash for glyph data + * \param g Glyph data (GLYPH_LEN bytes) + * \return glyph's hash + */ +static inline uint32_t glyph_hash(const uint8_t *g) +{ + uint32_t hash = 0x811c9dc5; + unsigned int len = GLYPH_LEN; + + while (len > 0) { + hash *= 0x01000193; + hash ^= *g++; + len--; + } + + return hash; +} + + +/** + * Check whether glyphs are identical (compares glyph data) + * + * \param g1 First glyph's data (GLYPH_LEN bytes) + * \param g2 Second glyph's data (GLYPH_LEN bytes) + * \return true iff both glyphs are identical, else false + */ +static inline bool glyphs_match(const uint8_t *g1, const uint8_t *g2) +{ + return (memcmp(g1, g2, GLYPH_LEN) == 0); +} + + +/** + * Add a glyph to a hash chain (or free, and return pointer to existing glyph) + * + * Note that if new glyph already exists in chain, it is freed and a pointer to + * the existing glyph is returned. If the glyph does not exist in the chain + * it is added and its pointer is returned. + * + * \param head Head of hash chain + * \param new New glyph to add (may be freed) + * \return pointer to glyph in hash chain + */ +static glyph_entry * glyph_add_to_chain(glyph_entry **head, glyph_entry *new) +{ + glyph_entry *e = *head; + + if (*head == NULL) { + new->next = NULL; + *head = new; + return new; + } + + do { + if (glyphs_match(new->data.u8, e->data.u8)) { + free(new); + return e; + } + if (e->next == NULL) + break; + e = e->next; + } while (1); + + new->next = e->next; + e->next = new; + return new; +} + + +/** + * Free a glyph entry chain + * + * \param head Head of hash chain + */ +static void free_chain(glyph_entry *head) +{ + glyph_entry *e = head; + + if (head == NULL) + return; + + while (e != NULL) { + head = e->next; + free(e); + e = head; + }; +} + + +/** + * Add new glyph to hash table (or free, and return pointer to existing glyph) + * + * Note that if new glyph already exists in table, it is freed and a pointer to + * the existing glyph is returned. If the glyph does not exist in the table + * it is added and its pointer is returned. + * + * \param new New glyph to add (may be freed) + * \return pointer to glyph in hash table + */ +static glyph_entry * glyph_add_to_table(glyph_entry *new) +{ + uint32_t hash = glyph_hash(new->data.u8); + + return glyph_add_to_chain(&ht[hash % BUCKETS], new); +} + + +/** + * Free glyph table. + */ +static void free_table(void) +{ + int i; + + for (i = 0; i < BUCKETS; i++) { + free_chain(ht[i]); + } +} + +struct parse_context { + enum { + START, + IN_HEADER, + BEFORE_ID, + GLYPH_ID, + BEFORE_GLYPH_DATA, + IN_GLYPH_DATA + } state; /**< Current parser state */ + + union { + struct { + bool new_line; + } in_header; + struct { + bool new_line; + bool u; + } before_id; + struct { + int c; + } g_id; + struct { + bool new_line; + bool prev_h; + bool prev_s; + int c; + } before_gd; + struct { + int line; + int pos; + int styles; + int line_styles; + glyph_entry *e[4]; + } in_gd; + } data; /**< The state specific data */ + + int id; /**< Current ID */ + + int codepoints; /**< Glyphs containing codepoints */ + int count[4]; /**< Count of glyphs in file */ +}; + +struct font_data { + char header[HEADER_MAX]; + int header_len; + + uint8_t section_table[4][256]; + uint8_t sec_count[4]; + uint16_t *sections[4]; + + glyph_entry *e[0xffff]; + int glyphs; +}; + +bool generate_font_header(const char *path, struct font_data *data) +{ + FILE *fp; + int s; + + fp = fopen(path, "wb"); + if (fp == NULL) { + LOG(LOG_ERROR, "Couldn't open header file \"%s\"\n", path); + return false; + } + + fprintf(fp, "/*\n"); + fwrite(data->header, 1, data->header_len, fp); + fprintf(fp, " */\n\n"); + fprintf(fp, "/* Don't edit this file, it was generated from the " + "plain text source data. */\n\n"); + + + for (s = 0; s < 4; s++) { + fprintf(fp, "const uint8_t *%s_section_table;\n", + var_lables[s]); + fprintf(fp, "const uint16_t *%s_sections;\n", + var_lables[s]); + + } + + fprintf(fp, "const uint8_t *font_glyph_data;\n"); + + fprintf(fp, "\n\n"); + + fclose(fp); + + return true; + +} + +bool generate_font_source(const char *path, struct font_data *data) +{ + int s, i, y; + int limit; + FILE *fp; + + fp = fopen(path, "wb"); + if (fp == NULL) { + LOG(LOG_ERROR, "Couldn't open output file \"%s\"\n", path); + return false; + } + + fprintf(fp, "/*\n"); + fwrite(data->header, 1, data->header_len, fp); + fprintf(fp, " */\n\n"); + fprintf(fp, "/* Don't edit this file, it was generated from the " + "plain text source data. */\n\n"); + + fprintf(fp, "#include \n"); + fprintf(fp, "\n"); + + for (s = 0; s < 4; s++) { + + fprintf(fp, "static const uint8_t %s_section_table_c[256] = {\n", + var_lables[s]); + + for (i = 0; i < 256; i++) { + if (i == 255) + fprintf(fp, "0x%.2X\n", + data->section_table[s][i]); + else if (i % 8 == 7) + fprintf(fp, "0x%.2X,\n", + data->section_table[s][i]); + else if (i % 8 == 0) + fprintf(fp, "\t0x%.2X, ", + data->section_table[s][i]); + else + fprintf(fp, "0x%.2X, ", + data->section_table[s][i]); + } + + fprintf(fp, "};\nconst uint8_t *%s_section_table = &%s_section_table_c[0];\n\n", + var_lables[s], var_lables[s]); + fprintf(fp, "static const uint16_t %s_sections_c[%i] = {\n", + var_lables[s], data->sec_count[s] * 256); + + limit = data->sec_count[s] * 256; + for (i = 0; i < limit; i++) { + uint16_t offset = data->sections[s][i]; + if (i == limit - 1) + fprintf(fp, "0x%.4X\n", offset); + else if (i % 4 == 3) + fprintf(fp, "0x%.4X,\n", offset); + else if (i % 4 == 0) + fprintf(fp, "\t0x%.4X, ", offset); + else + fprintf(fp, "0x%.4X, ", offset); + } + + fprintf(fp, "};\nconst uint16_t *%s_sections = &%s_sections_c[0];\n\n", var_lables[s], var_lables[s]); + } + + fprintf(fp, "static const uint8_t font_glyph_data_c[%i] = {\n", + (data->glyphs + 1) * 16); + + fprintf(fp, "\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n" + "\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n"); + + limit = data->glyphs; + for (i = 0; i < limit; i++) { + glyph_entry *e = data->e[i]; + + for (y = 0; y < 16; y++) { + if (i == limit - 1 && y == 15) + fprintf(fp, "0x%.2X\n", e->data.u8[y]); + else if (y % 8 == 7) + fprintf(fp, "0x%.2X,\n", e->data.u8[y]); + else if (y % 8 == 0) + fprintf(fp, "\t0x%.2X, ", e->data.u8[y]); + else + fprintf(fp, "0x%.2X, ", e->data.u8[y]); + } + } + + fprintf(fp, "};\n"); + fprintf(fp, "const uint8_t *font_glyph_data = &font_glyph_data_c[0];\n\n"); + + fclose(fp); + + return true; +} + +static bool add_glyph_to_data(glyph_entry *add, int id, int style, + struct font_data *d) +{ + glyph_entry *e; + int offset; + int s; + + /* Find out if 'add' is unique, and get its unique table entry */ + e = glyph_add_to_table(add); + if (e == add) { + /* Unique glyph */ + d->e[d->glyphs++] = e; + e->index = d->glyphs; + if (d->glyphs >= 0xfffd) { + LOG(LOG_ERROR, " Too many glyphs for internal data " + "representation\n"); + return false; + } + } else { + /* Duplicate glyph */ + LOG(LOG_DEBUG, " U+%.4X (%s) is duplicate\n", + id, short_labels[style]); + } + + /* Find glyph's section */ + s = id / 256; + + /* Allocate section if needed */ + if ((s == 0 && d->sections[style] == NULL) || + (s != 0 && d->section_table[style][s] == 0)) { + size_t size = (d->sec_count[style] + 1) * SECTION_SIZE; + uint16_t *temp = realloc(d->sections[style], size); + if (temp == NULL) { + LOG(LOG_ERROR, " Couldn't increase sections " + "allocation\n"); + return false; + } + memset(temp + d->sec_count[style] * 256, 0, + SECTION_SIZE); + d->section_table[style][s] = d->sec_count[style]; + d->sections[style] = temp; + d->sec_count[style]++; + } + + offset = d->section_table[style][s] * 256 + (id & 0xff); + d->sections[style][offset] = e->index; + + return true; +} + + +static bool check_glyph_data_valid(int pos, char c) +{ + int offset = pos % 11; + + if (pos == 44) { + if (c != '\n') { + LOG(LOG_ERROR, " Invalid glyph data: " + "expecting '\\n', got '%c' (%i)\n", + c, c); + return false; + } else { + return true; + } + } else if (pos < 3) { + if (c != ' ') { + LOG(LOG_ERROR, " Invalid glyph data: " + "expecting ' ', got '%c' (%i)\n", + c, c); + return false; + } else { + return true; + } + } else if (offset == 0) { + if (c != '\n' && c != ' ') { + LOG(LOG_ERROR, " Invalid glyph data: " + "expecting '\\n' or ' ', " + "got '%c' (%i)\n", + c, c); + return false; + } else { + return true; + } + } else if (offset < 3) { + if (c != ' ') { + LOG(LOG_ERROR, " Invalid glyph data: " + "expecting ' ', got '%c' (%i)\n", + c, c); + return false; + } else { + return true; + } + } else if (offset >= 3 && pos < 11) { + if (c != '.' && c != '#') { + LOG(LOG_ERROR, " Invalid glyph data: " + "expecting '.' or '#', " + "got '%c' (%i)\n", + c, c); + return false; + } else { + return true; + } + } + + /* offset must be >=3 */ + if (c != '.' && c != '#' && c != ' ') { + LOG(LOG_ERROR, " Invalid glyph data: " + "expecting '.', '#', or ' ', " + "got '%c' (%i)\n", + c, c); + return false; + } + + return true; +} + +#define SEVEN_SET ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | \ + (1 << 4) | (1 << 5) | (1 << 6)) + +#define THREE_SSS ((1 << 0) | (1 << 1) | (1 << 2)) +#define THREE_S_S ((1 << 0) | (1 << 2)) +#define THREE__SS ((1 << 0) | (1 << 1) ) +#define THREE_SS_ ( (1 << 1) | (1 << 2)) +#define THREE_S__ (1 << 2) +#define THREE__S_ (1 << 1) +#define THREE___S (1 << 0) + +uint8_t frag[16][5] = { + { THREE_SSS, + THREE_S_S, + THREE_S_S, + THREE_S_S, + THREE_SSS }, + + { THREE__S_, + THREE_SS_, + THREE__S_, + THREE__S_, + THREE_SSS }, + + { THREE_SS_, + THREE___S, + THREE__S_, + THREE_S__, + THREE_SSS }, + + { THREE_SS_, + THREE___S, + THREE_SS_, + THREE___S, + THREE_SS_ }, + + { THREE_S_S, + THREE_S_S, + THREE_SSS, + THREE___S, + THREE___S }, + + { THREE_SSS, + THREE_S__, + THREE_SSS, + THREE___S, + THREE_SSS }, + + { THREE__SS, + THREE_S__, + THREE_SSS, + THREE_S_S, + THREE_SSS }, + + { THREE_SSS, + THREE___S, + THREE__S_, + THREE__S_, + THREE__S_ }, + + { THREE_SSS, + THREE_S_S, + THREE_SSS, + THREE_S_S, + THREE_SSS }, + + { THREE_SSS, + THREE_S_S, + THREE_SSS, + THREE___S, + THREE___S }, + + { THREE__S_, + THREE_S_S, + THREE_SSS, + THREE_S_S, + THREE_S_S }, + + { THREE_SS_, + THREE_S_S, + THREE_SS_, + THREE_S_S, + THREE_SS_ }, + + { THREE__S_, + THREE_S_S, + THREE_S__, + THREE_S_S, + THREE__S_ }, + + { THREE_SS_, + THREE_S_S, + THREE_S_S, + THREE_S_S, + THREE_SS_ }, + + { THREE_SSS, + THREE_S__, + THREE_SS_, + THREE_S__, + THREE_SSS }, + + { THREE_SSS, + THREE_S__, + THREE_SS_, + THREE_S__, + THREE_S__ } +}; + +void build_codepoint(int id, bool italic, uint8_t *code_point) +{ + int shift = 0; + int l; + int r; + + if (!italic) + shift = 1; + + l = (id >> 12); + r = 0xf & (id >> 8); + + code_point[ 0] = 0; + code_point[ 1] = SEVEN_SET << shift; + code_point[ 2] = 0; + + code_point[ 3] = (frag[l][0] << (4 + shift)) | (frag[r][0] << shift); + code_point[ 4] = (frag[l][1] << (4 + shift)) | (frag[r][1] << shift); + code_point[ 5] = (frag[l][2] << (4 + shift)) | (frag[r][2] << shift); + code_point[ 6] = (frag[l][3] << (4 + shift)) | (frag[r][3] << shift); + code_point[ 7] = (frag[l][4] << (4 + shift)) | (frag[r][4] << shift); + + code_point[ 8] = 0; + + shift = 1; + + l = 0xf & (id >> 4); + r = 0xf & id ; + + code_point[ 9] = (frag[l][0] << (4 + shift)) | (frag[r][0] << shift); + code_point[10] = (frag[l][1] << (4 + shift)) | (frag[r][1] << shift); + code_point[11] = (frag[l][2] << (4 + shift)) | (frag[r][2] << shift); + code_point[12] = (frag[l][3] << (4 + shift)) | (frag[r][3] << shift); + code_point[13] = (frag[l][4] << (4 + shift)) | (frag[r][4] << shift); + + code_point[14] = 0; + code_point[15] = SEVEN_SET << shift; +} + +#undef SEVEN_SET +#undef THREE_SSS +#undef THREE_S_S +#undef THREE__SS +#undef THREE_SS_ +#undef THREE_S__ +#undef THREE__S_ +#undef THREE___S + +static bool glyph_is_codepoint(const glyph_entry *e, int id, int style) +{ + bool italic = false; + + if (style == 1 || style == 3) { + italic = true; + } + + build_codepoint(id, italic, code_point); + + return glyphs_match(code_point, e->data.u8); +} + + +static bool parse_glyph_data(struct parse_context *ctx, char c, + struct font_data *d) +{ + int glyph = ctx->data.in_gd.pos / 11; + int g_pos = ctx->data.in_gd.pos % 11 - 3; + uint8_t *row; + bool ok; + int i; + + /* Check that character is valid */ + if (check_glyph_data_valid(ctx->data.in_gd.pos, c) == false) { + LOG(LOG_ERROR, " Error in U+%.4X data: " + "glyph line: %i, pos: %i\n", + ctx->id, + ctx->data.in_gd.line, + ctx->data.in_gd.pos); + goto error; + } + + /* Allocate glyph data if needed */ + if (ctx->data.in_gd.line == 0 && + (c == '.' || c == '#')) { + if (ctx->data.in_gd.e[glyph] == NULL) { + ctx->data.in_gd.e[glyph] = + calloc(sizeof(struct glyph_entry), 1); + if (ctx->data.in_gd.e[glyph] == NULL) { + LOG(LOG_ERROR, " Couldn't allocate memory for " + "glyph entry\n"); + goto error; + } + + ctx->data.in_gd.styles |= 1 << glyph; + } + } + + /* Build glyph data */ + if (c == '#') { + row = &ctx->data.in_gd.e[glyph]->data.u8[ctx->data.in_gd.line]; + *row += 1 << (7 - g_pos); + + ctx->data.in_gd.line_styles |= 1 << glyph; + } else if (c == '.') { + ctx->data.in_gd.line_styles |= 1 << glyph; + } + + /* Deal with current position */ + if (c == '\n') { + if (ctx->data.in_gd.line == 0) { + if (ctx->data.in_gd.e[0] == NULL) { + LOG(LOG_ERROR, " Error in U+%.4X data: " + "\"Regular\" glyph style must " + "be present\n", ctx->id); + goto error; + } + } else if (ctx->data.in_gd.styles != + ctx->data.in_gd.line_styles) { + LOG(LOG_ERROR, " Error in U+%.4X data: " + "glyph line: %i " + "styles don't match first line\n", + ctx->id, + ctx->data.in_gd.line); + goto error; + } + + ctx->data.in_gd.pos = 0; + ctx->data.in_gd.line++; + ctx->data.in_gd.line_styles = 0; + } else { + ctx->data.in_gd.pos++; + } + + /* If we've got all the glyph data, tidy up and advance state */ + if (ctx->data.in_gd.line == 16) { + for (i = 0; i < 4; i++) { + if (ctx->data.in_gd.e[i] != NULL) { + ctx->count[i] += 1; + if (glyph_is_codepoint(ctx->data.in_gd.e[i], + ctx->id, i)) { + LOG(LOG_DEBUG, " U+%.4X (%s) is " + "codepoint\n", + ctx->id, + short_labels[i]); + ctx->codepoints += 1; + free(ctx->data.in_gd.e[i]); + ctx->data.in_gd.e[i] = NULL; + continue; + } + + ok = add_glyph_to_data(ctx->data.in_gd.e[i], + ctx->id, i, d); + if (!ok) { + goto error; + } + } + } + + ctx->data.before_id.new_line = false; + ctx->data.before_id.u = false; + ctx->state = BEFORE_ID; + } + + return true; + +error: + + for (i = 0; i < 4; i++) { + free(ctx->data.in_gd.e[i]); + } + + return false; +} + +static void parse_init(struct parse_context *ctx) +{ + memset(ctx, 0, sizeof(struct parse_context)); +} + +static bool get_hex_digit_value(char c, int *v) +{ + if (c >= '0' && c <= '9') + *v = (c - '0'); + else if (c >= 'A' && c <= 'F') + *v = (10 + c - 'A'); + else { + LOG(LOG_ERROR, "Invalid hex digit '%c' (%i)\n", c, c); + return false; + } + + return true; +} + +static bool assemble_codepoint(const char* c, int n, int *id) +{ + bool ok; + int v; + + ok = get_hex_digit_value(*c, &v); + if (!ok) { + return false; + } + + *id += v << (4 * (3 - n)); + + return true; +} + +static bool parse_chunk(struct parse_context *ctx, const char *buf, size_t len, + struct font_data *d) +{ + int i; + bool ok; + int count[4]; + const char *pos = buf; + const char *end = buf + len; + + for (i = 0; i < 4; i++) { + count[i] = ctx->count[i]; + } + + while (pos < end) { + if (*pos == '\r') { + LOG(LOG_ERROR, "Detected \'\\r\': Bad line ending\n"); + return false; + } + + switch (ctx->state) { + case START: + if (*pos != '*') { + LOG(LOG_ERROR, "First character must be '*'\n"); + printf("Got: %c (%i)\n", *pos, *pos); + return false; + } + d->header_len = 0; + ctx->data.in_header.new_line = true; + ctx->state = IN_HEADER; + + /* Fall through */ + case IN_HEADER: + if (ctx->data.in_header.new_line == true) { + if (*pos != '*') { + LOG(LOG_INFO, " Got header " + "(%i bytes)\n", + d->header_len); + LOG(LOG_DEBUG, " Header:\n\n%.*s\n", + d->header_len, + d->header); + ctx->data.before_id.new_line = false; + ctx->data.before_id.u = false; + ctx->state = BEFORE_ID; + continue; + } else if (*pos == '*') { + d->header[d->header_len++] = ' '; + } + ctx->data.in_header.new_line = false; + + } else if (*pos == '\n') { + ctx->data.in_header.new_line = true; + } + + if (d->header_len == HEADER_MAX) { + LOG(LOG_ERROR, " Header too long " + "(>%i bytes)\n", + d->header_len); + return false; + } + + d->header[d->header_len++] = *pos; + break; + + case BEFORE_ID: + if (*pos == '+' && + ctx->data.before_id.new_line == true && + ctx->data.before_id.u == true) { + ctx->data.g_id.c = 0; + ctx->id = 0; + ctx->state = GLYPH_ID; + break; + + } else if (*pos == 'U' && + ctx->data.before_id.new_line == true) { + ctx->data.before_id.u = true; + + } else if (*pos == '\n') { + ctx->data.before_id.new_line = true; + ctx->data.before_id.u = false; + + } else { + ctx->data.before_id.new_line = false; + ctx->data.before_id.u = false; + } + break; + + case GLYPH_ID: + ok = assemble_codepoint(pos, ctx->data.g_id.c++, + &ctx->id); + if (!ok) { + LOG(LOG_ERROR, " Invalid glyph ID\n"); + return false; + } + + if (ctx->data.g_id.c == 4) { + ctx->data.before_gd.new_line = false; + ctx->data.before_gd.prev_h = false; + ctx->data.before_gd.prev_s = false; + ctx->data.before_gd.c = 0; + ctx->state = BEFORE_GLYPH_DATA; + break; + } + break; + + case BEFORE_GLYPH_DATA: + /* Skip until end of dashed line */ + if (*pos == '\n' && ctx->data.before_gd.c == 53) { + ctx->state = IN_GLYPH_DATA; + ctx->data.in_gd.e[0] = NULL; + ctx->data.in_gd.e[1] = NULL; + ctx->data.in_gd.e[2] = NULL; + ctx->data.in_gd.e[3] = NULL; + ctx->data.in_gd.line = 0; + ctx->data.in_gd.pos = 0; + ctx->data.in_gd.line_styles = 0; + ctx->data.in_gd.styles = 0; + break; + + } else if (*pos == '\n') { + ctx->data.before_gd.new_line = true; + ctx->data.before_gd.prev_h = false; + ctx->data.before_gd.prev_s = false; + ctx->data.before_gd.c = 0; + } else if (*pos == '-' && + ctx->data.before_gd.new_line == true) { + assert(ctx->data.before_gd.c == 0); + ctx->data.before_gd.new_line = false; + ctx->data.before_gd.c++; + ctx->data.before_gd.prev_h = true; + } else if (*pos == ' ' && + ctx->data.before_gd.prev_h == true) { + assert(ctx->data.before_gd.prev_s == false); + ctx->data.before_gd.c++; + ctx->data.before_gd.prev_h = false; + ctx->data.before_gd.prev_s = true; + } else if (*pos == '-' && + ctx->data.before_gd.prev_s == true) { + assert(ctx->data.before_gd.prev_h == false); + ctx->data.before_gd.c++; + ctx->data.before_gd.prev_h = true; + ctx->data.before_gd.prev_s = false; + } else { + ctx->data.before_gd.new_line = false; + ctx->data.before_gd.prev_h = false; + ctx->data.before_gd.prev_s = false; + ctx->data.before_gd.c = 0; + } + break; + + case IN_GLYPH_DATA: + ok = parse_glyph_data(ctx, *pos, d); + if (!ok) { + return false; + } + + break; + } + + pos++; + } + + for (i = 0; i < 4; i++) { + LOG(LOG_DEBUG, " %s: %i gylphs\n", labels[i], + ctx->count[i] - count[i]); + } + + return true; +} + + +bool load_font(const char *path, struct font_data **data) +{ + struct parse_context ctx; + struct font_data *d; + size_t file_len; + size_t done; + size_t len; + int count; + char *buf; + FILE *fp; + bool ok; + int i; + + *data = NULL; + + fp = fopen(path, "rb"); + if (fp == NULL) { + LOG(LOG_ERROR, "Couldn't open font data file\n"); + return false; + } + + d = calloc(sizeof(struct font_data), 1); + if (d == NULL) { + LOG(LOG_ERROR, "Couldn't allocate memory for font data\n"); + fclose(fp); + return false; + } + + /* Find filesize */ + fseek(fp, 0L, SEEK_END); + file_len = ftell(fp); + if (file_len == -1) { + LOG(LOG_ERROR, "Could not size input file\n"); + free(d); + fclose(fp); + return false; + } + fseek(fp, 0L, SEEK_SET); + LOG(LOG_DEBUG, "Input size: %zu bytes\n", file_len); + + /* Allocate buffer for data chunks */ + buf = malloc(CHUNK_SIZE); + if (buf == NULL) { + LOG(LOG_ERROR, "Couldn't allocate memory for input buffer\n"); + free(d); + fclose(fp); + return false; + } + + /* Initialise parser */ + parse_init(&ctx); + + LOG(LOG_DEBUG, "Using chunk size of %i bytes\n", CHUNK_SIZE); + + /* Parse the input file in chunks */ + for (done = 0; done < file_len; done += CHUNK_SIZE) { + LOG(LOG_INFO, "Parsing input chunk %zu\n", done / CHUNK_SIZE); + + /* Read chunk */ + len = fread(buf, 1, CHUNK_SIZE, fp); + if (file_len - done < CHUNK_SIZE && + len != file_len - done) { + LOG(LOG_WARNING, "Last chunk has suspicious size\n"); + } else if (file_len - done >= CHUNK_SIZE && + len != CHUNK_SIZE) { + LOG(LOG_ERROR, "Problem reading file\n"); + free(buf); + free(d); + fclose(fp); + return false; + } + + /* Parse chunk */ + ok = parse_chunk(&ctx, buf, len, d); + if (!ok) { + free(buf); + free(d); + fclose(fp); + return false; + } + LOG(LOG_DEBUG, "Parsed %zu bytes\n", done + len); + } + + fclose(fp); + + if (ctx.state != BEFORE_ID) { + LOG(LOG_ERROR, "Unexpected end of file\n"); + free(buf); + free(d); + return false; + } + + LOG(LOG_INFO, "Parsing complete:\n"); + count = 0; + for (i = 0; i < 4; i++) { + LOG(LOG_INFO, " %s: %i gylphs\n", labels[i], ctx.count[i]); + count += ctx.count[i]; + } + + LOG(LOG_RESULT, " Total %i gylphs " + "(of which %i unique, %i codepoints, %i duplicates)\n", + count, d->glyphs, ctx.codepoints, + count - d->glyphs - ctx.codepoints); + + free(buf); + + *data = d; + return true; +} + +static void log_usage(const char *argv0) +{ + level = LOG_INFO; + LOG(LOG_INFO, + "Usage:\n" + "\t%s [options] \n" + "\n" + "Options:\n" + "\t--help -h Display this text\n" + "\t--quiet -q Don't show warnings\n" + "\t--verbose -v Verbose output\n" + "\t--debug -d Full debug output\n", + argv0); +} + +int main(int argc, char** argv) +{ + const char *in_path = NULL; + const char *out_path = NULL; + char *header_path = NULL; + struct font_data *data; + bool ok; + int i; + int opt; + + level = LOG_RESULT; + + /* Handle program arguments */ + struct option long_options[] = { + { "help", no_argument, NULL, 'h' }, + { "quiet", no_argument, NULL, 'q' }, + { "verbose", no_argument, NULL, 'v' }, + { "debug", no_argument, NULL, 'd' }, + { "header", required_argument, NULL, 'H' }, + }; + + while ((opt = getopt_long(argc, argv, "hqvdH:", long_options, NULL)) != -1) { + switch (opt) { + case 'q': + level = LOG_WARNING; + break; + + case 'v': + level = LOG_INFO; + break; + + case 'd': + level = LOG_DEBUG; + break; + + case 'H': + header_path = strdup(optarg); + break; + + case 'h': + log_usage(argv[0]); + free(header_path); + return EXIT_SUCCESS; + + default: + log_usage(argv[0]); + free(header_path); + return EXIT_FAILURE; + } + } + + if ((argc - optind) < 2) { + log_usage(argv[0]); + free(header_path); + return EXIT_FAILURE; + } + + in_path = argv[optind]; + out_path = argv[optind + 1]; + + LOG(LOG_DEBUG, "Using input path: \"%s\"\n", in_path); + LOG(LOG_DEBUG, "Using output path: \"%s\"\n", out_path); + + ok = load_font(in_path, &data); + if (!ok) { + free_table(); + free(header_path); + return EXIT_FAILURE; + } + + ok = generate_font_source(out_path, data); + if (ok && (header_path != NULL)) { + ok = generate_font_header(header_path, data); + } + free(header_path); + free_table(); + for (i = 0; i < 4; i++) { + free(data->sections[i]); + } + free(data); + if (!ok) { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/frontends/kolibrios/fb/convert_image.c b/frontends/kolibrios/fb/convert_image.c new file mode 100644 index 000000000..de772fc29 --- /dev/null +++ b/frontends/kolibrios/fb/convert_image.c @@ -0,0 +1,304 @@ +/* + * Copyright 2009 Daniel Silverstone + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf 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 . + */ + +#include +#include +#include +#include +#include +#include + +#if PNG_LIBPNG_VER < 10209 +#define png_set_expand_gray_1_2_4_to_8(png) png_set_gray_1_2_4_to_8(png) +#endif + +static png_structp png; +static png_infop info; +static int interlace; +static size_t rowbytes; +static int raw_width, raw_height; +static int rowstride; +static unsigned char *bitmap_data; +static bool is_cursor = true; +static int raw_hot_x, raw_hot_y; + +#define WIDTH (is_cursor?raw_width-1:raw_width) +#define HEIGHT (is_cursor?raw_height-1:raw_height) + +#define HOT_X (is_cursor?raw_hot_x-1:0) +#define HOT_Y (is_cursor?raw_hot_y-1:0) + +#define REAL(v) (is_cursor?v+1:v) + +#define PPIX_AT(x,y) ((bitmap_data + (rowstride * y)) + (x * 4)) + +#define R_OFF 2 +#define G_OFF 1 +#define B_OFF 0 +#define A_OFF 3 + +#define R_AT(x,y) *(PPIX_AT(x,y) + R_OFF) +#define G_AT(x,y) *(PPIX_AT(x,y) + G_OFF) +#define B_AT(x,y) *(PPIX_AT(x,y) + B_OFF) +#define A_AT(x,y) *(PPIX_AT(x,y) + A_OFF) + + +static void +usage(void) +{ + fprintf(stderr, "usage: fb_convert_image input.png output.inc varname\n"); +} + + +static void +detect_hotspot(void) +{ + int i; + int greenpixels = 0; + + for (i = 0; i < raw_width; ++i) { + if (A_AT(i, 0) == 255) { + if (G_AT(i, 0) == 255) { + greenpixels++; + raw_hot_x = i; + } + if ((B_AT(i, 0) != 0) || (R_AT(i, 0) != 0)) { + is_cursor = false; + return; + } + } else if (A_AT(i, 0) != 0) { + is_cursor = false; + return; + } + } + if (greenpixels != 1) { + is_cursor = false; + return; + } + + for (i = 0; i < raw_height; ++i) { + if (A_AT(0, i) == 255) { + if (G_AT(0, i) == 255) { + greenpixels++; + raw_hot_y = i; + } + if ((B_AT(0, i) != 0) || (R_AT(0, i) != 0)) { + is_cursor = false; + return; + } + } else if (A_AT(0, i) != 0) { + is_cursor = false; + return; + } + } + if (greenpixels != 2) { + is_cursor = false; + return; + } + printf(" Pointer detected. Adjusted hotspot at %d, %d (0-based)\n", + raw_hot_x - 1, raw_hot_y - 1); +} + + +static void +info_callback(png_structp png, png_infop info) +{ + int bit_depth, color_type, interlace, intent; + double gamma; + png_uint_32 width, height; + + /* Read the PNG details */ + png_get_IHDR(png, info, &width, &height, &bit_depth, + &color_type, &interlace, 0, 0); + + /* Set up our transformations */ + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(png); + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_expand_gray_1_2_4_to_8(png); + if (png_get_valid(png, info, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(png); + if (bit_depth == 16) + png_set_strip_16(png); + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png); + if (!(color_type & PNG_COLOR_MASK_ALPHA)) + png_set_filler(png, 0xff, PNG_FILLER_AFTER); + /* gamma correction - we use 2.2 as our screen gamma + * this appears to be correct (at least in respect to !Browse) + * see http://www.w3.org/Graphics/PNG/all_seven.html for a test case + */ + if (png_get_sRGB(png, info, &intent)) + png_set_gamma(png, 2.2, 0.45455); + else { + if (png_get_gAMA(png, info, &gamma)) + png_set_gamma(png, 2.2, gamma); + else + png_set_gamma(png, 2.2, 0.45455); + } + + + png_read_update_info(png, info); + + rowbytes = png_get_rowbytes(png, info); + interlace = (interlace == PNG_INTERLACE_ADAM7); + raw_width = width; + raw_height = height; + + rowstride = raw_width * 4; + bitmap_data = malloc(rowstride * raw_height); +} + +static unsigned int interlace_start[8] = {0, 16, 0, 8, 0, 4, 0}; +static unsigned int interlace_step[8] = {28, 28, 12, 12, 4, 4, 0}; +static unsigned int interlace_row_start[8] = {0, 0, 4, 0, 2, 0, 1}; +static unsigned int interlace_row_step[8] = {8, 8, 8, 4, 4, 2, 2}; + +static void +row_callback(png_structp png, png_bytep new_row, + png_uint_32 row_num, int pass) +{ + unsigned long i, j; + unsigned int start, step; + unsigned char *row = bitmap_data + (rowstride * row_num); + + if (new_row == 0) + return; + + if (interlace) { + start = interlace_start[pass]; + step = interlace_step[pass]; + row_num = interlace_row_start[pass] + + interlace_row_step[pass] * row_num; + + /* Copy the data to our current row taking interlacing + * into consideration */ + row = bitmap_data + (rowstride * row_num); + for (j = 0, i = start; i < rowbytes; i += step) { + row[i++] = new_row[j++]; + row[i++] = new_row[j++]; + row[i++] = new_row[j++]; + row[i++] = new_row[j++]; + } + } else { + memcpy(row, new_row, rowbytes); + } +} + +static void +end_callback(png_structp png, png_infop info) +{ +} + + +int +main(int argc, char **argv) +{ + FILE *f; + unsigned char buffer[1024]; + int br; + int x, y, c; + + if (argc != 4) { + usage(); + return 1; + } + + printf(" CONVERT: %s (%s)\n", argv[1], argv[3]); + + png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + info = png_create_info_struct(png); + + png_set_progressive_read_fn(png, NULL, info_callback, row_callback, end_callback); + + f = fopen(argv[1], "rb"); + if (f == NULL) { + printf(" Unable to open %s\n", argv[1]); + return 1; + } + + do { + br = fread(buffer, 1, 1024, f); + if (br > 0) { + png_process_data(png, info, buffer, br); + } + } while (br > 0); + + if (br < 0) { + printf("Error reading input: %s\n", strerror(errno)); + fclose(f); + return 1; + } + + fclose(f); + + detect_hotspot(); + + f = fopen(argv[2], "w"); + if (f == NULL) { + printf(" Unable to open %s\n", argv[2]); + return 2; + } + + fprintf(f, "/* This file is auto-generated from %s\n", argv[1]); + fprintf(f, " *\n * Do not edit this file directly.\n */\n\n"); + fprintf(f, "#include \n\n"); + fprintf(f, "#include \n\n"); + fprintf(f, "#include \n\n"); + fprintf(f, "#include \n\n"); + fprintf(f, "#include \"netsurf/plot_style.h\"\n"); + fprintf(f, "#include \"framebuffer/gui.h\"\n"); + fprintf(f, "#include \"framebuffer/fbtk.h\"\n\n"); + + fprintf(f, "static uint8_t %s_pixdata[] = {\n", argv[3]); + for (y = 0; y < HEIGHT; ++y) { + unsigned char *rowptr = bitmap_data + (rowstride * y); + if (is_cursor) { + /* If it's a cursor, skip one row and one column */ + rowptr += rowstride + 4; + } + fprintf(f, "\t"); + for (x = 0; x < WIDTH; ++x) { + for (c = 0; c < 4; ++c) { + unsigned char b = *rowptr++; + fprintf(f, "0x%02x, ", b); + } + } + fprintf(f, "\n"); + } + fprintf(f, "};\n\n"); + + fprintf(f, "struct fbtk_bitmap %s = {\n", argv[3]); + fprintf(f, "\t.width\t\t= %d,\n", WIDTH); + fprintf(f, "\t.height\t\t= %d,\n", HEIGHT); + fprintf(f, "\t.hot_x\t\t= %d,\n", HOT_X); + fprintf(f, "\t.hot_y\t\t= %d,\n", HOT_Y); + fprintf(f, "\t.pixdata\t= %s_pixdata,\n", argv[3]); + + fprintf(f, "};\n\n"); + fclose(f); + + return 0; +} + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/kolibrios/fb/fb_search.c b/frontends/kolibrios/fb/fb_search.c new file mode 100644 index 000000000..19fefa8b2 --- /dev/null +++ b/frontends/kolibrios/fb/fb_search.c @@ -0,0 +1,74 @@ +/* + * Copyright 2009 Mark Benjamin + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf 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 . + */ + +#include +#include + +#include "utils/log.h" + +/* callback functions for search implementation */ +static void gui_search_set_status(bool found, void *p); +static void gui_search_set_hourglass(bool active, void *p); +static void gui_search_add_recent(const char *string, void *p); +static void gui_search_set_forward_state(bool active, void *p); +static void gui_search_set_back_state(bool active, void *p); + +/** +* Change the displayed search status. +* \param found search pattern matched in text +* \param p the pointer sent to search_verify_new() / search_create_context() +*/ +void gui_search_set_status(bool found, void *p) +{ +} + +/** +* display hourglass while searching +* \param active start/stop indicator +* \param p the pointer sent to search_verify_new() / search_create_context() +*/ +void gui_search_set_hourglass(bool active, void *p) +{ +} + +/** +* add search string to recent searches list +* \param string search pattern +* \param p the pointer sent to search_verify_new() / search_create_context() +*/ +void gui_search_add_recent(const char *string, void *p) +{ +} + +/** +* activate search forwards button in gui +* \param active activate/inactivate +* \param p the pointer sent to search_verify_new() / search_create_context() +*/ +void gui_search_set_forward_state(bool active, void *p) +{ +} + +/** +* activate search forwards button in gui +* \param active activate/inactivate +* \param p the pointer sent to search_verify_new() / search_create_context() +*/ +void gui_search_set_back_state(bool active, void *p) +{ +} diff --git a/frontends/kolibrios/fb/fbtk.h b/frontends/kolibrios/fb/fbtk.h new file mode 100644 index 000000000..3cc326cef --- /dev/null +++ b/frontends/kolibrios/fb/fbtk.h @@ -0,0 +1,618 @@ +/* + * Copyright 2008,2010 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NETSURF_FB_FBTK_H +#define NETSURF_FB_FBTK_H + +#include "netsurf/types.h" + +#ifdef FBTK_LOGGING +#define FBTK_LOG(x) LOG(x) +#else +#define FBTK_LOG(x) +#endif + +#define FB_SCROLL_COLOUR 0xFFAAAAAA +#define FB_FRAME_COLOUR 0xFFDDDDDD +#define FB_COLOUR_BLACK 0xFF000000 +#define FB_COLOUR_WHITE 0xFFFFFFFF + +#define FBTK_WIDGET_PADDING 30 /**< percentage of widget size used for padding */ +#define FBTK_DPI 90 /**< screen DPI */ + +typedef struct fbtk_widget_s fbtk_widget_t; + +/** Widget Callback type */ +typedef enum fbtk_callback_type { + FBTK_CBT_START = 0, + FBTK_CBT_SCROLLX, + FBTK_CBT_SCROLLY, + FBTK_CBT_CLICK, + FBTK_CBT_INPUT, + FBTK_CBT_POINTERMOVE, + FBTK_CBT_POINTERLEAVE, + FBTK_CBT_POINTERENTER, + FBTK_CBT_REDRAW, + FBTK_CBT_DESTROY, + FBTK_CBT_USER, + FBTK_CBT_STRIP_FOCUS, + FBTK_CBT_END, +} fbtk_callback_type; + +/** widget callback information */ +typedef struct fbtk_callback_info { + enum fbtk_callback_type type; + void *context; + nsfb_event_t *event; + int x; + int y; + char *text; + fbtk_widget_t *widget; +} fbtk_callback_info; + +/** framebuffer toolkit bitmaps */ +struct fbtk_bitmap { + int width; + int height; + uint8_t *pixdata; + bool opaque; + + /* The following two are only used for cursors */ + int hot_x; + int hot_y; +}; + +/** Key modifier status */ +typedef enum fbtk_modifier_type { + FBTK_MOD_CLEAR = 0, + FBTK_MOD_LSHIFT = (1 << 0), + FBTK_MOD_RSHIFT = (1 << 1), + FBTK_MOD_LCTRL = (1 << 2), + FBTK_MOD_RCTRL = (1 << 3) +} fbtk_modifier_type; + +typedef int (*fbtk_callback)(fbtk_widget_t *widget, fbtk_callback_info *cbi); + +/* enter pressed on writable icon */ +typedef int (*fbtk_enter_t)(void *pw, char *text); + + +/************************ Core ****************************/ + + +/** + * Initialise widget toolkit. + * + * Initialises widget toolkit against a framebuffer. + * + * @param fb The underlying framebuffer. + * @return The root widget handle. + */ +fbtk_widget_t *fbtk_init(nsfb_t *fb); + +/** + * Retrieve the framebuffer library handle from toolkit widget. + * + * @param widget A fbtk widget. + * @return The underlying framebuffer. + */ +nsfb_t *fbtk_get_nsfb(fbtk_widget_t *widget); + +/** Perform any pending widget redraws. + * + * @param widget A fbtk widget. + */ +int fbtk_redraw(fbtk_widget_t *widget); + +/** Determine if there are any redraws pending for a widget. + * + * Mainly used by clients on the root widget to determine if they need + * to call ::fbtk_redraw + * + * @param widget to check. + */ +bool fbtk_get_redraw_pending(fbtk_widget_t *widget); + +/** clip a bounding box to a widgets area. + */ +bool fbtk_clip_to_widget(fbtk_widget_t *widget, bbox_t * restrict box); + +/** clip one bounding box to another. + */ +bool fbtk_clip_rect(const bbox_t * restrict clip, bbox_t * restrict box); + +/***************** Callback processing ********************/ + +/** Helper function to allow simple calling of callbacks with parameters. + * + * @param widget The fbtk widget to post the callback to. + * @param cbt The type of callback to post + * @param ... Parameters appropriate for the callback type. + */ +int fbtk_post_callback(fbtk_widget_t *widget, fbtk_callback_type cbt, ...); + +/** Set a callback handler. + * + * Set a callback handler and the pointer to pass for a widget. + * + * @param widget The widget to set the handler for. + * @param cbt The type of callback to set. + * @param cb The callback. + * @param pw The private pointer to pass when calling the callback. + * @return The previous callback handler for the type or NULL. + */ +fbtk_callback fbtk_set_handler(fbtk_widget_t *widget, fbtk_callback_type cbt, fbtk_callback cb, void *pw); + +/** Get a callback handler. + */ +fbtk_callback fbtk_get_handler(fbtk_widget_t *widget, fbtk_callback_type cbt); + + +/******************* Event processing **********************/ + +/** Retrive events from the framebuffer input. + * + * Obtain events from the framebuffer input system with a + * timeout. Some events may be used by the toolkit instead of being + * returned to the caller. + * + * @param root An fbtk widget. + * @param event an event structure to update. + * @param timeout The number of miliseconds to wait for an event. 0 + * means do not wait and -1 means wait foreevr. + * @return wether \a event has been updated. + */ +bool fbtk_event(fbtk_widget_t *root, nsfb_event_t *event, int timeout); + +/** Insert mouse button press into toolkit. + */ +void fbtk_click(fbtk_widget_t *widget, nsfb_event_t *event); + +/** Insert input into toolkit. + */ +void fbtk_input(fbtk_widget_t *widget, nsfb_event_t *event); + +/** Move pointer. + * + * Move the pointer cursor to a given location. + * + * @param widget any tookit widget. + * @param x movement in horizontal plane. + * @param y movement in vertical plane. + * @param relative Wheter the /a x and /a y should be considered relative to + * current pointer position. + */ +void fbtk_warp_pointer(fbtk_widget_t *widget, int x, int y, bool relative); + +/** Toggle pointer grab. + * + * Toggles the movement grab for a widget. + * + * @param widget The widget trying to grab the movement. + * @return true if the grab was ok, false if the grab failed (already grabbed). + */ +bool fbtk_tgrab_pointer(fbtk_widget_t *widget); + +/** Convert a framebuffer keycode to ucs4. + * + * Character mapping between keycode with modifier state and ucs-4. + */ +int fbtk_keycode_to_ucs4(int code, fbtk_modifier_type mods); + + +/******************* Widget Information **********************/ + +/** Obtain the widget at a point on screen. + * + * @param widget any tookit widget. + * @param x location in horizontal plane. + * @param y location in vertical plane. + * @return widget or NULL. + */ +fbtk_widget_t *fbtk_get_widget_at(fbtk_widget_t *widget, int x, int y); + +/** Get a widget's absolute horizontal screen co-ordinate. + * + * @param widget The widget to inspect. + * @return The absolute screen co-ordinate. + */ +int fbtk_get_absx(fbtk_widget_t *widget); + +/** Get a widget's absolute vertical screen co-ordinate. + * + * @param widget The widget to inspect. + * @return The absolute screen co-ordinate. + */ +int fbtk_get_absy(fbtk_widget_t *widget); + +/** + * Get a widget's width. + * + * @param widget The widget to inspect. + * @return The widget width. + */ +int fbtk_get_width(fbtk_widget_t *widget); + +/** + * Get a widget's height. + * + * @param widget The widget to inspect. + * @return The widget height. + */ +int fbtk_get_height(fbtk_widget_t *widget); + +/** + * Get a widget's bounding box in absolute screen co-ordinates. + * + * @param widget The widget to inspect. + * @param bbox The bounding box structure to update. + * @return If the \a bbox parameter has been updated. + */ +bool fbtk_get_bbox(fbtk_widget_t *widget, struct nsfb_bbox_s *bbox); + +/** + * Get a widget caret pos, if it owns caret. + * + * @param widget The widget to inspect. + * @param x If widget has caret, returns x-coord of caret within widget + * @param y If widget has caret, returns y-coord of caret within widget + * @param height If widget has caret, returns caret height + * @return true iff widget has caret + */ +bool fbtk_get_caret(fbtk_widget_t *widget, int *x, int *y, int *height); + + +/******************* Widget Manipulation **********************/ + +/** + * Change the widget's position and size. (Doesn't redraw) + * + */ +bool fbtk_set_pos_and_size(fbtk_widget_t *widget, int x, int y, int width, int height); + +/** + * Set caret owner and position + * + * @param widget widget to give caret to, or ensure caret is released from + * @param set true: caret to be set for widget, false: caret to be released + * @param x x-coordinate of caret top + * @param y y-coordinate of caret top + * @param height height of caret + * @param remove_caret callback when caret is removed. + */ +void fbtk_set_caret(fbtk_widget_t *widget, bool set, int x, int y, int height, + void (*remove_caret)(fbtk_widget_t *widget)); + +/** + * Map a widget and request it is redrawn. + */ +int fbtk_set_mapping(fbtk_widget_t *widget, bool mapped); + +/** + * Set the z order of a widget. + */ +int fbtk_set_zorder(fbtk_widget_t *widget, int z); + +/** + * Indicate a widget should be redrawn. + */ +void fbtk_request_redraw(fbtk_widget_t *widget); + +/** + * Destroy a widget and all its descendants. + * + * Removes a widget from the hierachy and frees it and all its children. + * + * @param widget The widget to destroy. + * @return 0 on success or -1 on error. + */ +int fbtk_destroy_widget(fbtk_widget_t *widget); + + + +/********************************* Widgets *********************************/ + + +/** + * Create a window widget. + * + * @param parent The parent window or the root widget for a top level window. + * @param x The x location relative to the parent window. + * @param y the y location relative to the parent window. + * @param width The width of the window. 0 indicates parents width should be + * used. Negative value indicates parents width less the value + * should be used. The width is limited to lie within the parent + * window. + * @param height The height of the window limited in a similar way to the + * /a width. + * @param bg The background colour. + * @return new window widget handle or NULL on error. + */ +fbtk_widget_t *fbtk_create_window(fbtk_widget_t *parent, int x, int y, int width, int height, colour bg); + + + +/** + * Create a filled rectangle + * + * Create a widget which is a filled rectangle, usually used for backgrounds. + * + * @param window The window to add the filled area widget to. + * @param x X coordinate of widget. + * @param y Y coordinate of widget. + * @param width Width of the widget + * @param height Height of the widget + * @param c widget colour + * @return new widget handle or NULL on error. + */ +fbtk_widget_t * +fbtk_create_fill(fbtk_widget_t *window, int x, int y, int width, int height, colour c); + + +/** + * Create a horizontal scroll widget + * + * Create a horizontal scroll widget. + * + * @param window The window to add the filled area widget to. + * @param x X coordinate of widget. + * @param y Y coordinate of widget. + * @param width Width of the widget + * @param height Height of the widget + * @param bg background colour + * @param fg foreground colour + * @param callback Called on scroll + * @param context context passed to callback. + * @return new widget handle or NULL on error. + */ +fbtk_widget_t * +fbtk_create_hscroll(fbtk_widget_t *window, int x, int y, int width, int height, colour fg, colour bg, fbtk_callback callback, void *context); + +/** + * Create a vertical scroll widget + * + * Create a vertical scroll widget. + * + * @param window The window to add the filled area widget to. + * @param x X coordinate of widget. + * @param y Y coordinate of widget. + * @param width Width of the widget + * @param height Height of the widget + * @param bg background colour + * @param fg foreground colour + * @param callback Called on scroll + * @param context context passed to callback. + * @return new widget handle or NULL on error. + */ +fbtk_widget_t * +fbtk_create_vscroll(fbtk_widget_t *window, int x, int y, int width, int height, colour fg, colour bg, fbtk_callback callback, void *context); + +/** + * Set scoll widget parameters + * + * @param widget The widget to set the parameters for. + * @param min The minimum range value. + * @param max The maximum range value. + * @param thumb The size of the slider. + * @param page The amout to scroll for a page. + * @return true if the scroll parameter was set else false. + */ +bool fbtk_set_scroll_parameters(fbtk_widget_t *widget, int min, int max, int thumb, int page); + +/** + * set scroll widget position. + * + * @param widget The widget to set the position on. + * @param pos The position to set + * @return true if the scroll parameter was set else false. + */ +bool fbtk_set_scroll_position(fbtk_widget_t *widget, int pos); + + +/** + * Move and/or resize a horizontal scroll widget + * + * @param scrollh the horizontal scroll widget + * @param x new x pos + * @param y new y pos + * @param width new width + * @param height new height + */ +void fbtk_reposition_hscroll(fbtk_widget_t *scrollh, + int x, int y, int width, int height); + +/** + * Move and/or resize a vertical scroll widget + * + * @param scrollv the vertical scroll widget + * @param x new x pos + * @param y new y pos + * @param width new width + * @param height new height + */ +void fbtk_reposition_vscroll(fbtk_widget_t *scrollv, + int x, int y, int width, int height); + + +/** + * Create a user widget. + * + * Create a widget which is to be handled entirely by the calling application. + * + * @param window The window to add the user widget to. + * @param x X coordinate of widget. + * @param y Y coordinate of widget. + * @param width Width of the widget + * @param height Height of the widget + * @param pw The private pointer which can be read using ::fbtk_get_userpw + * @return new widget handle or NULL on error. + */ +fbtk_widget_t *fbtk_create_user(fbtk_widget_t *window, int x, int y, int width, int height, void *pw); + + +/** + * Get the user context from a widget + * + * @param widget The widget to get the context from. + * @return The context or NULL. + */ +void *fbtk_get_userpw(fbtk_widget_t *widget); + + +/** + * Create a bitmap widget. + * + * Create a widget which shows a bitmap. + * + * @param window The window to add the bitmap widget to. + * @param x X coordinate of widget. + * @param y Y coordinate of widget. + * @param width Width of the widget + * @param height Height of the widget + * @param c background colour + * @param image The bitmap to put in the widget + * @return new widget handle or NULL on error. + */ +fbtk_widget_t *fbtk_create_bitmap(fbtk_widget_t *window, int x, int y, int width, int height, colour c, struct fbtk_bitmap *image); + + +/** + * Change the bitmap in a widget. + * + * @param widget The widget to get the context from. + * @param image The bitmap to put in the widget + */ +void fbtk_set_bitmap(fbtk_widget_t *widget, struct fbtk_bitmap *image); + + +/** + * Create a button widget with an image. + * + * Helper function which creates a bitmap widget and associate a handler for + * when it is clicked. + * + * @param window The window to add the button widget to. + * @param x X coordinate of widget. + * @param y Y coordinate of widget. + * @param width Width of the widget + * @param height Height of the widget + * @param c background colour + * @param image The bitmap to put in the widget + * @param click The callback upon a click + * @param pw The context tp pass to the callback + * @return new widget handle or NULL on error. + */ +fbtk_widget_t *fbtk_create_button(fbtk_widget_t *window, int x, int y, int width, int height, colour c, struct fbtk_bitmap *image, fbtk_callback click, void *pw); + + +/** + * Create a text widget. + * + * @param window The window to add the text widget to. + * @param x X coordinate of widget. + * @param y Y coordinate of widget. + * @param width Width of the widget + * @param height Height of the widget + * @param bg background colour + * @param fg foreground colour + * @param outline widget will have a border. + * @return new widget handle or NULL on error. + */ +fbtk_widget_t *fbtk_create_text(fbtk_widget_t *window, int x, int y, int width, int height, colour bg, colour fg, bool outline); + + +/** + * Create a button with text. + * + * @param window The window to add the text widget to. + * @param x X coordinate of widget. + * @param y Y coordinate of widget. + * @param width Width of the widget + * @param height Height of the widget + * @param bg background colour + * @param fg foreground colour + * @param click The callback upon a click + * @param pw The context tp pass to the callback + * @return new widget handle or NULL on error. + */ +fbtk_widget_t *fbtk_create_text_button(fbtk_widget_t *window, int x, int y, int width, int height, colour bg, colour fg, fbtk_callback click, void *pw); + + +/** + * Create a writable text widget. + * + * Helper function which creates a text widget and configures an input handler + * to create a writable text field. This call is equivalent to calling + * ::fbtk_create_text followed by ::fbtk_writable_text + * + * @param window The window to add the text widget to. + * @param x X coordinate of widget. + * @param y Y coordinate of widget. + * @param width Width of the widget + * @param height Height of the widget + * @param bg background colour + * @param fg foreground colour + * @param outline widget will have a border. + * @param enter Callback when enter is pressed in widget. + * @param pw Context pointer passed to entry callback. + * @return new widget handle or NULL on error. + */ +fbtk_widget_t *fbtk_create_writable_text(fbtk_widget_t *window, int x, int y, int width, int height, colour bg, colour fg, bool outline, fbtk_enter_t enter, void *pw); + + +/** + * Alter a text widget to be writable. + * + * @param widget Text widget. + * @param enter The routine to call when enter is pressed. + * @param pw The context to pass to the enter callback routine. + */ +void fbtk_writable_text(fbtk_widget_t *widget, fbtk_enter_t enter, void *pw); + + +/** + * Change the text of a text widget. + * + * @param widget Text widget. + * @param text The new UTF-8 text to put in the widget. + */ +void fbtk_set_text(fbtk_widget_t *widget, const char *text); + + +/** + * Give widget input focus. + * + * @param widget Widget to be given input focus. + */ +void fbtk_set_focus(fbtk_widget_t *widget); + + +/** + * enable the on screen keyboard for input + * + * @param widget Widget to be given input focus. + */ +void fbtk_enable_oskb(fbtk_widget_t *widget); + + +/** + * show the osk. + */ +void map_osk(void); + +#endif diff --git a/frontends/kolibrios/fb/fbtk/bitmap.c b/frontends/kolibrios/fb/fbtk/bitmap.c new file mode 100644 index 000000000..759b626d9 --- /dev/null +++ b/frontends/kolibrios/fb/fbtk/bitmap.c @@ -0,0 +1,136 @@ +/* + * Copyright 2010 Vincent Sanders + * + * Framebuffer windowing toolkit bitmaped image widget + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf 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 . + */ + +#include +#include + +#include +#include + +#include "netsurf/browser_window.h" + +#include "framebuffer/gui.h" +#include "framebuffer/fbtk.h" +#include "framebuffer/image_data.h" + +#include "widget.h" + +static int +fb_redraw_bitmap(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + nsfb_bbox_t bbox; + nsfb_bbox_t rect; + nsfb_t *nsfb; + + nsfb = fbtk_get_nsfb(widget); + + fbtk_get_bbox(widget, &bbox); + + rect = bbox; + + nsfb_claim(nsfb, &bbox); + + /* clear background */ + if ((widget->bg & 0xFF000000) != 0) { + /* transparent polygon filling isnt working so fake it */ + nsfb_plot_rectangle_fill(nsfb, &bbox, widget->bg); + } + + /* plot the image */ + nsfb_plot_bitmap(nsfb, + &rect, + (nsfb_colour_t *)widget->u.bitmap.bitmap->pixdata, + widget->u.bitmap.bitmap->width, + widget->u.bitmap.bitmap->height, + widget->u.bitmap.bitmap->width, + !widget->u.bitmap.bitmap->opaque); + + nsfb_update(nsfb, &bbox); + + return 0; +} + +/* exported function documented in fbtk.h */ +void +fbtk_set_bitmap(fbtk_widget_t *widget, struct fbtk_bitmap *image) +{ + if ((widget == NULL) || (widget->type != FB_WIDGET_TYPE_BITMAP)) + return; + + widget->u.bitmap.bitmap = image; + + fbtk_request_redraw(widget); +} + +/* exported function documented in fbtk.h */ +fbtk_widget_t * +fbtk_create_bitmap(fbtk_widget_t *parent, + int x, + int y, + int width, + int height, + colour c, + struct fbtk_bitmap *image) +{ + fbtk_widget_t *neww; + + neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_BITMAP, x, y, width, height); + + neww->bg = c; + neww->mapped = true; + neww->u.bitmap.bitmap = image; + + fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_bitmap, NULL); + + return neww; +} + +/* exported function documented in fbtk.h */ +fbtk_widget_t * +fbtk_create_button(fbtk_widget_t *parent, + int x, + int y, + int width, + int height, + colour c, + struct fbtk_bitmap *image, + fbtk_callback click, + void *pw) +{ + fbtk_widget_t *neww; + + neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_BITMAP, x, y, width, height); + + neww->bg = c; + neww->mapped = true; + neww->u.bitmap.bitmap = image; + + fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_bitmap, NULL); + fbtk_set_handler(neww, FBTK_CBT_CLICK, click, pw); + fbtk_set_handler(neww, FBTK_CBT_POINTERENTER, fbtk_set_ptr, &hand_image); + + return neww; +} + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/kolibrios/fb/fbtk/event.c b/frontends/kolibrios/fb/fbtk/event.c new file mode 100644 index 000000000..a48e63809 --- /dev/null +++ b/frontends/kolibrios/fb/fbtk/event.c @@ -0,0 +1,349 @@ +/* + * Copyright 2010 Vincent Sanders + * + * Framebuffer windowing toolkit event processing. + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf 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 . + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "utils/utils.h" +#include "utils/log.h" +#include "netsurf/browser_window.h" +#include "netsurf/keypress.h" + +#include "framebuffer/gui.h" +#include "framebuffer/fbtk.h" +#include "framebuffer/image_data.h" + +#include "widget.h" + +/* exported function documented in fbtk.h */ +void +fbtk_input(fbtk_widget_t *root, nsfb_event_t *event) +{ + fbtk_widget_t *input; + + root = fbtk_get_root_widget(root); + + /* obtain widget with input focus */ + input = root->u.root.input; + if (input == NULL) { + LOG("No widget has input focus."); + return; /* no widget with input */ + } + + fbtk_post_callback(input, FBTK_CBT_INPUT, event); +} + +/* exported function documented in fbtk.h */ +void +fbtk_click(fbtk_widget_t *widget, nsfb_event_t *event) +{ + fbtk_widget_t *root; + fbtk_widget_t *clicked; + nsfb_bbox_t cloc; + int x, y; + + /* ensure we have the root widget */ + root = fbtk_get_root_widget(widget); + + nsfb_cursor_loc_get(root->u.root.fb, &cloc); + + clicked = fbtk_get_widget_at(root, cloc.x0, cloc.y0); + + if (clicked == NULL) + return; + + if (fbtk_get_handler(clicked, FBTK_CBT_INPUT) != NULL) { + fbtk_set_focus(clicked); + } + + x = fbtk_get_absx(clicked); + y = fbtk_get_absy(clicked); + + LOG("clicked %p at %d,%d", clicked, x, y); + + /* post the click */ + fbtk_post_callback(clicked, FBTK_CBT_CLICK, event, cloc.x0 - x, cloc.y0 - y); +} + +/* exported function documented in fbtk.h */ +bool +fbtk_tgrab_pointer(fbtk_widget_t *widget) +{ + fbtk_widget_t *root; + + /* ensure we have the root widget */ + root = fbtk_get_root_widget(widget); + + if (root->u.root.grabbed == widget) { + /* release pointer grab */ + root->u.root.grabbed = NULL; + return true; + } else if (root->u.root.grabbed == NULL) { + /* set pointer grab */ + root->u.root.grabbed = widget; + return true; + } + /* pointer was already grabbed */ + return false; +} + +/* exported function documented in fbtk.h */ +void +fbtk_warp_pointer(fbtk_widget_t *widget, int x, int y, bool relative) +{ + fbtk_widget_t *root; + fbtk_widget_t *moved; + nsfb_bbox_t cloc; + + /* ensure we have the root widget */ + root = fbtk_get_root_widget(widget); + + if (relative) { + nsfb_cursor_loc_get(root->u.root.fb, &cloc); + cloc.x0 += x; + cloc.y0 += y; + } else { + cloc.x0 = x; + cloc.y0 = y; + } + + /* ensure cursor location lies within the root widget */ + if (cloc.x0 < root->x) + cloc.x0 = root->x; + if (cloc.x0 >= (root->x + root->width)) + cloc.x0 = (root->x + root->width) - 1; + if (cloc.y0 < root->y) + cloc.y0 = root->y; + if (cloc.y0 >= (root->y + root->height)) + cloc.y0 = (root->y + root->height) - 1; + + if (root->u.root.grabbed == NULL) { + /* update the pointer cursor */ + nsfb_cursor_loc_set(root->u.root.fb, &cloc); + + moved = fbtk_get_widget_at(root, cloc.x0, cloc.y0); + + x = fbtk_get_absx(moved); + y = fbtk_get_absy(moved); + + /* post enter and leaving messages */ + if (moved != root->u.root.prev) { + fbtk_post_callback(root->u.root.prev, FBTK_CBT_POINTERLEAVE); + root->u.root.prev = moved; + fbtk_post_callback(root->u.root.prev, FBTK_CBT_POINTERENTER); + } + } else { + /* pointer movement has been grabbed by a widget */ + moved = root->u.root.grabbed; + + /* ensure pointer remains within widget boundary */ + x = fbtk_get_absx(moved); + y = fbtk_get_absy(moved); + + if (cloc.x0 < x) + cloc.x0 = x; + if (cloc.y0 < y) + cloc.y0 = y; + if (cloc.x0 > (x + moved->width)) + cloc.x0 = (x + moved->width); + if (cloc.y0 > (y + moved->height)) + cloc.y0 = (y + moved->height); + + /* update the pointer cursor */ + nsfb_cursor_loc_set(root->u.root.fb, &cloc); + } + + /* post the movement */ + fbtk_post_callback(moved, FBTK_CBT_POINTERMOVE, cloc.x0 - x, cloc.y0 - y); + +} + +/* exported function documented in fbtk.h */ +bool +fbtk_event(fbtk_widget_t *root, nsfb_event_t *event, int timeout) +{ + nsfb_bbox_t cloc; + bool unused = false; /* is the event available */ + bool move_pointer = false; /* whether pointer move events occured */ + + /* ensure we have the root widget */ + root = fbtk_get_root_widget(root); + + do { + if (nsfb_event(root->u.root.fb, event, timeout) == false) { + if (move_pointer) + fbtk_warp_pointer(root, cloc.x0, cloc.y0, + false); + return false; + } + + if (move_pointer && event->type != NSFB_EVENT_MOVE_RELATIVE && + event->type != NSFB_EVENT_MOVE_ABSOLUTE) { + /* Flush the movements */ + fbtk_warp_pointer(root, cloc.x0, cloc.y0, false); + + } else if (!move_pointer && + event->type == NSFB_EVENT_MOVE_RELATIVE) { + /* Get current pointer coords */ + nsfb_cursor_loc_get(root->u.root.fb, &cloc); + } + + switch (event->type) { + case NSFB_EVENT_KEY_DOWN: + case NSFB_EVENT_KEY_UP: + if ((event->value.keycode >= NSFB_KEY_MOUSE_1) && + (event->value.keycode <= NSFB_KEY_MOUSE_5)) { + fbtk_click(root, event); + } else { + fbtk_input(root, event); + } + break; + + case NSFB_EVENT_CONTROL: + unused = true; + break; + + case NSFB_EVENT_MOVE_RELATIVE: + /* Consecutive move events are consolidated into a + * single pointer warp */ + move_pointer = true; + cloc.x0 += event->value.vector.x; + cloc.y0 += event->value.vector.y; + timeout = 0; + break; + + case NSFB_EVENT_MOVE_ABSOLUTE: + /* Consecutive move events are consolidated into a + * single pointer warp */ + move_pointer = true; + cloc.x0 = event->value.vector.x; + cloc.y0 = event->value.vector.y; + timeout = 0; + break; + + case NSFB_EVENT_RESIZE: + /* Try to resize framebuffer */ + gui_resize(root, + event->value.resize.w, + event->value.resize.h); + break; + + default: + break; + } + } while (event->type == NSFB_EVENT_MOVE_RELATIVE || + event->type == NSFB_EVENT_MOVE_ABSOLUTE); + return unused; +} + +static int keymap[] = { + /* 0 1 2 3 4 5 6 7 8 9 */ + -1, -1, -1, -1, -1, -1, -1, -1, 8, 9, /* 0 - 9 */ + -1, -1, -1, 13, -1, -1, -1, -1, -1, -1, /* 10 - 19 */ + -1, -1, -1, -1, -1, -1, -1, 27, -1, -1, /* 20 - 29 */ + -1, -1, ' ', '!', '"', '#', '$', -1, '&','\'', /* 30 - 39 */ + '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', /* 40 - 49 */ + '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', /* 50 - 59 */ + '<', '=', '>', '?', '@', -1, -1, -1, -1, -1, /* 60 - 69 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 70 - 79 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 - 89 */ + -1, '[','\\', ']', '~', '_', '`', 'a', 'b', 'c', /* 90 - 99 */ + 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', /* 100 - 109 */ + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', /* 110 - 119 */ + 'x', 'y', 'z', -1, -1, -1, -1, -1, -1, -1, /* 120 - 129 */ +}; + +static int sh_keymap[] = { + /* 0 1 2 3 4 5 6 7 8 9 */ + -1, -1, -1, -1, -1, -1, -1, -1, 8, 9, /* 0 - 9 */ + -1, -1, -1, 13, -1, -1, -1, -1, -1, -1, /* 10 - 19 */ + -1, -1, -1, -1, -1, -1, -1, 27, -1, -1, /* 20 - 29 */ + -1, -1, ' ', '!', '"', '~', '$', -1, '&', '@', /* 30 - 39 */ + '(', ')', '*', '+', '<', '_', '>', '?', ')', '!', /* 40 - 49 */ + '"', 243, '$', '%', '^', '&', '*', '(', ';', ':', /* 50 - 59 */ + '<', '+', '>', '?', '@', -1, -1, -1, -1, -1, /* 60 - 69 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 70 - 79 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 - 89 */ + -1, '{', '|', '}', '~', '_', 254, 'A', 'B', 'C', /* 90 - 99 */ + 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', /* 100 - 109 */ + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', /* 110 - 119 */ + 'X', 'Y', 'Z', -1, -1, -1, -1, -1, -1, -1, /* 120 - 129 */ +}; + + +/* exported function documented in fbtk.h */ +int +fbtk_keycode_to_ucs4(int code, fbtk_modifier_type mods) +{ + int ucs4 = -1; + + if (mods & FBTK_MOD_LSHIFT || mods & FBTK_MOD_RSHIFT) { + if ((code >= 0) && (code < (int) NOF_ELEMENTS(sh_keymap))) + ucs4 = sh_keymap[code]; + + } else if (mods == FBTK_MOD_CLEAR) { + if ((code >= 0) && (code < (int) NOF_ELEMENTS(keymap))) + ucs4 = keymap[code]; + + } else if (mods & FBTK_MOD_LCTRL || mods & FBTK_MOD_RCTRL) { + switch (code) { + case NSFB_KEY_a: + ucs4 = NS_KEY_SELECT_ALL; + break; + + case NSFB_KEY_c: + ucs4 = NS_KEY_COPY_SELECTION; + break; + + case NSFB_KEY_u: + ucs4 = NS_KEY_DELETE_LINE; + break; + + case NSFB_KEY_v: + ucs4 = NS_KEY_PASTE; + break; + + case NSFB_KEY_x: + ucs4 = NS_KEY_CUT_SELECTION; + break; + + case NSFB_KEY_z: + ucs4 = NS_KEY_CLEAR_SELECTION; + break; + default: + break; + } + } + return ucs4; +} + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/kolibrios/fb/fbtk/fbtk.c b/frontends/kolibrios/fb/fbtk/fbtk.c new file mode 100644 index 000000000..c63a6d8c9 --- /dev/null +++ b/frontends/kolibrios/fb/fbtk/fbtk.c @@ -0,0 +1,830 @@ +/* + * Copyright 2008,2010 Vincent Sanders + * + * Framebuffer windowing toolkit core. + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "utils/utils.h" +#include "utils/log.h" +#include "netsurf/browser_window.h" +#include "netsurf/plotters.h" + +#include "framebuffer/gui.h" +#include "framebuffer/fbtk.h" +#include "framebuffer/image_data.h" + +#include "widget.h" + +#ifdef FBTK_LOGGING + +/* tree dump debug, also example of depth first tree walk */ +static void +dump_tk_tree(fbtk_widget_t *widget) +{ + widget = fbtk_get_root_widget(widget); + int indent = 0; + + while (widget != NULL) { + LOG("%*s%p", indent, "", widget); + if (widget->first_child != NULL) { + widget = widget->first_child; + indent += 6; + } else if (widget->next != NULL) { + widget = widget->next; + } else { + while ((widget->parent != NULL) && + (widget->parent->next == NULL)) { + widget = widget->parent; + indent -= 6; + } + if (widget->parent != NULL) { + indent -= 6; + widget = widget->parent->next; + } else { + widget = NULL; + } + } + } +} + +#endif + +/* exported function documented in fbtk.h */ +void +fbtk_request_redraw(fbtk_widget_t *widget) +{ + fbtk_widget_t *cwidget; + fbtk_widget_t *pwidget; + + assert(widget != NULL); + + /* if widget not mapped do not try to redraw it */ + pwidget = widget; + while (pwidget != NULL) { + if (pwidget->mapped == false) + return; + pwidget = pwidget->parent; + } + + widget->redraw.needed = true; + widget->redraw.x = 0; + widget->redraw.y = 0; + widget->redraw.width = widget->width; + widget->redraw.height = widget->height; + +#ifdef FBTK_LOGGING + LOG("redrawing %p %d,%d %d,%d", widget, widget->redraw.x, widget->redraw.y, widget->redraw.width, widget->redraw.height); +#endif + + cwidget = widget->last_child; + while (cwidget != NULL) { + fbtk_request_redraw(cwidget); + cwidget = cwidget->prev; + } + + while (widget->parent != NULL) { + widget = widget->parent; + widget->redraw.child = true; + } +} + + + +/* exported function documented in fbtk.h */ +int +fbtk_set_mapping(fbtk_widget_t *widget, bool map) +{ + LOG("setting mapping on %p to %d", widget, map); + widget->mapped = map; + if (map) { + fbtk_request_redraw(widget); + } else { + fbtk_request_redraw(widget->parent); + } + return 0; +} + +/** swap the widget given with the next sibling. + * + * Swap a sibling widget with the next deepest in the hierachy + */ +static void +swap_siblings(fbtk_widget_t *lw) +{ + fbtk_widget_t *rw = lw->next; /* the widget to swap lw with */ + fbtk_widget_t *before; + fbtk_widget_t *after; + + assert(rw != NULL); + + LOG("Swapping %p with %p", lw, rw); + before = lw->prev; + after = rw->next; + + if (before == NULL) { + /* left widget is currently the first child */ + lw->parent->first_child = rw; + } else { + before->next = rw; + } + rw->prev = before; + rw->next = lw; + + if (after == NULL) { + /* right widget is currently the last child */ + rw->parent->last_child = lw; + } else { + after->prev = lw; + } + lw->next = after; + lw->prev = rw; +} + + + +/* exported function documented in fbtk.h */ +int +fbtk_set_zorder(fbtk_widget_t *widget, int z) +{ + while (z != 0) { + if (z < 0) { + if (widget->prev == NULL) + break; /* cannot go any shallower */ + + /* swap with previous entry */ + swap_siblings(widget->prev); + + z++; + } else { + if (widget->next == NULL) + break; /* cannot go any deeper */ + + /* swap with subsequent entry */ + swap_siblings(widget); + + z--; + } + } + + return z; +} + + +/* exported function documented in fbtk.h */ +bool +fbtk_set_pos_and_size(fbtk_widget_t *widget, + int x, int y, + int width, int height) +{ + if (widget->parent != NULL) { + fbtk_widget_t *parent = widget->parent; + + /* make new window fit inside parent */ + if (width == 0) { + width = parent->width - x; + } else if (width < 0) { + width = parent->width + width - x; + } + if ((width + x) > parent->width) { + width = parent->width - x; + } + + if (height == 0) { + height = parent->height - y; + } else if (height < 0) { + height = parent->height + height - y; + } + if ((height + y) > parent->height) { + height = parent->height - y; + } + } + + if ((widget->x != x) || + (widget->y != y) || + (widget->width != width) || + (widget->height != height)) { + widget->x = x; + widget->y = y; + widget->width = width; + widget->height = height; + return true; + } + return false; +} + + +/* exported function docuemnted in fbtk.h */ +void +fbtk_set_caret(fbtk_widget_t *widget, bool set, + int x, int y, int height, + void (*remove_caret)(fbtk_widget_t *widget)) +{ + fbtk_widget_t *root; + + assert(widget != NULL); + root = fbtk_get_root_widget(widget); + + if (root->u.root.caret.owner != NULL && + root->u.root.caret.remove_cb != NULL) + root->u.root.caret.remove_cb(widget); + + if (set) { + assert(remove_caret != NULL); + + root->u.root.caret.owner = widget; + root->u.root.caret.x = x; + root->u.root.caret.y = y; + root->u.root.caret.height = height; + root->u.root.caret.remove_cb = remove_caret; + + } else { + root->u.root.caret.owner = NULL; + root->u.root.caret.remove_cb = NULL; + } +} + +/* exported function documented in fbtk.h */ +int +fbtk_destroy_widget(fbtk_widget_t *widget) +{ + fbtk_widget_t *parent; + int ret = 0; + + ret = fbtk_post_callback(widget, FBTK_CBT_DESTROY); + + while (widget->first_child != NULL) { + fbtk_destroy_widget(widget->first_child); + } + + parent = widget->parent; + if (parent != NULL) { + + /* unlink from siblings */ + if (widget->prev != NULL) { + widget->prev->next = widget->next; + } else { + /* must be the first widget, unlink from parent */ + parent->first_child = widget->next; + } + if (widget->next != NULL) { + widget->next->prev = widget->prev; + } else { + /* must be the last widget, unlink from parent */ + parent->last_child = widget->prev; + } + + free(widget); + } + + return ret; +} + +/* region coverage flags. */ +enum { + POINT_LEFTOF_REGION = 1, + POINT_RIGHTOF_REGION = 2, + POINT_ABOVE_REGION = 4, + POINT_BELOW_REGION = 8, +}; + +/* Computes where a point lies in respect to an area. */ +#define REGION(x,y,cx1,cx2,cy1,cy2) \ + (( (y) > (cy2) ? POINT_BELOW_REGION : 0) | \ + ( (y) < (cy1) ? POINT_ABOVE_REGION : 0) | \ + ( (x) > (cx2) ? POINT_RIGHTOF_REGION : 0) | \ + ( (x) < (cx1) ? POINT_LEFTOF_REGION : 0) ) + +/* swap two integers */ +#define SWAP(a, b) do { int t; t=(a); (a)=(b); (b)=t; } while(0) + +/* exported function documented in fbtk.h */ +bool +fbtk_clip_rect(const bbox_t * restrict clip, bbox_t * restrict box) +{ + uint8_t region1; + uint8_t region2; + + /* ensure co-ordinates are in ascending order */ + if (box->x1 < box->x0) + SWAP(box->x0, box->x1); + if (box->y1 < box->y0) + SWAP(box->y0, box->y1); + + region1 = REGION(box->x0, box->y0, clip->x0, clip->x1 - 1, clip->y0, clip->y1 - 1); + region2 = REGION(box->x1, box->y1, clip->x0, clip->x1 - 1, clip->y0, clip->y1 - 1); + + /* area lies entirely outside the clipping rectangle */ + if ((region1 | region2) && (region1 & region2)) + return false; + + if (box->x0 < clip->x0) + box->x0 = clip->x0; + if (box->x0 > clip->x1) + box->x0 = clip->x1; + + if (box->x1 < clip->x0) + box->x1 = clip->x0; + if (box->x1 > clip->x1) + box->x1 = clip->x1; + + if (box->y0 < clip->y0) + box->y0 = clip->y0; + if (box->y0 > clip->y1) + box->y0 = clip->y1; + + if (box->y1 < clip->y0) + box->y1 = clip->y0; + if (box->y1 > clip->y1) + box->y1 = clip->y1; + + return true; +} + +/* exported function documented in fbtk.h */ +bool +fbtk_clip_to_widget(fbtk_widget_t *widget, bbox_t * restrict box) +{ + bbox_t wbox; + wbox.x0 = 0; + wbox.y0 = 0; + wbox.x1 = widget->width; + wbox.y1 = widget->height; + return fbtk_clip_rect(&wbox, box); +} + + + +/* internally exported function documented in widget.h */ +int +fbtk_set_ptr(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + fbtk_widget_t *root = fbtk_get_root_widget(widget); + struct fbtk_bitmap *bm = cbi->context; + + nsfb_cursor_set(root->u.root.fb, + (nsfb_colour_t *)bm->pixdata, + bm->width, + bm->height, + bm->width, + bm->hot_x, + bm->hot_y); + + return 0; +} + + + +/* internally exported function documented in widget.h */ +fbtk_widget_t * +fbtk_get_root_widget(fbtk_widget_t *widget) +{ + while (widget->parent != NULL) + widget = widget->parent; + + /* check root widget was found */ + if (widget->type != FB_WIDGET_TYPE_ROOT) { + LOG("Widget with null parent that is not the root widget!"); + return NULL; + } + + return widget; +} + + +/* exported function documented in fbtk.h */ +int +fbtk_get_absx(fbtk_widget_t *widget) +{ + int x = widget->x; + + while (widget->parent != NULL) { + widget = widget->parent; + x += widget->x; + } + + return x; +} + +/* exported function documented in fbtk.h */ +int +fbtk_get_absy(fbtk_widget_t *widget) +{ + int y = widget->y; + + while (widget->parent != NULL) { + widget = widget->parent; + y += widget->y; + } + + return y; +} + +/* exported function documented in fbtk.h */ +int +fbtk_get_height(fbtk_widget_t *widget) +{ + return widget->height; +} + +/* exported function documented in fbtk.h */ +int +fbtk_get_width(fbtk_widget_t *widget) +{ + return widget->width; +} + +/* exported function documented in fbtk.h */ +bool +fbtk_get_bbox(fbtk_widget_t *widget, nsfb_bbox_t *bbox) +{ + bbox->x0 = widget->x; + bbox->y0 = widget->y; + bbox->x1 = widget->x + widget->width; + bbox->y1 = widget->y + widget->height; + + widget = widget->parent; + while (widget != NULL) { + bbox->x0 += widget->x; + bbox->y0 += widget->y; + bbox->x1 += widget->x; + bbox->y1 += widget->y; + widget = widget->parent; + } + + return true; +} + +bool +fbtk_get_caret(fbtk_widget_t *widget, int *x, int *y, int *height) +{ + fbtk_widget_t *root = fbtk_get_root_widget(widget); + + if (root->u.root.caret.owner == widget) { + *x = root->u.root.caret.x; + *y = root->u.root.caret.y; + *height = root->u.root.caret.height; + + return true; + + } else { + *x = 0; + *y = 0; + *height = 0; + + return false; + } +} + +/* exported function documented in fbtk.h */ +fbtk_widget_t * +fbtk_get_widget_at(fbtk_widget_t *nwid, int x, int y) +{ + fbtk_widget_t *widget = NULL; /* found widget */ + + /* require the root widget to start */ + nwid = fbtk_get_root_widget(nwid); + + while (nwid != NULL) { + if ((nwid->mapped) && + (x >= nwid->x) && + (y >= nwid->y) && + (x < (nwid->x + nwid->width)) && + (y < (nwid->y + nwid->height))) { + widget = nwid; + x -= nwid->x; + y -= nwid->y; + nwid = nwid->first_child; + } else { + nwid = nwid->next; + } + } + + return widget; +} + + + + +/* internally exported function documented in widget.h */ +fbtk_widget_t * +fbtk_widget_new(fbtk_widget_t *parent, + enum fbtk_widgettype_e type, + int x, + int y, + int width, + int height) +{ + fbtk_widget_t *neww; /* new widget */ + + if (parent == NULL) + return NULL; + + neww = calloc(1, sizeof(fbtk_widget_t)); + if (neww == NULL) + return NULL; + +#ifdef FBTK_LOGGING + LOG("creating %p %d,%d %d,%d", neww, x, y, width, height); +#endif + + /* make new window fit inside parent */ + if (width == 0) { + width = parent->width - x; + } else if (width < 0) { + width = parent->width + width - x; + } + if ((width + x) > parent->width) { + width = parent->width - x; + } + + if (height == 0) { + height = parent->height - y; + } else if (height < 0) { + height = parent->height + height - y; + } + if ((height + y) > parent->height) { + height = parent->height - y; + } + +#ifdef FBTK_LOGGING + LOG("using %p %d,%d %d,%d", neww, x, y, width, height); +#endif + /* set values */ + neww->type = type; + neww->x = x; + neww->y = y; + neww->width = width; + neww->height = height; + + /* insert into widget heiarchy */ + + neww->parent = parent; + + if (parent->first_child == NULL) { + /* no child widgets yet */ + parent->last_child = neww; + } else { + /* add new widget to front of sibling chain */ + neww->next = parent->first_child; + neww->next->prev = neww; + } + parent->first_child = neww; + + return neww; +} + +/* exported function documented in fbtk.h */ +bool +fbtk_get_redraw_pending(fbtk_widget_t *widget) +{ + fbtk_widget_t *root; + + /* ensure we have the root widget */ + root = fbtk_get_root_widget(widget); + + return root->redraw.needed | root->redraw.child; +} + +/** Perform a depth-first tree-walk, calling the redraw callback of the widgets in turn. + * + * This function makes no decisions of its own and simply walks the + * widget tree depth first calling widgets redraw callbacks if flagged + * to do so. + * The tree search is optimised with a flag to indicate wether the + * children of a node should be considered. + */ +static int +do_redraw(nsfb_t *nsfb, fbtk_widget_t *widget) +{ + nsfb_bbox_t plot_ctx; + fbtk_widget_t *cwidget; /* child widget */ + + /* check if the widget requires redrawing */ + if (widget->redraw.needed == true) { + plot_ctx.x0 = fbtk_get_absx(widget) + widget->redraw.x; + plot_ctx.y0 = fbtk_get_absy(widget) + widget->redraw.y; + plot_ctx.x1 = plot_ctx.x0 + widget->redraw.width; + plot_ctx.y1 = plot_ctx.y0 + widget->redraw.height; + +#ifdef FBTK_LOGGING + LOG("clipping %p %d,%d %d,%d", widget, plot_ctx.x0, plot_ctx.y0, plot_ctx.x1, plot_ctx.y1); +#endif + if (nsfb_plot_set_clip(nsfb, &plot_ctx) == true) { + fbtk_post_callback(widget, FBTK_CBT_REDRAW); + } + widget->redraw.needed = false; + } + + /* walk the widgets children if child flag is set */ + if (widget->redraw.child) { + cwidget = widget->last_child; + while (cwidget != NULL) { + do_redraw(nsfb, cwidget); + cwidget = cwidget->prev; + } + widget->redraw.child = false; + } + + return 1; +} + +/* exported function documented in fbtk.h */ +int +fbtk_redraw(fbtk_widget_t *widget) +{ + fbtk_widget_t *root; + + /* ensure we have the root widget */ + root = fbtk_get_root_widget(widget); + + return do_redraw(root->u.root.fb, root); +} + +/* exported function documented in fbtk.h */ +fbtk_callback +fbtk_get_handler(fbtk_widget_t *widget, fbtk_callback_type cbt) +{ + if ((cbt <= FBTK_CBT_START) || (cbt >= FBTK_CBT_END)) { + /* type out of range, no way to report error so return NULL */ + return NULL; + } + + return widget->callback[cbt]; +} + +/* exported function documented in fbtk.h */ +fbtk_callback +fbtk_set_handler(fbtk_widget_t *widget, + fbtk_callback_type cbt, + fbtk_callback cb, + void *context) +{ + fbtk_callback prevcb; + + if ((cbt <= FBTK_CBT_START) || (cbt >= FBTK_CBT_END)) { + /* type out of range, no way to report error so return NULL */ + return NULL; + } + + prevcb = widget->callback[cbt]; + + widget->callback[cbt] = cb; + widget->callback_context[cbt] = context; + + return prevcb; +} + +/* exported function docuemnted in fbtk.h */ +int +fbtk_post_callback(fbtk_widget_t *widget, fbtk_callback_type cbt, ...) +{ + fbtk_callback_info cbi; + int ret = 0; + va_list ap; + + if (widget == NULL) + return -1; + /* if the widget is not mapped do not attempt to post any + * events to it + */ + if (widget->mapped == false) + return ret; + + if (widget->callback[cbt] != NULL) { + cbi.type = cbt; + cbi.context = widget->callback_context[cbt]; + + va_start(ap, cbt); + + switch (cbt) { + case FBTK_CBT_SCROLLX: + cbi.x = va_arg(ap,int); + break; + + case FBTK_CBT_SCROLLY: + cbi.y = va_arg(ap,int); + break; + + case FBTK_CBT_CLICK: + cbi.event = va_arg(ap, void *); + cbi.x = va_arg(ap, int); + cbi.y = va_arg(ap, int); + break; + + case FBTK_CBT_INPUT: + cbi.event = va_arg(ap, void *); + break; + + case FBTK_CBT_POINTERMOVE: + cbi.x = va_arg(ap, int); + cbi.y = va_arg(ap, int); + break; + + case FBTK_CBT_REDRAW: + break; + + case FBTK_CBT_USER: + break; + + case FBTK_CBT_STRIP_FOCUS: + break; + + default: + break; + } + va_end(ap); + + ret = (widget->callback[cbt])(widget, &cbi); + } + + return ret; +} + +/* exported function docuemnted in fbtk.h */ +void +fbtk_set_focus(fbtk_widget_t *widget) +{ + fbtk_widget_t *root; + + /* ensure we have the root widget */ + root = fbtk_get_root_widget(widget); + + if (root->u.root.input != NULL && + root->u.root.input != widget) { + /* inform previous holder of focus that it's being stripped + * of focus */ + fbtk_post_callback(root->u.root.input, FBTK_CBT_STRIP_FOCUS); + } + + root->u.root.input = widget; +} + + + +/* exported function docuemnted in fbtk.h */ +nsfb_t * +fbtk_get_nsfb(fbtk_widget_t *widget) +{ + fbtk_widget_t *root; + + /* ensure we have the root widget */ + root = fbtk_get_root_widget(widget); + + return root->u.root.fb; +} + +/* exported function docuemnted in fbtk.h */ +fbtk_widget_t * +fbtk_init(nsfb_t *fb) +{ + fbtk_widget_t *root; + + /* create and configure root widget */ + root = calloc(1, sizeof(fbtk_widget_t)); + if (root == NULL) + return NULL; + + root->type = FB_WIDGET_TYPE_ROOT; + root->u.root.fb = fb; + root->u.root.caret.owner = NULL; + + nsfb_get_geometry(fb, &root->width, &root->height, NULL); + + root->mapped = true; + + return root; +} + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/kolibrios/fb/fbtk/fill.c b/frontends/kolibrios/fb/fbtk/fill.c new file mode 100644 index 000000000..9377933cc --- /dev/null +++ b/frontends/kolibrios/fb/fbtk/fill.c @@ -0,0 +1,81 @@ +/* + * Copyright 2010 Vincent Sanders + * + * Framebuffer windowing toolkit filled area widget + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf 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 . + */ + +#include +#include + +#include +#include + +#include "netsurf/browser_window.h" + +#include "framebuffer/gui.h" +#include "framebuffer/fbtk.h" + +#include "widget.h" + +static int +fb_redraw_fill(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + nsfb_bbox_t bbox; + nsfb_t *nsfb; + + nsfb = fbtk_get_nsfb(widget); + + fbtk_get_bbox(widget, &bbox); + + nsfb_claim(nsfb, &bbox); + + /* clear background */ + if ((widget->bg & 0xFF000000) != 0) { + /* transparent polygon filling isnt working so fake it */ + nsfb_plot_rectangle_fill(nsfb, &bbox, widget->bg); + } + + nsfb_update(nsfb, &bbox); + + return 0; +} + +/* exported function documented in fbtk.h */ +fbtk_widget_t * +fbtk_create_fill(fbtk_widget_t *parent, + int x, + int y, + int width, + int height, + colour c) +{ + fbtk_widget_t *neww; + + neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_FILL, x, y, width, height); + neww->bg = c; + neww->mapped = true; + + fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_fill, NULL); + + return neww; +} + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/kolibrios/fb/fbtk/osk.c b/frontends/kolibrios/fb/fbtk/osk.c new file mode 100644 index 000000000..628fe9abd --- /dev/null +++ b/frontends/kolibrios/fb/fbtk/osk.c @@ -0,0 +1,199 @@ +/* + * Copyright 2010 Vincent Sanders + * + * Framebuffer windowing toolkit on screen keyboard. + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf 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 . + */ + +#include +#include + +#include +#include +#include +#include + +#include "utils/log.h" +#include "netsurf/browser_window.h" + +#include "framebuffer/gui.h" +#include "framebuffer/fbtk.h" +#include "framebuffer/image_data.h" + +#include "widget.h" + +struct kbd_button_s { + int x; + int y; + int w; + int h; + const char *t; + enum nsfb_key_code_e keycode; +}; + +#define KEYCOUNT 58 + +static struct kbd_button_s kbdbase[KEYCOUNT] = { + { 0, 0, 20, 15, "`", NSFB_KEY_BACKQUOTE}, + { 20, 0, 20, 15, "1", NSFB_KEY_1}, + { 40, 0, 20, 15, "2", NSFB_KEY_2}, + { 60, 0, 20, 15, "3", NSFB_KEY_3}, + { 80, 0, 20, 15, "4", NSFB_KEY_4}, + { 100, 0, 20, 15, "5", NSFB_KEY_5}, + { 120, 0, 20, 15, "6", NSFB_KEY_6}, + { 140, 0, 20, 15, "7", NSFB_KEY_7}, + { 160, 0, 20, 15, "8", NSFB_KEY_8}, + { 180, 0, 20, 15, "9", NSFB_KEY_9}, + { 200, 0, 20, 15, "0", NSFB_KEY_0}, + { 220, 0, 20, 15, "-", NSFB_KEY_MINUS}, + { 240, 0, 20, 15, "=", NSFB_KEY_EQUALS}, + { 260, 0, 40, 15, "\xe2\x8c\xab", NSFB_KEY_BACKSPACE}, + { 0, 15, 30, 15, "\xe2\x86\xb9", NSFB_KEY_TAB}, + { 30, 15, 20, 15, "q", NSFB_KEY_q}, + { 50, 15, 20, 15, "w", NSFB_KEY_w}, + { 70, 15, 20, 15, "e", NSFB_KEY_e}, + { 90, 15, 20, 15, "r", NSFB_KEY_r}, + { 110, 15, 20, 15, "t", NSFB_KEY_t}, + { 130, 15, 20, 15, "y", NSFB_KEY_y}, + { 150, 15, 20, 15, "u", NSFB_KEY_u}, + { 170, 15, 20, 15, "i", NSFB_KEY_i}, + { 190, 15, 20, 15, "o", NSFB_KEY_o}, + { 210, 15, 20, 15, "p", NSFB_KEY_p}, + { 230, 15, 20, 15, "[", NSFB_KEY_LEFTBRACKET}, + { 250, 15, 20, 15, "]", NSFB_KEY_RIGHTBRACKET}, + { 275, 15, 25, 30, "\xe2\x8f\x8e", NSFB_KEY_RETURN}, + { 35, 30, 20, 15, "a", NSFB_KEY_a}, + { 55, 30, 20, 15, "s", NSFB_KEY_s}, + { 75, 30, 20, 15, "d", NSFB_KEY_d}, + { 95, 30, 20, 15, "f", NSFB_KEY_f}, + { 115, 30, 20, 15, "g", NSFB_KEY_g}, + { 135, 30, 20, 15, "h", NSFB_KEY_h}, + { 155, 30, 20, 15, "j", NSFB_KEY_j}, + { 175, 30, 20, 15, "k", NSFB_KEY_k}, + { 195, 30, 20, 15, "l", NSFB_KEY_l}, + { 215, 30, 20, 15, ";", NSFB_KEY_SEMICOLON}, + { 235, 30, 20, 15, "'", NSFB_KEY_l}, + { 255, 30, 20, 15, "#", NSFB_KEY_HASH}, + { 0, 45, 25, 15, "\xe2\x87\xa7", NSFB_KEY_LSHIFT}, + { 25, 45, 20, 15, "\\", NSFB_KEY_SLASH}, + { 45, 45, 20, 15, "z", NSFB_KEY_z}, + { 65, 45, 20, 15, "x", NSFB_KEY_x}, + { 85, 45, 20, 15, "c", NSFB_KEY_c}, + { 105, 45, 20, 15, "v", NSFB_KEY_v}, + { 125, 45, 20, 15, "b", NSFB_KEY_b}, + { 145, 45, 20, 15, "n", NSFB_KEY_n}, + { 165, 45, 20, 15, "m", NSFB_KEY_m}, + { 185, 45, 20, 15, ",", NSFB_KEY_COMMA}, + { 205, 45, 20, 15, ".", NSFB_KEY_PERIOD}, + { 225, 45, 20, 15, "/", NSFB_KEY_BACKSLASH}, + { 245, 45, 55, 15, "\xe2\x87\xa7", NSFB_KEY_RSHIFT}, + { 40, 67, 185, 15, "", NSFB_KEY_SPACE}, + { 250, 60, 20, 15, "\xe2\x96\xb2", NSFB_KEY_UP}, + { 230, 67, 20, 15, "\xe2\x97\x80", NSFB_KEY_LEFT}, + { 270, 67, 20, 15, "\xe2\x96\xb6", NSFB_KEY_RIGHT}, + { 250, 75, 20, 15, "\xe2\x96\xbc", NSFB_KEY_DOWN}, +}; + +static fbtk_widget_t *osk; + +static int +osk_close(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + if (cbi->event->type != NSFB_EVENT_KEY_UP) + return 0; + + fbtk_set_mapping(osk, false); + + return 0; +} + +static int +osk_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + nsfb_event_t event; + struct kbd_button_s *kbd_button = cbi->context; + + event.type = cbi->event->type; + event.value.keycode = kbd_button->keycode; + fbtk_input(widget, &event); + + return 0; +} + +/* exported function documented in fbtk.h */ +void +fbtk_enable_oskb(fbtk_widget_t *fbtk) +{ + fbtk_widget_t *widget; + int kloop; + int maxx = 0; + int maxy = 0; + int ww; + int wh; + fbtk_widget_t *root = fbtk_get_root_widget(fbtk); + int furniture_width = 18; + + for (kloop=0; kloop < KEYCOUNT; kloop++) { + if ((kbdbase[kloop].x + kbdbase[kloop].w) > maxx) + maxx=kbdbase[kloop].x + kbdbase[kloop].w; + if ((kbdbase[kloop].y + kbdbase[kloop].h) > maxy) + maxy=kbdbase[kloop].y + kbdbase[kloop].h; + } + + ww = fbtk_get_width(root); + + /* scale window height apropriately */ + wh = (maxy * ww) / maxx; + + osk = fbtk_create_window(root, 0, fbtk_get_height(root) - wh, 0, wh, 0xff202020); + + for (kloop=0; kloop < KEYCOUNT; kloop++) { + widget = fbtk_create_text_button(osk, + (kbdbase[kloop].x * ww) / maxx, + (kbdbase[kloop].y * ww) / maxx, + (kbdbase[kloop].w * ww) / maxx, + (kbdbase[kloop].h *ww) / maxx, + FB_FRAME_COLOUR, + FB_COLOUR_BLACK, + osk_click, + &kbdbase[kloop]); + fbtk_set_text(widget, kbdbase[kloop].t); + } + + widget = fbtk_create_button(osk, + fbtk_get_width(osk) - furniture_width, + fbtk_get_height(osk) - furniture_width, + furniture_width, + furniture_width, + FB_FRAME_COLOUR, + &osk_image, + osk_close, + NULL); +} + +/* exported function documented in fbtk.h */ +void +map_osk(void) +{ + fbtk_set_zorder(osk, INT_MIN); + fbtk_set_mapping(osk, true); +} + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/kolibrios/fb/fbtk/scroll.c b/frontends/kolibrios/fb/fbtk/scroll.c new file mode 100644 index 000000000..cc98fb2dd --- /dev/null +++ b/frontends/kolibrios/fb/fbtk/scroll.c @@ -0,0 +1,589 @@ +/* + * Copyright 2010 Vincent Sanders + * + * Framebuffer windowing toolkit scrollbar widgets + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf 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 . + */ + +#include + +#include + +#include +#include +#include +#include + +#include "utils/log.h" +#include "netsurf/browser_window.h" + +#include "framebuffer/gui.h" +#include "framebuffer/fbtk.h" +#include "framebuffer/image_data.h" + +#include "widget.h" + +/* Vertical scroll widget */ + +static int +vscroll_redraw(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + int vscroll; + int vpos; + + nsfb_bbox_t bbox; + nsfb_bbox_t rect; + fbtk_widget_t *root = fbtk_get_root_widget(widget); + + fbtk_get_bbox(widget, &bbox); + + nsfb_claim(root->u.root.fb, &bbox); + + rect = bbox; + + /* background */ + nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg); + + /* scroll well */ + rect.x0 = bbox.x0 + 2; + rect.y0 = bbox.y0 + 1; + rect.x1 = bbox.x1 - 3; + rect.y1 = bbox.y1 - 2; + + nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->fg); + nsfb_plot_rectangle(root->u.root.fb, &rect, 1, 0xFF999999, false, false); + + /* scroll bar */ + if ((widget->u.scroll.maximum - widget->u.scroll.minimum) > 0) { + vscroll = ((widget->height - 4) * widget->u.scroll.thumb) / + (widget->u.scroll.maximum - widget->u.scroll.minimum) ; + vpos = ((widget->height - 4) * widget->u.scroll.position) / + (widget->u.scroll.maximum - widget->u.scroll.minimum) ; + } else { + vscroll = (widget->height - 4); + vpos = 0; + } + + rect.x0 = bbox.x0 + 5; + rect.y0 = bbox.y0 + 3 + vpos; + rect.x1 = bbox.x0 + widget->width - 5; + rect.y1 = bbox.y0 + vscroll + vpos; + + nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg); + + nsfb_update(root->u.root.fb, &bbox); + + return 0; +} + +static int +vscroll_drag(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + int newpos; + fbtk_widget_t *scrollw = cbi->context; + + newpos = ((widget->u.scroll.drag_position + + (cbi->y - widget->u.scroll.drag)) * + (widget->u.scroll.maximum - widget->u.scroll.minimum)) / + (widget->height - 4); + + if (newpos < scrollw->u.scroll.minimum) + newpos = scrollw->u.scroll.minimum; + + if (newpos > (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb )) + newpos = (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb); + + if (newpos == scrollw->u.scroll.position) + return 0; + + return fbtk_post_callback(widget, FBTK_CBT_SCROLLY, newpos); +} + +static int +vscrollu_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + int newpos; + fbtk_widget_t *scrollw = cbi->context; + + if (cbi->event->type != NSFB_EVENT_KEY_DOWN) + return 0; + + newpos = scrollw->u.scroll.position - scrollw->u.scroll.page; + if (newpos < scrollw->u.scroll.minimum) + newpos = scrollw->u.scroll.minimum; + + if (newpos == scrollw->u.scroll.position) + return 0; + + return fbtk_post_callback(scrollw, FBTK_CBT_SCROLLY, newpos); +} + +static int +vscrolld_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + int newpos; + fbtk_widget_t *scrollw = cbi->context; + + if (cbi->event->type != NSFB_EVENT_KEY_DOWN) + return 0; + + newpos = scrollw->u.scroll.position + scrollw->u.scroll.page; + if (newpos > (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb )) + newpos = (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb); + + if (newpos == scrollw->u.scroll.position) + return 0; + + return fbtk_post_callback(scrollw, FBTK_CBT_SCROLLY, newpos); +} + +static int +vscrollarea_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + int vscroll; + int vpos; + int newpos; + int ret = 0; + + if (cbi->event->type != NSFB_EVENT_KEY_DOWN) { + /* end all drags, just in case */ + if (fbtk_set_handler(widget, FBTK_CBT_POINTERMOVE, NULL, NULL) != NULL) + fbtk_tgrab_pointer(widget); + return 0; + } + + switch (cbi->event->value.keycode) { + + case NSFB_KEY_MOUSE_4: + /* scroll up */ + newpos = widget->u.scroll.position - widget->u.scroll.page; + if (newpos < widget->u.scroll.minimum) + newpos = widget->u.scroll.minimum; + ret = fbtk_post_callback(cbi->context, FBTK_CBT_SCROLLY, newpos); + break; + + case NSFB_KEY_MOUSE_5: + /* scroll down */ + newpos = widget->u.scroll.position + widget->u.scroll.page; + if (newpos > widget->u.scroll.maximum) + newpos = widget->u.scroll.maximum; + ret = fbtk_post_callback(cbi->context, FBTK_CBT_SCROLLY, newpos); + break; + + default: + + if ((widget->u.scroll.maximum - widget->u.scroll.minimum) > 0) { + vscroll = ((widget->height - 4) * widget->u.scroll.thumb) / + (widget->u.scroll.maximum - widget->u.scroll.minimum) ; + vpos = ((widget->height - 4) * widget->u.scroll.position) / + (widget->u.scroll.maximum - widget->u.scroll.minimum) ; + } else { + vscroll = (widget->height - 4); + vpos = 0; + } + + if (cbi->y < vpos) { + /* above bar */ + newpos = widget->u.scroll.position - widget->u.scroll.thumb; + if (newpos < widget->u.scroll.minimum) + newpos = widget->u.scroll.minimum; + ret = fbtk_post_callback(cbi->context, FBTK_CBT_SCROLLY, newpos); + } else if (cbi->y > (vpos + vscroll)) { + /* below bar */ + newpos = widget->u.scroll.position + widget->u.scroll.thumb; + if (newpos > widget->u.scroll.maximum) + newpos = widget->u.scroll.maximum; + ret = fbtk_post_callback(cbi->context, FBTK_CBT_SCROLLY, newpos); + } else { + /* on bar - start drag */ + widget->u.scroll.drag = cbi->y; + widget->u.scroll.drag_position = vpos; + fbtk_set_handler(widget, FBTK_CBT_POINTERMOVE, vscroll_drag, widget); + fbtk_tgrab_pointer(widget); + } + } + return ret; +} + + +/* exported function documented in fbtk.h */ +fbtk_widget_t * +fbtk_create_vscroll(fbtk_widget_t *parent, + int x, + int y, + int width, + int height, + colour fg, + colour bg, + fbtk_callback callback, + void *context) +{ + fbtk_widget_t *neww; + + neww = fbtk_widget_new(parent, + FB_WIDGET_TYPE_VSCROLL, + x, + y + scrollu.height, + width, + height - scrollu.height - scrolld.height); + + neww->fg = fg; + neww->bg = bg; + neww->mapped = true; + + fbtk_set_handler(neww, FBTK_CBT_REDRAW, vscroll_redraw, NULL); + + fbtk_set_handler(neww, FBTK_CBT_CLICK, vscrollarea_click, neww); + + fbtk_set_handler(neww, FBTK_CBT_SCROLLY, callback, context); + + neww->u.scroll.btnul = fbtk_create_button(parent, + x, + y, + width, + scrollu.height, + fg, + &scrollu, + vscrollu_click, + neww); + + neww->u.scroll.btndr = fbtk_create_button(parent, + x, + y + height - scrolld.height, + width, + scrolld.height, + fg, + &scrolld, + vscrolld_click, + neww); + + + return neww; +} + + +/* exported function documented in fbtk.h */ +void +fbtk_reposition_vscroll(fbtk_widget_t *vscroll, + int x, + int y, + int width, + int height) +{ + assert(vscroll->type == FB_WIDGET_TYPE_VSCROLL); + + fbtk_set_pos_and_size(vscroll, x, y + scrollu.height, + width, height - scrollu.height - scrolld.height); + fbtk_set_pos_and_size(vscroll->u.scroll.btnul, + x, y, width, scrollu.height); + fbtk_set_pos_and_size(vscroll->u.scroll.btndr, + x, y + height - scrolld.height, + width, scrolld.height); +} + +/* Horizontal scroll widget */ + +static int +hscroll_redraw(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + int hscroll; + int hpos; + nsfb_bbox_t bbox; + nsfb_bbox_t rect; + fbtk_widget_t *root = fbtk_get_root_widget(widget); + + fbtk_get_bbox(widget, &bbox); + + nsfb_claim(root->u.root.fb, &bbox); + + rect = bbox; + + /* background */ + nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg); + + /* scroll well */ + rect.x0 = bbox.x0 + 1; + rect.y0 = bbox.y0 + 2; + rect.x1 = bbox.x1 - 2; + rect.y1 = bbox.y1 - 3; + nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->fg); + + /* scroll well outline */ + nsfb_plot_rectangle(root->u.root.fb, &rect, 1, 0xFF999999, false, false); + + if ((widget->u.scroll.maximum - widget->u.scroll.minimum) > 0) { + hscroll = ((widget->width - 4) * widget->u.scroll.thumb) / + (widget->u.scroll.maximum - widget->u.scroll.minimum) ; + hpos = ((widget->width - 4) * widget->u.scroll.position) / + (widget->u.scroll.maximum - widget->u.scroll.minimum) ; + } else { + hscroll = (widget->width - 4); + hpos = 0; + } + + LOG("hscroll %d", hscroll); + + rect.x0 = bbox.x0 + 3 + hpos; + rect.y0 = bbox.y0 + 5; + rect.x1 = bbox.x0 + hscroll + hpos; + rect.y1 = bbox.y0 + widget->height - 5; + + nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg); + + nsfb_update(root->u.root.fb, &bbox); + + return 0; +} + +static int +hscrolll_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + int newpos; + fbtk_widget_t *scrollw = cbi->context; + + if (cbi->event->type != NSFB_EVENT_KEY_DOWN) + return 0; + + newpos = scrollw->u.scroll.position - scrollw->u.scroll.page; + if (newpos < scrollw->u.scroll.minimum) + newpos = scrollw->u.scroll.minimum; + + if (newpos == scrollw->u.scroll.position) { + LOG("horiz scroll was the same %d", newpos); + return 0; + } + + return fbtk_post_callback(scrollw, FBTK_CBT_SCROLLX, newpos); +} + +static int +hscrollr_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + int newpos; + fbtk_widget_t *scrollw = cbi->context; + + if (cbi->event->type != NSFB_EVENT_KEY_DOWN) + return 0; + + newpos = scrollw->u.scroll.position + scrollw->u.scroll.page; + if (newpos > (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb )) + newpos = (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb); + + if (newpos == scrollw->u.scroll.position) + return 0; + + return fbtk_post_callback(scrollw, FBTK_CBT_SCROLLX, newpos); +} + +static int +hscroll_drag(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + int newpos; + fbtk_widget_t *scrollw = cbi->context; + + newpos = ((widget->u.scroll.drag_position + + (cbi->x - widget->u.scroll.drag)) * + (widget->u.scroll.maximum - widget->u.scroll.minimum)) / + (widget->width - 4); + + if (newpos < scrollw->u.scroll.minimum) + newpos = scrollw->u.scroll.minimum; + + if (newpos > (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb )) + newpos = (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb); + + if (newpos == scrollw->u.scroll.position) + return 0; + + return fbtk_post_callback(widget, FBTK_CBT_SCROLLX, newpos); +} + +static int +hscrollarea_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + int hscroll; + int hpos; + int newpos; + int ret = 0; + + if (cbi->event->type != NSFB_EVENT_KEY_DOWN) { + /* end all drags, just in case */ + if (fbtk_set_handler(widget, FBTK_CBT_POINTERMOVE, NULL, NULL) != NULL) + fbtk_tgrab_pointer(widget); + return 0; + } + + if ((widget->u.scroll.maximum - widget->u.scroll.minimum) > 0) { + hscroll = ((widget->width - 4) * widget->u.scroll.thumb) / + (widget->u.scroll.maximum - widget->u.scroll.minimum) ; + hpos = ((widget->width - 4) * widget->u.scroll.position) / + (widget->u.scroll.maximum - widget->u.scroll.minimum) ; + } else { + hscroll = (widget->width - 4); + hpos = 0; + } + + if (cbi->x < hpos) { + /* left of bar */ + newpos = widget->u.scroll.position - widget->u.scroll.page; + if (newpos < widget->u.scroll.minimum) + newpos = widget->u.scroll.minimum; + ret = fbtk_post_callback(cbi->context, FBTK_CBT_SCROLLX, newpos); + } else if (cbi->x > (hpos + hscroll)) { + /* right of bar */ + newpos = widget->u.scroll.position + widget->u.scroll.page; + if (newpos > widget->u.scroll.maximum) + newpos = widget->u.scroll.maximum; + ret = fbtk_post_callback(cbi->context, FBTK_CBT_SCROLLX, newpos); + } else { + /* on bar - start drag */ + widget->u.scroll.drag = cbi->x; + widget->u.scroll.drag_position = hpos; + fbtk_set_handler(widget, FBTK_CBT_POINTERMOVE, hscroll_drag, widget); + fbtk_tgrab_pointer(widget); + } + return ret; +} + +/* exported function documented in fbtk.h */ +fbtk_widget_t * +fbtk_create_hscroll(fbtk_widget_t *parent, + int x, + int y, + int width, + int height, + colour fg, + colour bg, + fbtk_callback callback, + void *context) +{ + fbtk_widget_t *neww; + + neww = fbtk_widget_new(parent, + FB_WIDGET_TYPE_HSCROLL, + x + scrolll.width, + y, + width - scrolll.width - scrollr.width, + height); + + neww->fg = fg; + neww->bg = bg; + neww->mapped = true; + + fbtk_set_handler(neww, FBTK_CBT_REDRAW, hscroll_redraw, NULL); + fbtk_set_handler(neww, FBTK_CBT_CLICK, hscrollarea_click, neww); + fbtk_set_handler(neww, FBTK_CBT_SCROLLX, callback, context); + + neww->u.scroll.btnul = fbtk_create_button(parent, + x, + y, + scrolll.width, + height, + fg, + &scrolll, + hscrolll_click, + neww); + + neww->u.scroll.btndr = fbtk_create_button(parent, + x + width - scrollr.width, + y, + scrollr.width, + height, + fg, + &scrollr, + hscrollr_click, + neww); + + return neww; +} + +/* exported function documented in fbtk.h */ +void +fbtk_reposition_hscroll(fbtk_widget_t *scrollh, + int x, + int y, + int width, + int height) +{ + assert(scrollh->type == FB_WIDGET_TYPE_HSCROLL); + + fbtk_set_pos_and_size(scrollh, x + scrolll.width, y, + width - scrolll.width - scrollr.width, height); + fbtk_set_pos_and_size(scrollh->u.scroll.btnul, + x, y, scrolll.width, height); + fbtk_set_pos_and_size(scrollh->u.scroll.btndr, + x + width - scrollr.width, y, + scrollr.width, height); +} + + +/* exported function documented in fbtk.h */ +bool +fbtk_set_scroll_parameters(fbtk_widget_t *widget, + int min, + int max, + int thumb, + int page) +{ + if (widget == NULL) + return false; + + if ((widget->type != FB_WIDGET_TYPE_HSCROLL) && + (widget->type != FB_WIDGET_TYPE_VSCROLL)) + return false; + + widget->u.scroll.minimum = min; + widget->u.scroll.maximum = max; + widget->u.scroll.thumb = thumb; + widget->u.scroll.page = page; + + if (widget->u.scroll.position > max) + widget->u.scroll.position = max; + if (widget->u.scroll.position < min) + widget->u.scroll.position = min; + + fbtk_request_redraw(widget); + + return true; +} + +/* exported function documented in fbtk.h */ +bool +fbtk_set_scroll_position(fbtk_widget_t *widget, int position) +{ + if (widget == NULL) + return false; + + if ((widget->type != FB_WIDGET_TYPE_HSCROLL) && + (widget->type != FB_WIDGET_TYPE_VSCROLL)) + return false; + + if ((position < widget->u.scroll.minimum) || + (position > widget->u.scroll.maximum)) + return false; + + widget->u.scroll.position = position; + + fbtk_request_redraw(widget); + + return true; +} + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/kolibrios/fb/fbtk/text.c b/frontends/kolibrios/fb/fbtk/text.c new file mode 100644 index 000000000..31417c2e0 --- /dev/null +++ b/frontends/kolibrios/fb/fbtk/text.c @@ -0,0 +1,654 @@ +/* + * Copyright 2010 Vincent Sanders + * + * Framebuffer windowing toolkit scrollbar widgets. + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf 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 . + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "utils/log.h" +#include "netsurf/browser_window.h" +#include "netsurf/plotters.h" + +#include "framebuffer/gui.h" +#include "framebuffer/fbtk.h" +#include "framebuffer/font.h" +#include "framebuffer/framebuffer.h" +#include "framebuffer/image_data.h" + +#include "widget.h" + +//#define TEXT_WIDGET_BORDER 3 /**< The pixel border round a text widget. */ + +/* Lighten a colour by taking seven eights of each channel's intensity + * and adding a full eighth + */ +#define brighten_colour(c1) \ + (((((7 * ((c1 >> 16) & 0xff)) >> 3) + 32) << 16) | \ + ((((7 * ((c1 >> 8) & 0xff)) >> 3) + 32) << 8) | \ + ((((7 * (c1 & 0xff)) >> 3) + 32) << 0)) + +/* Convert pixels to points, assuming a DPI of 90 */ +#define px_to_pt(x) (((x) * 72) / FBTK_DPI) + +/* Get a font style for a text input */ +static inline void +fb_text_font_style(fbtk_widget_t *widget, int *font_height, int *padding, + plot_font_style_t *font_style) +{ + if (widget->u.text.outline) + *padding = 1; + else + *padding = 0; + +#ifdef FB_USE_FREETYPE + *padding += widget->height / 6; + *font_height = widget->height - *padding - *padding; +#else + *font_height = FB_FONT_HEIGHT; + *padding = (widget->height - *padding - *font_height) / 2; +#endif + + font_style->family = PLOT_FONT_FAMILY_SANS_SERIF; + font_style->size = px_to_pt(*font_height * FONT_SIZE_SCALE); + font_style->weight = 400; + font_style->flags = FONTF_NONE; + font_style->background = widget->bg; + font_style->foreground = widget->fg; +} + +/** Text redraw callback. + * + * Called when a text widget requires redrawing. + * + * @param widget The widget to be redrawn. + * @param cbi The callback parameters. + * @return The callback result. + */ +static int +fb_redraw_text(fbtk_widget_t *widget, fbtk_callback_info *cbi ) +{ + nsfb_bbox_t bbox; + nsfb_bbox_t rect; + fbtk_widget_t *root; + plot_font_style_t font_style; + int caret_x, caret_y, caret_h; + int fh; + int padding; + int scroll = 0; + bool caret = false; + struct redraw_context ctx = { + .interactive = true, + .background_images = true, + .plot = &fb_plotters + }; + + fb_text_font_style(widget, &fh, &padding, &font_style); + + if (fbtk_get_caret(widget, &caret_x, &caret_y, &caret_h)) { + caret = true; + } + + root = fbtk_get_root_widget(widget); + + fbtk_get_bbox(widget, &bbox); + + rect = bbox; + + nsfb_claim(root->u.root.fb, &bbox); + + /* clear background */ + if ((widget->bg & 0xFF000000) != 0) { + /* transparent polygon filling isnt working so fake it */ + nsfb_plot_rectangle_fill(root->u.root.fb, &bbox, widget->bg); + } + + /* widget can have a single pixel outline border */ + if (widget->u.text.outline) { + rect.x1--; + rect.y1--; + nsfb_plot_rectangle(root->u.root.fb, &rect, 1, + 0x00000000, false, false); + } + + if (widget->u.text.text != NULL) { + int x = bbox.x0 + padding; + int y = bbox.y0 + ((fh * 3 + 2) / 4) + padding; + +#ifdef FB_USE_FREETYPE + /* Freetype renders text higher */ + y += 1; +#endif + + if (caret && widget->width - padding - padding < caret_x) { + scroll = (widget->width - padding - padding) - caret_x; + x += scroll; + } + + /* Call the fb text plotting, baseline is 3/4 down the font */ + ctx.plot->text(&ctx, + &font_style, + x, y, + widget->u.text.text, + widget->u.text.len); + } + + if (caret) { + /* This widget has caret, so render it */ + nsfb_t *nsfb = fbtk_get_nsfb(widget); + nsfb_bbox_t line; + nsfb_plot_pen_t pen; + + line.x0 = bbox.x0 + caret_x + scroll; + line.y0 = bbox.y0 + caret_y; + line.x1 = bbox.x0 + caret_x + scroll; + line.y1 = bbox.y0 + caret_y + caret_h; + + pen.stroke_type = NFSB_PLOT_OPTYPE_SOLID; + pen.stroke_width = 1; + pen.stroke_colour = 0xFF0000FF; + + nsfb_plot_line(nsfb, &line, &pen); + } + + nsfb_update(root->u.root.fb, &bbox); + + return 0; +} + +/** Text destroy callback. + * + * Called when a text widget is destroyed. + * + * @param widget The widget being destroyed. + * @param cbi The callback parameters. + * @return The callback result. + */ +static int fb_destroy_text(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + if ((widget == NULL) || (widget->type != FB_WIDGET_TYPE_TEXT)) { + return 0; + } + + if (widget->u.text.text != NULL) { + free(widget->u.text.text); + } + + return 0; +} + +/** Text button redraw callback. + * + * Called when a text widget requires redrawing. + * + * @param widget The widget to be redrawn. + * @param cbi The callback parameters. + * @return The callback result. + */ +static int +fb_redraw_text_button(fbtk_widget_t *widget, fbtk_callback_info *cbi ) +{ + nsfb_bbox_t bbox; + nsfb_bbox_t rect; + nsfb_bbox_t line; + nsfb_plot_pen_t pen; + plot_font_style_t font_style; + int fh; + int border; + fbtk_widget_t *root = fbtk_get_root_widget(widget); + struct redraw_context ctx = { + .interactive = true, + .background_images = true, + .plot = &fb_plotters + }; + + fb_text_font_style(widget, &fh, &border, &font_style); + + pen.stroke_type = NFSB_PLOT_OPTYPE_SOLID; + pen.stroke_width = 1; + pen.stroke_colour = brighten_colour(widget->bg); + + fbtk_get_bbox(widget, &bbox); + + rect = bbox; + rect.x1--; + rect.y1--; + + nsfb_claim(root->u.root.fb, &bbox); + + /* clear background */ + if ((widget->bg & 0xFF000000) != 0) { + /* transparent polygon filling isnt working so fake it */ + nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg); + } + + if (widget->u.text.outline) { + line.x0 = rect.x0; + line.y0 = rect.y0; + line.x1 = rect.x0; + line.y1 = rect.y1; + nsfb_plot_line(root->u.root.fb, &line, &pen); + line.x0 = rect.x0; + line.y0 = rect.y0; + line.x1 = rect.x1; + line.y1 = rect.y0; + nsfb_plot_line(root->u.root.fb, &line, &pen); + pen.stroke_colour = darken_colour(widget->bg); + line.x0 = rect.x0; + line.y0 = rect.y1; + line.x1 = rect.x1; + line.y1 = rect.y1; + nsfb_plot_line(root->u.root.fb, &line, &pen); + line.x0 = rect.x1; + line.y0 = rect.y0; + line.x1 = rect.x1; + line.y1 = rect.y1; + nsfb_plot_line(root->u.root.fb, &line, &pen); + } + + if (widget->u.text.text != NULL) { + /* Call the fb text plotting, baseline is 3/4 down the font */ + ctx.plot->text(&ctx, + &font_style, + bbox.x0 + border, + bbox.y0 + ((fh * 3) / 4) + border, + widget->u.text.text, + widget->u.text.len); + } + + nsfb_update(root->u.root.fb, &bbox); + + return 0; +} + +static void +fb_text_input_remove_caret_cb(fbtk_widget_t *widget) +{ + int c_x, c_y, c_h; + + if (fbtk_get_caret(widget, &c_x, &c_y, &c_h)) { + fbtk_request_redraw(widget); + } +} + +/** Routine called when text events occour in writeable widget. + * + * @param widget The widget reciving input events. + * @param cbi The callback parameters. + * @return The callback result. + */ +static int +text_input(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + int value; + static fbtk_modifier_type modifier = FBTK_MOD_CLEAR; + char *temp; + plot_font_style_t font_style; + int fh; + int border; + bool caret_moved = false; + + fb_text_font_style(widget, &fh, &border, &font_style); + + if (cbi->event == NULL) { + /* gain focus */ + if (widget->u.text.text == NULL) + widget->u.text.text = calloc(1,1); + + return 0; + } + + value = cbi->event->value.keycode; + + if (cbi->event->type != NSFB_EVENT_KEY_DOWN) { + switch (value) { + case NSFB_KEY_RSHIFT: + modifier &= ~FBTK_MOD_RSHIFT; + break; + + case NSFB_KEY_LSHIFT: + modifier &= ~FBTK_MOD_LSHIFT; + break; + + case NSFB_KEY_RCTRL: + modifier &= ~FBTK_MOD_RCTRL; + break; + + case NSFB_KEY_LCTRL: + modifier &= ~FBTK_MOD_LCTRL; + break; + + default: + break; + } + return 0; + } + + switch (value) { + case NSFB_KEY_BACKSPACE: + if (widget->u.text.idx <= 0) + break; + memmove(widget->u.text.text + widget->u.text.idx - 1, + widget->u.text.text + widget->u.text.idx, + widget->u.text.len - widget->u.text.idx); + widget->u.text.idx--; + widget->u.text.len--; + widget->u.text.text[widget->u.text.len] = 0; + + fb_font_width(&font_style, widget->u.text.text, + widget->u.text.len, &widget->u.text.width); + + caret_moved = true; + break; + + case NSFB_KEY_RETURN: + widget->u.text.enter(widget->u.text.pw, widget->u.text.text); + break; + + case NSFB_KEY_RIGHT: + if (widget->u.text.idx < widget->u.text.len) { + if (modifier == FBTK_MOD_CLEAR) + widget->u.text.idx++; + else + widget->u.text.idx = widget->u.text.len; + + caret_moved = true; + } + break; + + case NSFB_KEY_LEFT: + if (widget->u.text.idx > 0) { + if (modifier == FBTK_MOD_CLEAR) + widget->u.text.idx--; + else + widget->u.text.idx = 0; + + caret_moved = true; + } + break; + + case NSFB_KEY_PAGEUP: + case NSFB_KEY_PAGEDOWN: + case NSFB_KEY_UP: + case NSFB_KEY_DOWN: + /* Not handling any of these correctly yet, but avoid putting + * charcters in the text widget when they're pressed. */ + break; + + case NSFB_KEY_RSHIFT: + modifier |= FBTK_MOD_RSHIFT; + break; + + case NSFB_KEY_LSHIFT: + modifier |= FBTK_MOD_LSHIFT; + break; + + case NSFB_KEY_RCTRL: + modifier |= FBTK_MOD_RCTRL; + break; + + case NSFB_KEY_LCTRL: + modifier |= FBTK_MOD_LCTRL; + break; + + default: + if (modifier & FBTK_MOD_LCTRL || modifier & FBTK_MOD_RCTRL) { + /* CTRL pressed, don't enter any text */ + if (value == NSFB_KEY_u) { + /* CTRL+U: clear writable */ + widget->u.text.idx = 0; + widget->u.text.len = 0; + widget->u.text.text[widget->u.text.len] = '\0'; + widget->u.text.width = 0; + caret_moved = true; + } + break; + } + + /* allow for new character and null */ + temp = realloc(widget->u.text.text, widget->u.text.len + 2); + if (temp == NULL) { + break; + } + + widget->u.text.text = temp; + memmove(widget->u.text.text + widget->u.text.idx + 1, + widget->u.text.text + widget->u.text.idx, + widget->u.text.len - widget->u.text.idx); + widget->u.text.text[widget->u.text.idx] = + fbtk_keycode_to_ucs4(value, modifier); + widget->u.text.idx++; + widget->u.text.len++; + widget->u.text.text[widget->u.text.len] = '\0'; + + fb_font_width(&font_style, widget->u.text.text, + widget->u.text.len, &widget->u.text.width); + caret_moved = true; + break; + } + + if (caret_moved) { + fb_font_width(&font_style, widget->u.text.text, + widget->u.text.idx, &widget->u.text.idx_offset); + fbtk_set_caret(widget, true, + widget->u.text.idx_offset + border, + border, + widget->height - border - border, + fb_text_input_remove_caret_cb); + } + + fbtk_request_redraw(widget); + + return 0; +} + +/** Routine called when click events occour in writeable widget. + * + * @param widget The widget reciving click events. + * @param cbi The callback parameters. + * @return The callback result. + */ +static int +text_input_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + plot_font_style_t font_style; + int fh; + int border; + size_t idx; + + fb_text_font_style(widget, &fh, &border, &font_style); + + widget->u.text.idx = widget->u.text.len; + + fb_font_position(&font_style, widget->u.text.text, + widget->u.text.len, cbi->x - border, + &idx, + &widget->u.text.idx_offset); + widget->u.text.idx = idx; + fbtk_set_caret(widget, true, + widget->u.text.idx_offset + border, + border, + widget->height - border - border, + fb_text_input_remove_caret_cb); + + fbtk_request_redraw(widget); + + return 0; +} + +/** Routine called when "stripped of focus" event occours for writeable widget. + * + * @param widget The widget reciving "stripped of focus" event. + * @param cbi The callback parameters. + * @return The callback result. + */ +static int +text_input_strip_focus(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + fbtk_set_caret(widget, false, 0, 0, 0, NULL); + + return 0; +} + +/* exported function documented in fbtk.h */ +void +fbtk_writable_text(fbtk_widget_t *widget, fbtk_enter_t enter, void *pw) +{ + widget->u.text.enter = enter; + widget->u.text.pw = pw; + + fbtk_set_handler(widget, FBTK_CBT_INPUT, text_input, widget); +} + +/* exported function documented in fbtk.h */ +void +fbtk_set_text(fbtk_widget_t *widget, const char *text) +{ + plot_font_style_t font_style; + int c_x, c_y, c_h; + int fh; + int border; + + if ((widget == NULL) || (widget->type != FB_WIDGET_TYPE_TEXT)) + return; + if (widget->u.text.text != NULL) { + if (strcmp(widget->u.text.text, text) == 0) + return; /* text is being set to the same thing */ + free(widget->u.text.text); + } + widget->u.text.text = strdup(text); + widget->u.text.len = strlen(text); + widget->u.text.idx = widget->u.text.len; + + + fb_text_font_style(widget, &fh, &border, &font_style); + fb_font_width(&font_style, widget->u.text.text, + widget->u.text.len, &widget->u.text.width); + fb_font_width(&font_style, widget->u.text.text, + widget->u.text.idx, &widget->u.text.idx_offset); + + if (fbtk_get_caret(widget, &c_x, &c_y, &c_h)) { + /* Widget has caret; move it to end of new string */ + fbtk_set_caret(widget, true, + widget->u.text.idx_offset + border, + border, + widget->height - border - border, + fb_text_input_remove_caret_cb); + } + + fbtk_request_redraw(widget); +} + +/* exported function documented in fbtk.h */ +fbtk_widget_t * +fbtk_create_text(fbtk_widget_t *parent, + int x, + int y, + int width, + int height, + colour bg, + colour fg, + bool outline) +{ + fbtk_widget_t *neww; + + neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_TEXT, x, y, width, height); + neww->fg = fg; + neww->bg = bg; + neww->mapped = true; + neww->u.text.outline = outline; + + fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_text, NULL); + fbtk_set_handler(neww, FBTK_CBT_DESTROY, fb_destroy_text, NULL); + + return neww; +} + +/* exported function documented in fbtk.h */ +fbtk_widget_t * +fbtk_create_writable_text(fbtk_widget_t *parent, + int x, + int y, + int width, + int height, + colour bg, + colour fg, + bool outline, + fbtk_enter_t enter, + void *pw) +{ + fbtk_widget_t *neww; + + neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_TEXT, x, y, width, height); + neww->fg = fg; + neww->bg = bg; + neww->mapped = true; + + neww->u.text.outline = outline; + neww->u.text.enter = enter; + neww->u.text.pw = pw; + + fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_text, NULL); + fbtk_set_handler(neww, FBTK_CBT_DESTROY, fb_destroy_text, NULL); + fbtk_set_handler(neww, FBTK_CBT_CLICK, text_input_click, pw); + fbtk_set_handler(neww, FBTK_CBT_STRIP_FOCUS, text_input_strip_focus, NULL); + fbtk_set_handler(neww, FBTK_CBT_INPUT, text_input, neww); + + return neww; +} + +/* exported function documented in fbtk.h */ +fbtk_widget_t * +fbtk_create_text_button(fbtk_widget_t *parent, + int x, + int y, + int width, + int height, + colour bg, + colour fg, + fbtk_callback click, + void *pw) +{ + fbtk_widget_t *neww; + + neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_TEXT, x, y, width, height); + neww->fg = fg; + neww->bg = bg; + neww->mapped = true; + + neww->u.text.outline = true; + + fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_text_button, NULL); + fbtk_set_handler(neww, FBTK_CBT_DESTROY, fb_destroy_text, NULL); + fbtk_set_handler(neww, FBTK_CBT_CLICK, click, pw); + fbtk_set_handler(neww, FBTK_CBT_POINTERENTER, fbtk_set_ptr, &hand_image); + + return neww; +} + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/kolibrios/fb/fbtk/user.c b/frontends/kolibrios/fb/fbtk/user.c new file mode 100644 index 000000000..de547ab46 --- /dev/null +++ b/frontends/kolibrios/fb/fbtk/user.c @@ -0,0 +1,63 @@ +/* + * Copyright 2010 Vincent Sanders + * + * Framebuffer windowing toolkit user widget. + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf 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 . + */ + +#include +#include +#include + +#include "framebuffer/gui.h" +#include "framebuffer/fbtk.h" + +#include "widget.h" + +/* exported function documented in fbtk.h */ +void * +fbtk_get_userpw(fbtk_widget_t *widget) +{ + if ((widget == NULL) || + (widget->type != FB_WIDGET_TYPE_USER)) + return NULL; + + return widget->u.user.pw; +} + +/* exported function documented in fbtk.h */ +fbtk_widget_t * +fbtk_create_user(fbtk_widget_t *parent, + int x, + int y, + int width, + int height, + void *pw) +{ + fbtk_widget_t *neww; + + neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_USER, x, y, width, height); + neww->u.user.pw = pw; + neww->mapped = true; + + return neww; +} + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/kolibrios/fb/fbtk/widget.h b/frontends/kolibrios/fb/fbtk/widget.h new file mode 100644 index 000000000..5622723ee --- /dev/null +++ b/frontends/kolibrios/fb/fbtk/widget.h @@ -0,0 +1,259 @@ +/* + * Copyright 2010 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NETSURF_FB_FBTK_WIDGET_H +#define NETSURF_FB_FBTK_WIDGET_H + +#include + +enum fbtk_widgettype_e { + FB_WIDGET_TYPE_ROOT = 0, + FB_WIDGET_TYPE_WINDOW, + FB_WIDGET_TYPE_BITMAP, + FB_WIDGET_TYPE_FILL, + FB_WIDGET_TYPE_TEXT, + FB_WIDGET_TYPE_HSCROLL, + FB_WIDGET_TYPE_VSCROLL, + FB_WIDGET_TYPE_USER, +}; + + +/** Widget description. + * + * A widget is an entry in a tree structure which represents a + * rectangular area with co-ordinates relative to its parent widget. + * This area has a distinct set of callback operations for handling + * events which occour within its boundries. A widget may have an + * arbitrary number of child widgets. The order within the tree + * determines a widgets z order. + * + * --- + * A + * | + * +----------+ + * +--->| Button 3 | + * | +----------+ + * | | A + * | V | + * | +----------+ + * | | Button 2 | + * | +----------+ + * | | A + * | V | + * | +----------+ + * | | Button 1 | + * | +----------+ + * | | A + * | V | + * --- | +----------+ + * A | +->| Filled | + * | | | +----------+ + * +----------+ | | | + * +---->| |-+ | V + * | | Window 1 | | --- --- + * | | |---+ A + * | +----------+ | + * | | A +----------+ --- + * | | | +--->| Button 2 | A + * | | | | +----------+ | + * | | | | | A +-------------+ + * | | | | | | +--->| Button Up | + * | | | | | | | +-------------+ + * | | | | | | | | A + * | | | | | | | V | + * | | | | | | | +-------------+ + * | | | | | | | | Button Down | + * | | | | | | | +-------------+ + * | | | | | | | | A + * | | | | | | | V | + * | | | | | | | +-------------+ + * | | | | | | | +->| Scroller | + * | | | | V | | | +-------------+ + * | | | | +----------+ | | | + * | | | | | |-+ | V + * | | | | | V Scroll | | --- + * | | | | | |---+ + * | | | | +----------+ + * | | | | | A + * | | | | V | + * | | | | +----------+ + * | | | | +->| Button 1 | + * | | | | | +----------+ + * | +----------+ | | | + * | | |-+ | V + * | | Window 2 | | --- + * | | |---+ + * | +----------+ + * | | A + * | V | + * | +------------+ + * --- | | Background | + * A | +->| Bitmap | + * | | | +------------+ + * +------+ | | | + * | |-+ | V + * | Root | | --- + * | |---+ + * +------+ + * | + * V + * --- + * + * Every widget is contained within this generic wrapper. The + * integrated union provides for data specific to a widget type. + */ +struct fbtk_widget_s { + struct fbtk_widget_s *next; /* next lower z ordered widget in tree */ + struct fbtk_widget_s *prev; /* next higher z ordered widget in tree */ + + struct fbtk_widget_s *parent; /* parent widget */ + + struct fbtk_widget_s *first_child; /* first child widget */ + struct fbtk_widget_s *last_child; /* last child widget */ + + /* flags */ + bool mapped; /**< The widget is mapped/visible . */ + + /* Generic properties */ + int x; + int y; + int width; + int height; + colour bg; + colour fg; + + /* event callback handlers */ + fbtk_callback callback[FBTK_CBT_END]; + void *callback_context[FBTK_CBT_END]; + + /* widget redraw */ + struct { + bool child; /* A child of this widget requires redrawing */ + bool needed; /* the widget requires redrawing */ + int x; + int y; + int width; + int height; + } redraw; + + enum fbtk_widgettype_e type; /**< The type of the widget */ + + + union { + /* toolkit base handle */ + struct { + nsfb_t *fb; + struct fbtk_widget_s *prev; /* previous widget pointer wasin */ + struct fbtk_widget_s *grabbed; /* widget that has grabbed pointer movement. */ + struct fbtk_widget_s *input; + + /* caret */ + struct { + struct fbtk_widget_s *owner; /* widget / NULL */ + int x; /* relative to owner */ + int y; /* relative to owner */ + int height; + void (*remove_cb)(fbtk_widget_t *widget); + } caret; + } root; + + /* bitmap */ + struct { + struct fbtk_bitmap *bitmap; + } bitmap; + + /* text */ + struct { + char* text; + bool outline; + fbtk_enter_t enter; + void *pw; + int idx; /* caret pos in text */ + int len; /* text length */ + int width; /* text width in px */ + int idx_offset; /* caret pos in pixels */ + } text; + + /* application driven widget */ + struct { + void *pw; /* private data for user widget */ + } user; + + struct { + int minimum; /* lowest value of scrollbar */ + int maximum; /* highest value of scrollbar */ + int thumb; /* size of bar representing a page */ + int page; /* amount to page document */ + int position; /* position of bar */ + int drag; /* offset to start of drag */ + int drag_position; /* indicator bar pos at drag start */ + struct fbtk_widget_s *btnul; /* scroll button up/left */ + struct fbtk_widget_s *btndr; /* scroll button down/right*/ + } scroll; + + } u; +}; + + +/* These functions are not considered part of the public API but are + * not static as they are used by the higher level widget provision + * routines + */ + + +/** creates a new widget and insert it into to hierachy. + * + * The widget is set to defaults of false, 0 or NULL. + * + * @param parent The parent widget. The new widget will be added with + * the shallowest z order relative to its siblings. + * @param type The type of the widget. + * @param x The x co-ordinate relative to the parent widget. + * @param y The y co-ordinate relative to the parent widget. + * @param width the widgets width. This will be clipped to the parent, if + * the value is 0 the largest extent which can fit within the parent + * is used, if the value is negative the largest value that will fit + * within the parent less the value given will be used. + * @param height the widgets width. This will be clipped to the parent, if + * the value is 0 the largest extent which can fit within the parent + * is used, if the value is negative the largest value that will fit + * within the parent less the value given will be used. + */ +fbtk_widget_t *fbtk_widget_new(fbtk_widget_t *parent, enum fbtk_widgettype_e type, int x, int y, int width, int height); + +/** find the root widget from any widget in the toolkit hierarchy. + * + * @param widget Any widget. + * @return The root widget or NULL if \a widget was not valid. + */ +fbtk_widget_t *fbtk_get_root_widget(fbtk_widget_t *widget); + +/** set pointer to bitmap in context. + * + * widget helper callback to set cursor image to the bitmap passed in + * the callbacks private data. + */ +int fbtk_set_ptr(fbtk_widget_t *widget, fbtk_callback_info *cbi); + +#endif + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/kolibrios/fb/fbtk/window.c b/frontends/kolibrios/fb/fbtk/window.c new file mode 100644 index 000000000..b385e8e3f --- /dev/null +++ b/frontends/kolibrios/fb/fbtk/window.c @@ -0,0 +1,91 @@ +/* + * Copyright 2010 Vincent Sanders + * + * Framebuffer windowing toolkit window widget. + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf 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 . + */ + +#include +#include + +#include +#include + +#include "netsurf/browser_window.h" + +#include "framebuffer/gui.h" +#include "framebuffer/fbtk.h" + +#include "widget.h" + +/** Window redraw callback. + * + * Called when a window requires redrawing. + * + * @param widget The widget to be redrawn. + * @param cbi The callback parameters. + * @return The callback result. + */ +static int +fb_redraw_window(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + nsfb_bbox_t bbox; + nsfb_t *nsfb; + + if ((widget->bg & 0xFF000000) == 0) + return 0; + + nsfb = fbtk_get_nsfb(widget); + + fbtk_get_bbox(widget, &bbox); + + nsfb_claim(nsfb, &bbox); + + nsfb_plot_rectangle_fill(nsfb, &bbox, widget->bg); + + nsfb_update(nsfb, &bbox); + + return 0; +} + +/* exported function documented in fbtk.h */ +fbtk_widget_t * +fbtk_create_window(fbtk_widget_t *parent, + int x, + int y, + int width, + int height, + colour bg) +{ + fbtk_widget_t *neww; + + if (parent == NULL) + return NULL; + + neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_WINDOW, x, y, width, height); + + neww->bg = bg; + + fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_window, NULL); + + return neww; +} + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/kolibrios/fb/fetch.c b/frontends/kolibrios/fb/fetch.c new file mode 100644 index 000000000..801b87a74 --- /dev/null +++ b/frontends/kolibrios/fb/fetch.c @@ -0,0 +1,100 @@ +/* + * Copyright 2014 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf 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 . + */ + +/** \file + * Interfaces for fetch table. + */ + +#include +#include +#include +#include + +#include "utils/nsurl.h" +#include "utils/log.h" +#include "utils/filepath.h" +#include "utils/file.h" +#include "netsurf/fetch.h" + +#include "framebuffer/findfile.h" +#include "framebuffer/fetch.h" + + +/** + * Translate resource to full url. + * + * Transforms a resource: path into a full URL. The returned URL + * is used as the target for a redirect. The caller takes ownership of + * the returned nsurl including unrefing it when finished with it. + * + * \param path The path of the resource to locate. + * \return A string containing the full URL of the target object or + * NULL if no suitable resource can be found. + */ +static nsurl *get_resource_url(const char *path) +{ + char buf[PATH_MAX]; + nsurl *url = NULL; + + if (strcmp(path, "favicon.ico") == 0) + path = "favicon.png"; + + netsurf_path_to_nsurl(filepath_sfind(respaths, buf, path), &url); + + return url; +} + +/** + * filetype -- determine the MIME type of a local file + */ +static const char *fetch_filetype(const char *unix_path) +{ + int l; + LOG("unix path %s", unix_path); + l = strlen(unix_path); + if (2 < l && strcasecmp(unix_path + l - 3, "css") == 0) + return "text/css"; + if (2 < l && strcasecmp(unix_path + l - 3, "f79") == 0) + return "text/css"; + if (2 < l && strcasecmp(unix_path + l - 3, "jpg") == 0) + return "image/jpeg"; + if (3 < l && strcasecmp(unix_path + l - 4, "jpeg") == 0) + return "image/jpeg"; + if (2 < l && strcasecmp(unix_path + l - 3, "gif") == 0) + return "image/gif"; + if (2 < l && strcasecmp(unix_path + l - 3, "png") == 0) + return "image/png"; + if (2 < l && strcasecmp(unix_path + l - 3, "b60") == 0) + return "image/png"; + if (2 < l && strcasecmp(unix_path + l - 3, "jng") == 0) + return "image/jng"; + if (2 < l && strcasecmp(unix_path + l - 3, "svg") == 0) + return "image/svg"; + if (2 < l && strcasecmp(unix_path + l - 3, "bmp") == 0) + return "image/bmp"; + return "text/html"; +} + +/* table for fetch operations */ +static struct gui_fetch_table fetch_table = { + .filetype = fetch_filetype, + + .get_resource_url = get_resource_url, +}; + +struct gui_fetch_table *framebuffer_fetch_table = &fetch_table; diff --git a/frontends/kolibrios/fb/fetch.h b/frontends/kolibrios/fb/fetch.h new file mode 100644 index 000000000..718b08300 --- /dev/null +++ b/frontends/kolibrios/fb/fetch.h @@ -0,0 +1,25 @@ +/* + * Copyright 2014 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#ifndef NETSURF_FB_FETCH_H +#define NETSURF_FB_FETCH_H + +struct gui_fetch_table *framebuffer_fetch_table; + +#endif diff --git a/frontends/kolibrios/fb/findfile.c b/frontends/kolibrios/fb/findfile.c new file mode 100644 index 000000000..67312f452 --- /dev/null +++ b/frontends/kolibrios/fb/findfile.c @@ -0,0 +1,56 @@ +/* + * Copyright 2008 Daniel Silverstone + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf 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 . + */ + +#include + +#include "utils/filepath.h" + +#include "framebuffer/findfile.h" + +char **respaths; /** resource search path vector */ + +/** Create an array of valid paths to search for resources. + * + * The idea is that all the complex path computation to find resources + * is performed here, once, rather than every time a resource is + * searched for. + */ +char ** +fb_init_resource(const char *resource_path) +{ + char **pathv; /* resource path string vector */ + char **respath; /* resource paths vector */ + const char *lang = NULL; + + pathv = filepath_path_to_strvec(resource_path); + + respath = filepath_generate(pathv, &lang); + + filepath_free_strvec(pathv); + + return respath; +} + + + +/* + * Local Variables: + * c-basic-offset: 8 + * End: + */ + diff --git a/frontends/kolibrios/fb/findfile.h b/frontends/kolibrios/fb/findfile.h new file mode 100644 index 000000000..1f3db6eb1 --- /dev/null +++ b/frontends/kolibrios/fb/findfile.h @@ -0,0 +1,32 @@ +/* + * Copyright 2008 Daniel Silverstone + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NETSURF_FB_FINDFILE_H +#define NETSURF_FB_FINDFILE_H + +extern char **respaths; + +/** Create an array of valid paths to search for resources. + * + * The idea is that all the complex path computation to find resources + * is performed here, once, rather than every time a resource is + * searched for. + */ +char **fb_init_resource(const char *resource_path); + +#endif /* NETSURF_FB_FINDFILE_H */ diff --git a/frontends/kolibrios/fb/font.h b/frontends/kolibrios/fb/font.h new file mode 100644 index 000000000..8513c93ed --- /dev/null +++ b/frontends/kolibrios/fb/font.h @@ -0,0 +1,68 @@ +/* + * Copyright 2008 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NETSURF_FB_FONT_H +#define NETSURF_FB_FONT_H + +extern struct gui_layout_table *framebuffer_layout_table; +extern struct gui_utf8_table *framebuffer_utf8_table; + +/** + * Initialise framebuffer font handling. + */ +bool fb_font_init(void); + +/** + * Finalise framebuffer font handling. + */ +bool fb_font_finalise(void); + +/** + * Find the position in a string where an x coordinate falls. + * + * \param[in] fstyle style for this text + * \param[in] string UTF-8 string to measure + * \param[in] length length of string, in bytes + * \param[in] x coordinate to search for + * \param[out] char_offset updated to offset in string of actual_x, [0..length] + * \param[out] actual_x updated to x coordinate of character closest to x + * \return NSERROR_OK and char_offset and actual_x updated or + * appropriate error code on faliure + */ +nserror fb_font_position(const struct plot_font_style *fstyle, const char *string, size_t length, int x, size_t *char_offset, int *actual_x); + +/** + * Measure the width of a string. + * + * \param[in] fstyle plot style for this text + * \param[in] string UTF-8 string to measure + * \param[in] length length of string, in bytes + * \param[out] width updated to width of string[0..length) + * \return NSERROR_OK and width updated or appropriate error code on faliure + */ +nserror fb_font_width(const struct plot_font_style *fstyle, const char *string, size_t length, int *width); + + +#ifdef KOLIBRI_USE_FREETYPE +#include "kolibrios/fb/font_freetype.h" +#else +#include "kolibrios/fb/font_internal.h" +#endif + +#endif /* NETSURF_FB_FONT_H */ + diff --git a/frontends/kolibrios/fb/font_freetype.c b/frontends/kolibrios/fb/font_freetype.c new file mode 100644 index 000000000..e7c07f5ff --- /dev/null +++ b/frontends/kolibrios/fb/font_freetype.c @@ -0,0 +1,566 @@ +/* + * Copyright 2005 James Bursa + * 2008 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf 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 . + */ + +#include + +#include +#include FT_CACHE_H + +#include "netsurf/inttypes.h" +#include "utils/filepath.h" +#include "utils/utf8.h" +#include "utils/log.h" +#include "utils/nsoption.h" +#include "netsurf/utf8.h" +#include "netsurf/layout.h" +#include "netsurf/browser_window.h" +#include "netsurf/plot_style.h" + +#include "framebuffer/gui.h" +#include "framebuffer/font.h" +#include "framebuffer/findfile.h" + +/* glyph cache minimum size */ +#define CACHE_MIN_SIZE (100 * 1024) + +#define BOLD_WEIGHT 700 + +static FT_Library library; +static FTC_Manager ft_cmanager; +static FTC_CMapCache ft_cmap_cache ; +static FTC_ImageCache ft_image_cache; + +int ft_load_type; + +/* cache manager faceID data to create freetype faceid on demand */ +typedef struct fb_faceid_s { + char *fontfile; /* path to font */ + int index; /* index of font */ + int cidx; /* character map index for unicode */ +} fb_faceid_t; + + +enum fb_face_e { + FB_FACE_SANS_SERIF = 0, + FB_FACE_SANS_SERIF_BOLD, + FB_FACE_SANS_SERIF_ITALIC, + FB_FACE_SANS_SERIF_ITALIC_BOLD, + FB_FACE_SERIF, + FB_FACE_SERIF_BOLD, + FB_FACE_MONOSPACE, + FB_FACE_MONOSPACE_BOLD, + FB_FACE_CURSIVE, + FB_FACE_FANTASY, + FB_FACE_COUNT +}; + +/* defines for accesing the faces */ +#define FB_FACE_DEFAULT 0 + +static fb_faceid_t *fb_faces[FB_FACE_COUNT]; + +/** + * map cache manager handle to face id + */ +static FT_Error +ft_face_requester(FTC_FaceID face_id, + FT_Library library, + FT_Pointer request_data, + FT_Face *face ) +{ + FT_Error error; + fb_faceid_t *fb_face = (fb_faceid_t *)face_id; + int cidx; + + error = FT_New_Face(library, fb_face->fontfile, fb_face->index, face); + if (error) { + LOG("Could not find font (code %d)", error); + } else { + + error = FT_Select_Charmap(*face, FT_ENCODING_UNICODE); + if (error) { + LOG("Could not select charmap (code %d)", error); + } else { + for (cidx = 0; cidx < (*face)->num_charmaps; cidx++) { + if ((*face)->charmap == (*face)->charmaps[cidx]) { + fb_face->cidx = cidx; + break; + } + } + } + } + LOG("Loaded face from %s", fb_face->fontfile); + + return error; +} + +/** + * create new framebuffer face and cause it to be loaded to check its ok + */ +static fb_faceid_t * +fb_new_face(const char *option, const char *resname, const char *fontname) +{ + fb_faceid_t *newf; + FT_Error error; + FT_Face aface; + char buf[PATH_MAX]; + + newf = calloc(1, sizeof(fb_faceid_t)); + + if (option != NULL) { + newf->fontfile = strdup(option); + } else { + filepath_sfind(respaths, buf, fontname); + newf->fontfile = strdup(buf); + } + + error = FTC_Manager_LookupFace(ft_cmanager, (FTC_FaceID)newf, &aface); + if (error) { + LOG("Could not find font face %s (code %d)", fontname, error); + free(newf->fontfile); + free(newf); + newf = NULL; + } + + return newf; +} + +/* exported interface documented in framebuffer/font.h */ +bool fb_font_init(void) +{ + FT_Error error; + FT_ULong max_cache_size; + FT_UInt max_faces = 6; + fb_faceid_t *fb_face; + + /* freetype library initialise */ + error = FT_Init_FreeType( &library ); + if (error) { + LOG("Freetype could not initialised (code %d)", error); + return false; + } + + /* set the Glyph cache size up */ + max_cache_size = nsoption_int(fb_font_cachesize) * 1024; + + if (max_cache_size < CACHE_MIN_SIZE) { + max_cache_size = CACHE_MIN_SIZE; + } + + /* cache manager initialise */ + error = FTC_Manager_New(library, + max_faces, + 0, + max_cache_size, + ft_face_requester, + NULL, + &ft_cmanager); + if (error) { + LOG("Freetype could not initialise cache manager (code %d)", error); + FT_Done_FreeType(library); + return false; + } + + error = FTC_CMapCache_New(ft_cmanager, &ft_cmap_cache); + + error = FTC_ImageCache_New(ft_cmanager, &ft_image_cache); + + /* need to obtain the generic font faces */ + + /* Start with the sans serif font */ + fb_face = fb_new_face(nsoption_charp(fb_face_sans_serif), + "sans_serif.ttf", + NETSURF_FB_FONT_SANS_SERIF); + if (fb_face == NULL) { + /* The sans serif font is the default and must be found. */ + LOG("Could not find the default font"); + FTC_Manager_Done(ft_cmanager); + FT_Done_FreeType(library); + return false; + } else { + fb_faces[FB_FACE_SANS_SERIF] = fb_face; + } + + /* Bold sans serif face */ + fb_face = fb_new_face(nsoption_charp(fb_face_sans_serif_bold), + "sans_serif_bold.ttf", + NETSURF_FB_FONT_SANS_SERIF_BOLD); + if (fb_face == NULL) { + /* seperate bold face unavailabe use the normal weight version */ + fb_faces[FB_FACE_SANS_SERIF_BOLD] = fb_faces[FB_FACE_SANS_SERIF]; + } else { + fb_faces[FB_FACE_SANS_SERIF_BOLD] = fb_face; + } + + /* Italic sans serif face */ + fb_face = fb_new_face(nsoption_charp(fb_face_sans_serif_italic), + "sans_serif_italic.ttf", + NETSURF_FB_FONT_SANS_SERIF_ITALIC); + if (fb_face == NULL) { + /* seperate italic face unavailabe use the normal weight version */ + fb_faces[FB_FACE_SANS_SERIF_ITALIC] = fb_faces[FB_FACE_SANS_SERIF]; + } else { + fb_faces[FB_FACE_SANS_SERIF_ITALIC] = fb_face; + } + + /* Bold italic sans serif face */ + fb_face = fb_new_face(nsoption_charp(fb_face_sans_serif_italic_bold), + "sans_serif_italic_bold.ttf", + NETSURF_FB_FONT_SANS_SERIF_ITALIC_BOLD); + if (fb_face == NULL) { + /* seperate italic face unavailabe use the normal weight version */ + fb_faces[FB_FACE_SANS_SERIF_ITALIC_BOLD] = fb_faces[FB_FACE_SANS_SERIF]; + } else { + fb_faces[FB_FACE_SANS_SERIF_ITALIC_BOLD] = fb_face; + } + + /* serif face */ + fb_face = fb_new_face(nsoption_charp(fb_face_serif), + "serif.ttf", + NETSURF_FB_FONT_SERIF); + if (fb_face == NULL) { + /* serif face unavailabe use the default */ + fb_faces[FB_FACE_SERIF] = fb_faces[FB_FACE_SANS_SERIF]; + } else { + fb_faces[FB_FACE_SERIF] = fb_face; + } + + /* bold serif face*/ + fb_face = fb_new_face(nsoption_charp(fb_face_serif_bold), + "serif_bold.ttf", + NETSURF_FB_FONT_SERIF_BOLD); + if (fb_face == NULL) { + /* bold serif face unavailabe use the normal weight */ + fb_faces[FB_FACE_SERIF_BOLD] = fb_faces[FB_FACE_SERIF]; + } else { + fb_faces[FB_FACE_SERIF_BOLD] = fb_face; + } + + + /* monospace face */ + fb_face = fb_new_face(nsoption_charp(fb_face_monospace), + "monospace.ttf", + NETSURF_FB_FONT_MONOSPACE); + if (fb_face == NULL) { + /* serif face unavailabe use the default */ + fb_faces[FB_FACE_MONOSPACE] = fb_faces[FB_FACE_SANS_SERIF]; + } else { + fb_faces[FB_FACE_MONOSPACE] = fb_face; + } + + /* bold monospace face*/ + fb_face = fb_new_face(nsoption_charp(fb_face_monospace_bold), + "monospace_bold.ttf", + NETSURF_FB_FONT_MONOSPACE_BOLD); + if (fb_face == NULL) { + /* bold serif face unavailabe use the normal weight */ + fb_faces[FB_FACE_MONOSPACE_BOLD] = fb_faces[FB_FACE_MONOSPACE]; + } else { + fb_faces[FB_FACE_MONOSPACE_BOLD] = fb_face; + } + + /* cursive face */ + fb_face = fb_new_face(nsoption_charp(fb_face_cursive), + "cursive.ttf", + NETSURF_FB_FONT_CURSIVE); + if (fb_face == NULL) { + /* cursive face unavailabe use the default */ + fb_faces[FB_FACE_CURSIVE] = fb_faces[FB_FACE_SANS_SERIF]; + } else { + fb_faces[FB_FACE_CURSIVE] = fb_face; + } + + /* fantasy face */ + fb_face = fb_new_face(nsoption_charp(fb_face_fantasy), + "fantasy.ttf", + NETSURF_FB_FONT_FANTASY); + if (fb_face == NULL) { + /* fantasy face unavailabe use the default */ + fb_faces[FB_FACE_FANTASY] = fb_faces[FB_FACE_SANS_SERIF]; + } else { + fb_faces[FB_FACE_FANTASY] = fb_face; + } + + + /* set the default render mode */ + if (nsoption_bool(fb_font_monochrome) == true) + ft_load_type = FT_LOAD_MONOCHROME; /* faster but less pretty */ + else + ft_load_type = 0; + + return true; +} + +/* exported interface documented in framebuffer/font.h */ +bool fb_font_finalise(void) +{ + int i, j; + + FTC_Manager_Done(ft_cmanager); + FT_Done_FreeType(library); + + for (i = 0; i < FB_FACE_COUNT; i++) { + if (fb_faces[i] == NULL) + continue; + + /* Unset any faces that duplicate this one */ + for (j = i + 1; j < FB_FACE_COUNT; j++) { + if (fb_faces[i] == fb_faces[j]) + fb_faces[j] = NULL; + } + + free(fb_faces[i]->fontfile); + free(fb_faces[i]); + + fb_faces[i] = NULL; + } + + return true; +} + +/** + * fill freetype scalar + */ +static void fb_fill_scalar(const plot_font_style_t *fstyle, FTC_Scaler srec) +{ + int selected_face = FB_FACE_DEFAULT; + + switch (fstyle->family) { + + case PLOT_FONT_FAMILY_SERIF: + if (fstyle->weight >= BOLD_WEIGHT) { + selected_face = FB_FACE_SERIF_BOLD; + } else { + selected_face = FB_FACE_SERIF; + } + break; + + case PLOT_FONT_FAMILY_MONOSPACE: + if (fstyle->weight >= BOLD_WEIGHT) { + selected_face = FB_FACE_MONOSPACE_BOLD; + } else { + selected_face = FB_FACE_MONOSPACE; + } + break; + + case PLOT_FONT_FAMILY_CURSIVE: + selected_face = FB_FACE_CURSIVE; + break; + + case PLOT_FONT_FAMILY_FANTASY: + selected_face = FB_FACE_FANTASY; + break; + + case PLOT_FONT_FAMILY_SANS_SERIF: + default: + if ((fstyle->flags & FONTF_ITALIC) || + (fstyle->flags & FONTF_OBLIQUE)) { + if (fstyle->weight >= BOLD_WEIGHT) { + selected_face = FB_FACE_SANS_SERIF_ITALIC_BOLD; + } else { + selected_face = FB_FACE_SANS_SERIF_ITALIC; + } + } else { + if (fstyle->weight >= BOLD_WEIGHT) { + selected_face = FB_FACE_SANS_SERIF_BOLD; + } else { + selected_face = FB_FACE_SANS_SERIF; + } + } + } + + srec->face_id = (FTC_FaceID)fb_faces[selected_face]; + + srec->width = srec->height = (fstyle->size * 64) / FONT_SIZE_SCALE; + srec->pixel = 0; + + srec->x_res = srec->y_res = browser_get_dpi(); +} + +/* exported interface documented in framebuffer/freetype_font.h */ +FT_Glyph fb_getglyph(const plot_font_style_t *fstyle, uint32_t ucs4) +{ + FT_UInt glyph_index; + FTC_ScalerRec srec; + FT_Glyph glyph; + FT_Error error; + fb_faceid_t *fb_face; + + fb_fill_scalar(fstyle, &srec); + + fb_face = (fb_faceid_t *)srec.face_id; + + glyph_index = FTC_CMapCache_Lookup(ft_cmap_cache, srec.face_id, + fb_face->cidx, ucs4); + + error = FTC_ImageCache_LookupScaler(ft_image_cache, + &srec, + FT_LOAD_RENDER | + FT_LOAD_FORCE_AUTOHINT | + ft_load_type, + glyph_index, + &glyph, + NULL); + if (error != 0) + return NULL; + + return glyph; +} + + +/* exported interface documented in framebuffer/freetype_font.h */ +nserror +fb_font_width(const plot_font_style_t *fstyle, + const char *string, size_t length, + int *width) +{ + uint32_t ucs4; + size_t nxtchr = 0; + FT_Glyph glyph; + + *width = 0; + while (nxtchr < length) { + ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr); + nxtchr = utf8_next(string, length, nxtchr); + + glyph = fb_getglyph(fstyle, ucs4); + if (glyph == NULL) + continue; + + *width += glyph->advance.x >> 16; + } + return NSERROR_OK; +} + + +/* exported interface documented in framebuffer/freetype_font.h */ +nserror +fb_font_position(const plot_font_style_t *fstyle, + const char *string, size_t length, + int x, size_t *char_offset, int *actual_x) +{ + uint32_t ucs4; + size_t nxtchr = 0; + FT_Glyph glyph; + int prev_x = 0; + + *actual_x = 0; + while (nxtchr < length) { + ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr); + + glyph = fb_getglyph(fstyle, ucs4); + if (glyph == NULL) + continue; + + *actual_x += glyph->advance.x >> 16; + if (*actual_x > x) + break; + + prev_x = *actual_x; + nxtchr = utf8_next(string, length, nxtchr); + } + + /* choose nearest of previous and last x */ + if (abs(*actual_x - x) > abs(prev_x - x)) + *actual_x = prev_x; + + *char_offset = nxtchr; + return NSERROR_OK; +} + + +/** + * Find where to split a string to make it fit a width. + * + * \param fstyle style for this text + * \param string UTF-8 string to measure + * \param length length of string, in bytes + * \param x width available + * \param char_offset updated to offset in string of actual_x, [1..length] + * \param actual_x updated to x coordinate of character closest to x + * \return true on success, false on error and error reported + * + * On exit, char_offset indicates first character after split point. + * + * Note: char_offset of 0 should never be returned. + * + * Returns: + * char_offset giving split point closest to x, where actual_x <= x + * else + * char_offset giving split point closest to x, where actual_x > x + * + * Returning char_offset == length means no split possible + */ +static nserror +fb_font_split(const plot_font_style_t *fstyle, + const char *string, size_t length, + int x, size_t *char_offset, int *actual_x) +{ + uint32_t ucs4; + size_t nxtchr = 0; + int last_space_x = 0; + int last_space_idx = 0; + FT_Glyph glyph; + + *actual_x = 0; + while (nxtchr < length) { + ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr); + + glyph = fb_getglyph(fstyle, ucs4); + if (glyph == NULL) + continue; + + if (ucs4 == 0x20) { + last_space_x = *actual_x; + last_space_idx = nxtchr; + } + + *actual_x += glyph->advance.x >> 16; + if (*actual_x > x && last_space_idx != 0) { + /* string has exceeded available width and we've + * found a space; return previous space */ + *actual_x = last_space_x; + *char_offset = last_space_idx; + return NSERROR_OK; + } + + nxtchr = utf8_next(string, length, nxtchr); + } + + *char_offset = nxtchr; + + return NSERROR_OK; +} + +static struct gui_layout_table layout_table = { + .width = fb_font_width, + .position = fb_font_position, + .split = fb_font_split, +}; + +struct gui_layout_table *framebuffer_layout_table = &layout_table; + + +struct gui_utf8_table *framebuffer_utf8_table = NULL; + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/kolibrios/fb/font_freetype.h b/frontends/kolibrios/fb/font_freetype.h new file mode 100644 index 000000000..cbc6d82c8 --- /dev/null +++ b/frontends/kolibrios/fb/font_freetype.h @@ -0,0 +1,30 @@ +/* + * Copyright 2008 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NETSURF_FB_FONT_FREETYPE_H +#define NETSURF_FB_FONT_FREETYPE_H + +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H + +extern int ft_load_type; + +FT_Glyph fb_getglyph(const plot_font_style_t *fstyle, uint32_t ucs4); + +#endif /* NETSURF_FB_FONT_FREETYPE_H */ diff --git a/frontends/kolibrios/fb/font_internal.c b/frontends/kolibrios/fb/font_internal.c new file mode 100644 index 000000000..3b8a1c43f --- /dev/null +++ b/frontends/kolibrios/fb/font_internal.c @@ -0,0 +1,492 @@ +/* + * Copyright 2005 James Bursa + * 2008 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf 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 . + */ + +#include +#include +#include +#include + +#include "utils/nsoption.h" +#include "utils/utf8.h" +#include "netsurf/utf8.h" +#include "netsurf/layout.h" +#include "netsurf/plot_style.h" + +#include "framebuffer/gui.h" +#include "framebuffer/font.h" + +#include + +#define GLYPH_LEN 16 + +uint8_t code_point[GLYPH_LEN]; +uint8_t glyph_x2[GLYPH_LEN * 4]; + +#define SEVEN_SET ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | \ + (1 << 4) | (1 << 5) | (1 << 6)) + +#define THREE_SSS ((1 << 0) | (1 << 1) | (1 << 2)) +#define THREE_S_S ((1 << 0) | (1 << 2)) +#define THREE__SS ((1 << 0) | (1 << 1) ) +#define THREE_SS_ ( (1 << 1) | (1 << 2)) +#define THREE_S__ (1 << 2) +#define THREE__S_ (1 << 1) +#define THREE___S (1 << 0) + +uint8_t frag[16][5] = { + { THREE_SSS, + THREE_S_S, + THREE_S_S, + THREE_S_S, + THREE_SSS }, + + { THREE__S_, + THREE_SS_, + THREE__S_, + THREE__S_, + THREE_SSS }, + + { THREE_SS_, + THREE___S, + THREE__S_, + THREE_S__, + THREE_SSS }, + + { THREE_SS_, + THREE___S, + THREE_SS_, + THREE___S, + THREE_SS_ }, + + { THREE_S_S, + THREE_S_S, + THREE_SSS, + THREE___S, + THREE___S }, + + { THREE_SSS, + THREE_S__, + THREE_SSS, + THREE___S, + THREE_SSS }, + + { THREE__SS, + THREE_S__, + THREE_SSS, + THREE_S_S, + THREE_SSS }, + + { THREE_SSS, + THREE___S, + THREE__S_, + THREE__S_, + THREE__S_ }, + + { THREE_SSS, + THREE_S_S, + THREE_SSS, + THREE_S_S, + THREE_SSS }, + + { THREE_SSS, + THREE_S_S, + THREE_SSS, + THREE___S, + THREE___S }, + + { THREE__S_, + THREE_S_S, + THREE_SSS, + THREE_S_S, + THREE_S_S }, + + { THREE_SS_, + THREE_S_S, + THREE_SS_, + THREE_S_S, + THREE_SS_ }, + + { THREE__S_, + THREE_S_S, + THREE_S__, + THREE_S_S, + THREE__S_ }, + + { THREE_SS_, + THREE_S_S, + THREE_S_S, + THREE_S_S, + THREE_SS_ }, + + { THREE_SSS, + THREE_S__, + THREE_SS_, + THREE_S__, + THREE_SSS }, + + { THREE_SSS, + THREE_S__, + THREE_SS_, + THREE_S__, + THREE_S__ } +}; + +static uint8_t * get_codepoint(uint32_t id, bool italic) +{ + int shift = 0; + int l; + int r; + + if (!italic) + shift = 1; + + l = (id >> 12); + r = 0xf & (id >> 8); + + code_point[ 0] = SEVEN_SET << shift; + + code_point[ 2] = (frag[l][0] << (4 + shift)) | (frag[r][0] << shift); + code_point[ 3] = (frag[l][1] << (4 + shift)) | (frag[r][1] << shift); + code_point[ 4] = (frag[l][2] << (4 + shift)) | (frag[r][2] << shift); + code_point[ 5] = (frag[l][3] << (4 + shift)) | (frag[r][3] << shift); + code_point[ 6] = (frag[l][4] << (4 + shift)) | (frag[r][4] << shift); + + shift = 1; + + l = 0xf & (id >> 4); + r = 0xf & id; + + code_point[ 8] = (frag[l][0] << (4 + shift)) | (frag[r][0] << shift); + code_point[ 9] = (frag[l][1] << (4 + shift)) | (frag[r][1] << shift); + code_point[10] = (frag[l][2] << (4 + shift)) | (frag[r][2] << shift); + code_point[11] = (frag[l][3] << (4 + shift)) | (frag[r][3] << shift); + code_point[12] = (frag[l][4] << (4 + shift)) | (frag[r][4] << shift); + + code_point[14] = SEVEN_SET << shift; + + return (uint8_t *)code_point; +} + + +bool fb_font_init(void) +{ + return true; +} + +bool fb_font_finalise(void) +{ + return true; +} + +enum fb_font_style +fb_get_font_style(const plot_font_style_t *fstyle) +{ + enum fb_font_style style = FB_REGULAR; + + if (fstyle->weight >= 700) + style |= FB_BOLD; + if ((fstyle->flags & FONTF_ITALIC) || (fstyle->flags & FONTF_OBLIQUE)) + style |= FB_ITALIC; + + return style; +} + +int +fb_get_font_size(const plot_font_style_t *fstyle) +{ + int size = fstyle->size * 10 / + (((nsoption_int(font_min_size) * 3 + + nsoption_int(font_size)) / 4) * FONT_SIZE_SCALE); + if (size > 2) + size = 2; + else if (size <= 0) + size = 1; + + return size; +} + +/** Lookup table to scale 4 bits to 8 bits, so e.g. 0101 --> 00110011 */ +const uint8_t glyph_lut[16] = { + 0x00, 0x03, 0x0c, 0x0f, + 0x30, 0x33, 0x3c, 0x3f, + 0xc0, 0xc3, 0xcc, 0xcf, + 0xf0, 0xf3, 0xfc, 0xff +}; + +static const uint8_t * +glyph_scale_2(const uint8_t *glyph_data) +{ + const uint8_t *glyph_max = glyph_data + GLYPH_LEN; + uint8_t *pos = glyph_x2; + + do { + *pos++ = glyph_lut[*glyph_data >> 4]; + *pos++ = glyph_lut[*glyph_data & 0xf]; + *pos++ = glyph_lut[*glyph_data >> 4]; + *pos++ = glyph_lut[*glyph_data & 0xf]; + } while (++glyph_data < glyph_max); + + return glyph_x2; +} + +const uint8_t * +fb_get_glyph(uint32_t ucs4, enum fb_font_style style, int scale) +{ + const uint8_t *glyph_data; + unsigned int section; + unsigned int offset; + uint16_t g_offset; + + /* Internal font has no glyphs beyond U+FFFF and there isn't + * space to render a >4 digit codepoint; just show replacement + * character. */ + if (ucs4 > 0xffff) + ucs4 = 0xfffd; + + switch (style) { + case FB_BOLD_ITALIC: + section = fb_bold_italic_section_table[ucs4 / 256]; + if (section != 0 || ucs4 / 256 == 0) { + offset = section * 256 + (ucs4 & 0xff); + g_offset = fb_bold_italic_sections[offset] * 16; + if (g_offset != 0) { + glyph_data = &font_glyph_data[g_offset]; + break; + } + } + case FB_BOLD: + section = fb_bold_section_table[ucs4 / 256]; + if (section != 0 || ucs4 / 256 == 0) { + offset = section * 256 + (ucs4 & 0xff); + g_offset = fb_bold_sections[offset] * 16; + if (g_offset != 0) { + glyph_data = &font_glyph_data[g_offset]; + break; + } + } + case FB_ITALIC: + section = fb_italic_section_table[ucs4 / 256]; + if (section != 0 || ucs4 / 256 == 0) { + offset = section * 256 + (ucs4 & 0xff); + g_offset = fb_italic_sections[offset] * 16; + if (g_offset != 0) { + glyph_data = &font_glyph_data[g_offset]; + break; + } + } + case FB_REGULAR: + section = fb_regular_section_table[ucs4 / 256]; + if (section != 0 || ucs4 / 256 == 0) { + offset = section * 256 + (ucs4 & 0xff); + g_offset = fb_regular_sections[offset] * 16; + if (g_offset != 0) { + glyph_data = &font_glyph_data[g_offset]; + break; + } + } + default: + glyph_data = get_codepoint(ucs4, style & FB_ITALIC); + break; + } + + switch (scale) { + case 1: + break; + case 2: + glyph_data = glyph_scale_2(glyph_data); + break; + default: + assert(scale >= 1 && scale <= 2); + break; + } + + return glyph_data; +} + +static nserror utf8_to_local(const char *string, + size_t len, + char **result) +{ + return utf8_to_enc(string, "CP1252", len, result); + +} + +static nserror utf8_from_local(const char *string, + size_t len, + char **result) +{ + *result = malloc(len + 1); + if (*result == NULL) { + return NSERROR_NOMEM; + } + + memcpy(*result, string, len); + + (*result)[len] = '\0'; + + return NSERROR_OK; +} + + +/* exported interface documented in framebuffer/freetype_font.h */ +nserror +fb_font_width(const plot_font_style_t *fstyle, + const char *string, + size_t length, + int *width) +{ + size_t nxtchr = 0; + + *width = 0; + while (nxtchr < length) { + uint32_t ucs4; + ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr); + if (codepoint_displayable(ucs4)) { + *width += FB_FONT_WIDTH; + } + + nxtchr = utf8_next(string, length, nxtchr); + } + + *width *= fb_get_font_size(fstyle); + return NSERROR_OK; +} + + +/* exported interface documented in framebuffer/freetype_font.h */ +nserror +fb_font_position(const plot_font_style_t *fstyle, + const char *string, + size_t length, + int x, + size_t *char_offset, + int *actual_x) +{ + const int width = fb_get_font_size(fstyle) * FB_FONT_WIDTH; + size_t nxtchr = 0; + int x_pos = 0; + + while (nxtchr < length) { + uint32_t ucs4; + if (abs(x_pos - x) <= (width / 2)) + break; + + ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr); + if (codepoint_displayable(ucs4)) { + x_pos += width; + } + + nxtchr = utf8_next(string, length, nxtchr); + } + + *actual_x = x_pos; + + *char_offset = nxtchr; + return NSERROR_OK; +} + + +/** + * Find where to split a string to make it fit a width. + * + * \param fstyle style for this text + * \param string UTF-8 string to measure + * \param length length of string, in bytes + * \param x width available + * \param char_offset updated to offset in string of actual_x, [1..length] + * \param actual_x updated to x coordinate of character closest to x + * \return true on success, false on error and error reported + * + * On exit, char_offset indicates first character after split point. + * + * Note: char_offset of 0 should never be returned. + * + * Returns: + * char_offset giving split point closest to x, where actual_x <= x + * else + * char_offset giving split point closest to x, where actual_x > x + * + * Returning char_offset == length means no split possible + */ +static nserror +fb_font_split(const plot_font_style_t *fstyle, + const char *string, + size_t length, + int x, + size_t *char_offset, + int *actual_x) +{ + const int width = fb_get_font_size(fstyle) * FB_FONT_WIDTH; + size_t nxtchr = 0; + int last_space_x = 0; + int last_space_idx = 0; + + *actual_x = 0; + while (nxtchr < length) { + uint32_t ucs4; + + if (string[nxtchr] == ' ') { + last_space_x = *actual_x; + last_space_idx = nxtchr; + } + + ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr); + if (codepoint_displayable(ucs4)) { + *actual_x += width; + } + + if (*actual_x > x && last_space_idx != 0) { + /* string has exceeded available width and we've + * found a space; return previous space */ + *actual_x = last_space_x; + *char_offset = last_space_idx; + return NSERROR_OK; + } + + nxtchr = utf8_next(string, length, nxtchr); + } + + *char_offset = nxtchr; + + return NSERROR_OK; +} + + +static struct gui_layout_table layout_table = { + .width = fb_font_width, + .position = fb_font_position, + .split = fb_font_split, +}; + +struct gui_layout_table *framebuffer_layout_table = &layout_table; + + +static struct gui_utf8_table utf8_table = { + .utf8_to_local = utf8_to_local, + .local_to_utf8 = utf8_from_local, +}; + +struct gui_utf8_table *framebuffer_utf8_table = &utf8_table; + + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/kolibrios/fb/font_internal.h b/frontends/kolibrios/fb/font_internal.h new file mode 100644 index 000000000..f25df8de6 --- /dev/null +++ b/frontends/kolibrios/fb/font_internal.h @@ -0,0 +1,49 @@ +/* + * Copyright 2008 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NETSURF_FB_FONT_INTERNAL_H +#define NETSURF_FB_FONT_INTERNAL_H + +#include + +struct fb_font_desc { + const char *name; + int width, height, pitch; +}; + +#define FB_FONT_WIDTH 8 +#define FB_FONT_HEIGHT 16 +#define FB_FONT_PITCH 8 + +enum fb_font_style { + FB_REGULAR = 0, + FB_ITALIC = (1 << 0), + FB_BOLD = (1 << 1), + FB_BOLD_ITALIC = (FB_ITALIC | FB_BOLD) +}; + +enum fb_font_style fb_get_font_style(const plot_font_style_t *fstyle); +int fb_get_font_size(const plot_font_style_t *fstyle); + +#define codepoint_displayable(u) \ + (!(u >= 0x200b && u <= 0x200f)) + +const uint8_t *fb_get_glyph(uint32_t ucs4, enum fb_font_style style, int scale); + +#endif /* NETSURF_FB_FONT_INTERNAL_H */ + diff --git a/frontends/kolibrios/fb/framebuffer.c b/frontends/kolibrios/fb/framebuffer.c new file mode 100644 index 000000000..649862aa3 --- /dev/null +++ b/frontends/kolibrios/fb/framebuffer.c @@ -0,0 +1,651 @@ +/* + * Copyright 2008 Vincent Sanders + * + * Framebuffer interface + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf 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 . + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "utils/utils.h" +#include "utils/log.h" +#include "utils/utf8.h" +#include "netsurf/browser_window.h" +#include "netsurf/plotters.h" +#include "netsurf/bitmap.h" + +#include "framebuffer/gui.h" +#include "framebuffer/fbtk.h" +#include "framebuffer/framebuffer.h" +#include "framebuffer/font.h" +#include "framebuffer/bitmap.h" + +/* netsurf framebuffer library handle */ +static nsfb_t *nsfb; + + +/** + * \brief Sets a clip rectangle for subsequent plot operations. + * + * \param ctx The current redraw context. + * \param clip The rectangle to limit all subsequent plot + * operations within. + * \return NSERROR_OK on success else error code. + */ +static nserror +framebuffer_plot_clip(const struct redraw_context *ctx, const struct rect *clip) +{ + nsfb_bbox_t nsfb_clip; + nsfb_clip.x0 = clip->x0; + nsfb_clip.y0 = clip->y0; + nsfb_clip.x1 = clip->x1; + nsfb_clip.y1 = clip->y1; + + if (!nsfb_plot_set_clip(nsfb, &nsfb_clip)) { + return NSERROR_INVALID; + } + return NSERROR_OK; +} + + +/** + * Plots an arc + * + * plot an arc segment around (x,y), anticlockwise from angle1 + * to angle2. Angles are measured anticlockwise from + * horizontal, in degrees. + * + * \param ctx The current redraw context. + * \param style Style controlling the arc plot. + * \param x The x coordinate of the arc. + * \param y The y coordinate of the arc. + * \param radius The radius of the arc. + * \param angle1 The start angle of the arc. + * \param angle2 The finish angle of the arc. + * \return NSERROR_OK on success else error code. + */ +static nserror +framebuffer_plot_arc(const struct redraw_context *ctx, + const plot_style_t *style, + int x, int y, int radius, int angle1, int angle2) +{ + if (!nsfb_plot_arc(nsfb, x, y, radius, angle1, angle2, style->fill_colour)) { + return NSERROR_INVALID; + } + return NSERROR_OK; +} + + +/** + * Plots a circle + * + * Plot a circle centered on (x,y), which is optionally filled. + * + * \param ctx The current redraw context. + * \param style Style controlling the circle plot. + * \param x x coordinate of circle centre. + * \param y y coordinate of circle centre. + * \param radius circle radius. + * \return NSERROR_OK on success else error code. + */ +static nserror +framebuffer_plot_disc(const struct redraw_context *ctx, + const plot_style_t *style, + int x, int y, int radius) +{ + nsfb_bbox_t ellipse; + ellipse.x0 = x - radius; + ellipse.y0 = y - radius; + ellipse.x1 = x + radius; + ellipse.y1 = y + radius; + + if (style->fill_type != PLOT_OP_TYPE_NONE) { + nsfb_plot_ellipse_fill(nsfb, &ellipse, style->fill_colour); + } + + if (style->stroke_type != PLOT_OP_TYPE_NONE) { + nsfb_plot_ellipse(nsfb, &ellipse, style->stroke_colour); + } + return NSERROR_OK; +} + + +/** + * Plots a line + * + * plot a line from (x0,y0) to (x1,y1). Coordinates are at + * centre of line width/thickness. + * + * \param ctx The current redraw context. + * \param style Style controlling the line plot. + * \param line A rectangle defining the line to be drawn + * \return NSERROR_OK on success else error code. + */ +static nserror +framebuffer_plot_line(const struct redraw_context *ctx, + const plot_style_t *style, + const struct rect *line) +{ + nsfb_bbox_t rect; + nsfb_plot_pen_t pen; + + rect.x0 = line->x0; + rect.y0 = line->y0; + rect.x1 = line->x1; + rect.y1 = line->y1; + + if (style->stroke_type != PLOT_OP_TYPE_NONE) { + + if (style->stroke_type == PLOT_OP_TYPE_DOT) { + pen.stroke_type = NFSB_PLOT_OPTYPE_PATTERN; + pen.stroke_pattern = 0xAAAAAAAA; + } else if (style->stroke_type == PLOT_OP_TYPE_DASH) { + pen.stroke_type = NFSB_PLOT_OPTYPE_PATTERN; + pen.stroke_pattern = 0xF0F0F0F0; + } else { + pen.stroke_type = NFSB_PLOT_OPTYPE_SOLID; + } + + pen.stroke_colour = style->stroke_colour; + pen.stroke_width = style->stroke_width; + nsfb_plot_line(nsfb, &rect, &pen); + } + + return NSERROR_OK; +} + + +/** + * Plots a rectangle. + * + * The rectangle can be filled an outline or both controlled + * by the plot style The line can be solid, dotted or + * dashed. Top left corner at (x0,y0) and rectangle has given + * width and height. + * + * \param ctx The current redraw context. + * \param style Style controlling the rectangle plot. + * \param nsrect A rectangle defining the line to be drawn + * \return NSERROR_OK on success else error code. + */ +static nserror +framebuffer_plot_rectangle(const struct redraw_context *ctx, + const plot_style_t *style, + const struct rect *nsrect) +{ + nsfb_bbox_t rect; + bool dotted = false; + bool dashed = false; + + rect.x0 = nsrect->x0; + rect.y0 = nsrect->y0; + rect.x1 = nsrect->x1; + rect.y1 = nsrect->y1; + + if (style->fill_type != PLOT_OP_TYPE_NONE) { + nsfb_plot_rectangle_fill(nsfb, &rect, style->fill_colour); + } + + if (style->stroke_type != PLOT_OP_TYPE_NONE) { + if (style->stroke_type == PLOT_OP_TYPE_DOT) { + dotted = true; + } + + if (style->stroke_type == PLOT_OP_TYPE_DASH) { + dashed = true; + } + + nsfb_plot_rectangle(nsfb, &rect, style->stroke_width, style->stroke_colour, dotted, dashed); + } + return NSERROR_OK; +} + + +/** + * Plot a polygon + * + * Plots a filled polygon with straight lines between + * points. The lines around the edge of the ploygon are not + * plotted. The polygon is filled with the non-zero winding + * rule. + * + * \param ctx The current redraw context. + * \param style Style controlling the polygon plot. + * \param p verticies of polygon + * \param n number of verticies. + * \return NSERROR_OK on success else error code. + */ +static nserror +framebuffer_plot_polygon(const struct redraw_context *ctx, + const plot_style_t *style, + const int *p, + unsigned int n) +{ + if (!nsfb_plot_polygon(nsfb, p, n, style->fill_colour)) { + return NSERROR_INVALID; + } + return NSERROR_OK; +} + + +/** + * Plots a path. + * + * Path plot consisting of cubic Bezier curves. Line and fill colour is + * controlled by the plot style. + * + * \param ctx The current redraw context. + * \param pstyle Style controlling the path plot. + * \param p elements of path + * \param n nunber of elements on path + * \param width The width of the path + * \param transform A transform to apply to the path. + * \return NSERROR_OK on success else error code. + */ +static nserror +framebuffer_plot_path(const struct redraw_context *ctx, + const plot_style_t *pstyle, + const float *p, + unsigned int n, + float width, + const float transform[6]) +{ + LOG("path unimplemented"); + return NSERROR_OK; +} + + +/** + * Plot a bitmap + * + * Tiled plot of a bitmap image. (x,y) gives the top left + * coordinate of an explicitly placed tile. From this tile the + * image can repeat in all four directions -- up, down, left + * and right -- to the extents given by the current clip + * rectangle. + * + * The bitmap_flags say whether to tile in the x and y + * directions. If not tiling in x or y directions, the single + * image is plotted. The width and height give the dimensions + * the image is to be scaled to. + * + * \param ctx The current redraw context. + * \param bitmap The bitmap to plot + * \param x The x coordinate to plot the bitmap + * \param y The y coordiante to plot the bitmap + * \param width The width of area to plot the bitmap into + * \param height The height of area to plot the bitmap into + * \param bg the background colour to alpha blend into + * \param flags the flags controlling the type of plot operation + * \return NSERROR_OK on success else error code. + */ +static nserror +framebuffer_plot_bitmap(const struct redraw_context *ctx, + struct bitmap *bitmap, + int x, int y, + int width, + int height, + colour bg, + bitmap_flags_t flags) +{ + nsfb_bbox_t loc; + nsfb_bbox_t clipbox; + bool repeat_x = (flags & BITMAPF_REPEAT_X); + bool repeat_y = (flags & BITMAPF_REPEAT_Y); + int bmwidth; + int bmheight; + int bmstride; + enum nsfb_format_e bmformat; + unsigned char *bmptr; + nsfb_t *bm = (nsfb_t *)bitmap; + + /* x and y define coordinate of top left of of the initial explicitly + * placed tile. The width and height are the image scaling and the + * bounding box defines the extent of the repeat (which may go in all + * four directions from the initial tile). + */ + + if (!(repeat_x || repeat_y)) { + /* Not repeating at all, so just plot it */ + loc.x0 = x; + loc.y0 = y; + loc.x1 = loc.x0 + width; + loc.y1 = loc.y0 + height; + + return nsfb_plot_copy(bm, NULL, nsfb, &loc); + } + + nsfb_plot_get_clip(nsfb, &clipbox); + nsfb_get_geometry(bm, &bmwidth, &bmheight, &bmformat); + nsfb_get_buffer(bm, &bmptr, &bmstride); + + /* Optimise tiled plots of 1x1 bitmaps by replacing with a flat fill + * of the area. Can only be done when image is fully opaque. */ + if ((bmwidth == 1) && (bmheight == 1)) { + if ((*(nsfb_colour_t *)bmptr & 0xff000000) != 0) { + if (!nsfb_plot_rectangle_fill(nsfb, &clipbox, + *(nsfb_colour_t *)bmptr)) { + return NSERROR_INVALID; + } + return NSERROR_OK; + } + } + + /* Optimise tiled plots of bitmaps scaled to 1x1 by replacing with + * a flat fill of the area. Can only be done when image is fully + * opaque. */ + if ((width == 1) && (height == 1)) { + if (framebuffer_bitmap_get_opaque(bm)) { + /** TODO: Currently using top left pixel. Maybe centre + * pixel or average value would be better. */ + if (!nsfb_plot_rectangle_fill(nsfb, &clipbox, + *(nsfb_colour_t *)bmptr)) { + return NSERROR_INVALID; + } + return NSERROR_OK; + } + } + + /* get left most tile position */ + if (repeat_x) { + for (; x > clipbox.x0; x -= width); + } + + /* get top most tile position */ + if (repeat_y) { + for (; y > clipbox.y0; y -= height); + } + + /* set up top left tile location */ + loc.x0 = x; + loc.y0 = y; + loc.x1 = loc.x0 + width; + loc.y1 = loc.y0 + height; + + /* plot tiling across and down to extents */ + nsfb_plot_bitmap_tiles(nsfb, &loc, + repeat_x ? ((clipbox.x1 - x) + width - 1) / width : 1, + repeat_y ? ((clipbox.y1 - y) + height - 1) / height : 1, + (nsfb_colour_t *)bmptr, bmwidth, bmheight, + bmstride * 8 / 32, bmformat == NSFB_FMT_ABGR8888); + + return NSERROR_OK; +} + + +#ifdef FB_USE_FREETYPE +/** + * Text plotting. + * + * \param ctx The current redraw context. + * \param fstyle plot style for this text + * \param x x coordinate + * \param y y coordinate + * \param text UTF-8 string to plot + * \param length length of string, in bytes + * \return NSERROR_OK on success else error code. + */ +static nserror +framebuffer_plot_text(const struct redraw_context *ctx, + const struct plot_font_style *fstyle, + int x, + int y, + const char *text, + size_t length) +{ + uint32_t ucs4; + size_t nxtchr = 0; + FT_Glyph glyph; + FT_BitmapGlyph bglyph; + nsfb_bbox_t loc; + + while (nxtchr < length) { + ucs4 = utf8_to_ucs4(text + nxtchr, length - nxtchr); + nxtchr = utf8_next(text, length, nxtchr); + + glyph = fb_getglyph(fstyle, ucs4); + if (glyph == NULL) + continue; + + if (glyph->format == FT_GLYPH_FORMAT_BITMAP) { + bglyph = (FT_BitmapGlyph)glyph; + + loc.x0 = x + bglyph->left; + loc.y0 = y - bglyph->top; + loc.x1 = loc.x0 + bglyph->bitmap.width; + loc.y1 = loc.y0 + bglyph->bitmap.rows; + + /* now, draw to our target surface */ + if (bglyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { + nsfb_plot_glyph1(nsfb, + &loc, + bglyph->bitmap.buffer, + bglyph->bitmap.pitch, + fstyle->foreground); + } else { + nsfb_plot_glyph8(nsfb, + &loc, + bglyph->bitmap.buffer, + bglyph->bitmap.pitch, + fstyle->foreground); + } + } + x += glyph->advance.x >> 16; + + } + return NSERROR_OK; + +} + +#else + +/** + * Text plotting. + * + * \param ctx The current redraw context. + * \param fstyle plot style for this text + * \param x x coordinate + * \param y y coordinate + * \param text UTF-8 string to plot + * \param length length of string, in bytes + * \return NSERROR_OK on success else error code. + */ +static nserror +framebuffer_plot_text(const struct redraw_context *ctx, + const struct plot_font_style *fstyle, + int x, + int y, + const char *text, + size_t length) +{ + enum fb_font_style style = fb_get_font_style(fstyle); + int size = fb_get_font_size(fstyle); + const uint8_t *chrp; + size_t nxtchr = 0; + nsfb_bbox_t loc; + uint32_t ucs4; + int p = FB_FONT_PITCH * size; + int w = FB_FONT_WIDTH * size; + int h = FB_FONT_HEIGHT * size; + + y -= ((h * 3) / 4); + /* the coord is the bottom-left of the pixels offset by 1 to make + * it work since fb coords are the top-left of pixels */ + y += 1; + + while (nxtchr < length) { + ucs4 = utf8_to_ucs4(text + nxtchr, length - nxtchr); + nxtchr = utf8_next(text, length, nxtchr); + + if (!codepoint_displayable(ucs4)) + continue; + + loc.x0 = x; + loc.y0 = y; + loc.x1 = loc.x0 + w; + loc.y1 = loc.y0 + h; + + chrp = fb_get_glyph(ucs4, style, size); + nsfb_plot_glyph1(nsfb, &loc, chrp, p, fstyle->foreground); + + x += w; + + } + + return NSERROR_OK; +} +#endif + + +/** framebuffer plot operation table */ +const struct plotter_table fb_plotters = { + .clip = framebuffer_plot_clip, + .arc = framebuffer_plot_arc, + .disc = framebuffer_plot_disc, + .line = framebuffer_plot_line, + .rectangle = framebuffer_plot_rectangle, + .polygon = framebuffer_plot_polygon, + .path = framebuffer_plot_path, + .bitmap = framebuffer_plot_bitmap, + .text = framebuffer_plot_text, + .option_knockout = true, +}; + + +static bool framebuffer_format_from_bpp(int bpp, enum nsfb_format_e *fmt) +{ + switch (bpp) { + case 32: + *fmt = NSFB_FMT_XRGB8888; + break; + + case 24: + *fmt = NSFB_FMT_RGB888; + break; + + case 16: + *fmt = NSFB_FMT_RGB565; + break; + + case 8: + *fmt = NSFB_FMT_I8; + break; + + case 4: + *fmt = NSFB_FMT_I4; + break; + + case 1: + *fmt = NSFB_FMT_I1; + break; + + default: + LOG("Bad bits per pixel (%d)\n", bpp); + return false; + } + + return true; +} + + + +nsfb_t * +framebuffer_initialise(const char *fename, int width, int height, int bpp) +{ + enum nsfb_type_e fbtype; + enum nsfb_format_e fbfmt; + + /* bpp is a proxy for the framebuffer format */ + if (framebuffer_format_from_bpp(bpp, &fbfmt) == false) { + return NULL; + } + + fbtype = nsfb_type_from_name(fename); + if (fbtype == NSFB_SURFACE_NONE) { + LOG("The %s surface is not available from libnsfb\n", fename); + return NULL; + } + + nsfb = nsfb_new(fbtype); + if (nsfb == NULL) { + LOG("Unable to create %s fb surface\n", fename); + return NULL; + } + + if (nsfb_set_geometry(nsfb, width, height, fbfmt) == -1) { + LOG("Unable to set surface geometry\n"); + nsfb_free(nsfb); + return NULL; + } + + nsfb_cursor_init(nsfb); + + if (nsfb_init(nsfb) == -1) { + LOG("Unable to initialise nsfb surface\n"); + nsfb_free(nsfb); + return NULL; + } + + return nsfb; + +} + +bool +framebuffer_resize(nsfb_t *nsfb, int width, int height, int bpp) +{ + enum nsfb_format_e fbfmt; + + /* bpp is a proxy for the framebuffer format */ + if (framebuffer_format_from_bpp(bpp, &fbfmt) == false) { + return false; + } + + if (nsfb_set_geometry(nsfb, width, height, fbfmt) == -1) { + LOG("Unable to change surface geometry\n"); + return false; + } + + return true; + +} + +void +framebuffer_finalise(void) +{ + nsfb_free(nsfb); +} + +bool +framebuffer_set_cursor(struct fbtk_bitmap *bm) +{ + return nsfb_cursor_set(nsfb, (nsfb_colour_t *)bm->pixdata, bm->width, bm->height, bm->width, bm->hot_x, bm->hot_y); +} + +nsfb_t *framebuffer_set_surface(nsfb_t *new_nsfb) +{ + nsfb_t *old_nsfb; + old_nsfb = nsfb; + nsfb = new_nsfb; + return old_nsfb; +} diff --git a/frontends/kolibrios/fb/framebuffer.h b/frontends/kolibrios/fb/framebuffer.h new file mode 100644 index 000000000..d99049f52 --- /dev/null +++ b/frontends/kolibrios/fb/framebuffer.h @@ -0,0 +1,40 @@ +/* + * Copyright 2008 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf 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 . + */ + +/** + * \file + * framebuffer interface. + */ + +#ifndef NETSURF_FB_FRAMEBUFFER_H +#define NETSURF_FB_FRAMEBUFFER_H + +extern const struct plotter_table fb_plotters; + +nsfb_t *framebuffer_initialise(const char *fename, int width, int height, int bpp); +bool framebuffer_resize(nsfb_t *nsfb, int width, int height, int bpp); +void framebuffer_finalise(void); +bool framebuffer_set_cursor(struct fbtk_bitmap *bm); + +/** Set framebuffer surface to render into + * + * @return return old surface + */ +nsfb_t *framebuffer_set_surface(nsfb_t *new_nsfb); + +#endif diff --git a/frontends/kolibrios/fb/gui.c b/frontends/kolibrios/fb/gui.c new file mode 100644 index 000000000..4d4c7334f --- /dev/null +++ b/frontends/kolibrios/fb/gui.c @@ -0,0 +1,2226 @@ +/* + * Copyright 2008, 2014 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "utils/utils.h" +#include "utils/nsoption.h" +#include "utils/filepath.h" +#include "utils/log.h" +#include "utils/messages.h" +#include "netsurf/browser_window.h" +#include "netsurf/keypress.h" +#include "desktop/browser_history.h" +#include "netsurf/plotters.h" +#include "netsurf/window.h" +#include "netsurf/misc.h" +#include "netsurf/netsurf.h" +#include "netsurf/cookie_db.h" +#include "content/fetch.h" + +#include "framebuffer/gui.h" +#include "framebuffer/fbtk.h" +#include "framebuffer/framebuffer.h" +#include "framebuffer/schedule.h" +#include "framebuffer/findfile.h" +#include "framebuffer/image_data.h" +#include "framebuffer/font.h" +#include "framebuffer/clipboard.h" +#include "framebuffer/fetch.h" +#include "framebuffer/bitmap.h" + + +#define NSFB_TOOLBAR_DEFAULT_LAYOUT "blfsrutc" + +fbtk_widget_t *fbtk; + +static bool fb_complete = false; + +struct gui_window *input_window = NULL; +struct gui_window *search_current_window; +struct gui_window *window_list = NULL; + +/* private data for browser user widget */ +struct browser_widget_s { + struct browser_window *bw; /**< The browser window connected to this gui window */ + int scrollx, scrolly; /**< scroll offsets. */ + + /* Pending window redraw state. */ + bool redraw_required; /**< flag indicating the foreground loop + * needs to redraw the browser widget. + */ + bbox_t redraw_box; /**< Area requiring redraw. */ + bool pan_required; /**< flag indicating the foreground loop + * needs to pan the window. + */ + int panx, pany; /**< Panning required. */ +}; + +static struct gui_drag { + enum state { + GUI_DRAG_NONE, + GUI_DRAG_PRESSED, + GUI_DRAG_DRAG + } state; + int button; + int x; + int y; + bool grabbed_pointer; +} gui_drag; + + +/** + * Cause an abnormal program termination. + * + * \note This never returns and is intended to terminate without any cleanup. + * + * \param error The message to display to the user. + */ +static void die(const char *error) +{ + fprintf(stderr, "%s\n", error); + exit(1); +} + + +/** + * Warn the user of an event. + * + * \param[in] warning A warning looked up in the message translation table + * \param[in] detail Additional text to be displayed or NULL. + * \return NSERROR_OK on success or error code if there was a + * faliure displaying the message to the user. + */ +static nserror fb_warn_user(const char *warning, const char *detail) +{ + LOG("%s %s", warning, detail); + return NSERROR_OK; +} + +/* queue a redraw operation, co-ordinates are relative to the window */ +static void +fb_queue_redraw(struct fbtk_widget_s *widget, int x0, int y0, int x1, int y1) +{ + struct browser_widget_s *bwidget = fbtk_get_userpw(widget); + + bwidget->redraw_box.x0 = min(bwidget->redraw_box.x0, x0); + bwidget->redraw_box.y0 = min(bwidget->redraw_box.y0, y0); + bwidget->redraw_box.x1 = max(bwidget->redraw_box.x1, x1); + bwidget->redraw_box.y1 = max(bwidget->redraw_box.y1, y1); + + if (fbtk_clip_to_widget(widget, &bwidget->redraw_box)) { + bwidget->redraw_required = true; + fbtk_request_redraw(widget); + } else { + bwidget->redraw_box.y0 = bwidget->redraw_box.x0 = INT_MAX; + bwidget->redraw_box.y1 = bwidget->redraw_box.x1 = -(INT_MAX); + bwidget->redraw_required = false; + } +} + +/* queue a window scroll */ +static void +widget_scroll_y(struct gui_window *gw, int y, bool abs) +{ + struct browser_widget_s *bwidget = fbtk_get_userpw(gw->browser); + int content_width, content_height; + int height; + + LOG("window scroll"); + if (abs) { + bwidget->pany = y - bwidget->scrolly; + } else { + bwidget->pany += y; + } + + browser_window_get_extents(gw->bw, true, + &content_width, &content_height); + + height = fbtk_get_height(gw->browser); + + /* dont pan off the top */ + if ((bwidget->scrolly + bwidget->pany) < 0) + bwidget->pany = -bwidget->scrolly; + + /* do not pan off the bottom of the content */ + if ((bwidget->scrolly + bwidget->pany) > (content_height - height)) + bwidget->pany = (content_height - height) - bwidget->scrolly; + + if (bwidget->pany == 0) + return; + + bwidget->pan_required = true; + + fbtk_request_redraw(gw->browser); + + fbtk_set_scroll_position(gw->vscroll, bwidget->scrolly + bwidget->pany); +} + +/* queue a window scroll */ +static void +widget_scroll_x(struct gui_window *gw, int x, bool abs) +{ + struct browser_widget_s *bwidget = fbtk_get_userpw(gw->browser); + int content_width, content_height; + int width; + + if (abs) { + bwidget->panx = x - bwidget->scrollx; + } else { + bwidget->panx += x; + } + + browser_window_get_extents(gw->bw, true, + &content_width, &content_height); + + width = fbtk_get_width(gw->browser); + + /* dont pan off the left */ + if ((bwidget->scrollx + bwidget->panx) < 0) + bwidget->panx = - bwidget->scrollx; + + /* do not pan off the right of the content */ + if ((bwidget->scrollx + bwidget->panx) > (content_width - width)) + bwidget->panx = (content_width - width) - bwidget->scrollx; + + if (bwidget->panx == 0) + return; + + bwidget->pan_required = true; + + fbtk_request_redraw(gw->browser); + + fbtk_set_scroll_position(gw->hscroll, bwidget->scrollx + bwidget->panx); +} + +static void +fb_pan(fbtk_widget_t *widget, + struct browser_widget_s *bwidget, + struct browser_window *bw) +{ + int x; + int y; + int width; + int height; + nsfb_bbox_t srcbox; + nsfb_bbox_t dstbox; + + nsfb_t *nsfb = fbtk_get_nsfb(widget); + + height = fbtk_get_height(widget); + width = fbtk_get_width(widget); + + LOG("panning %d, %d", bwidget->panx, bwidget->pany); + + x = fbtk_get_absx(widget); + y = fbtk_get_absy(widget); + + /* if the pan exceeds the viewport size just redraw the whole area */ + if (bwidget->pany >= height || bwidget->pany <= -height || + bwidget->panx >= width || bwidget->panx <= -width) { + + bwidget->scrolly += bwidget->pany; + bwidget->scrollx += bwidget->panx; + fb_queue_redraw(widget, 0, 0, width, height); + + /* ensure we don't try to scroll again */ + bwidget->panx = 0; + bwidget->pany = 0; + bwidget->pan_required = false; + return; + } + + if (bwidget->pany < 0) { + /* pan up by less then viewport height */ + srcbox.x0 = x; + srcbox.y0 = y; + srcbox.x1 = srcbox.x0 + width; + srcbox.y1 = srcbox.y0 + height + bwidget->pany; + + dstbox.x0 = x; + dstbox.y0 = y - bwidget->pany; + dstbox.x1 = dstbox.x0 + width; + dstbox.y1 = dstbox.y0 + height + bwidget->pany; + + /* move part that remains visible up */ + nsfb_plot_copy(nsfb, &srcbox, nsfb, &dstbox); + + /* redraw newly exposed area */ + bwidget->scrolly += bwidget->pany; + fb_queue_redraw(widget, 0, 0, width, - bwidget->pany); + + } else if (bwidget->pany > 0) { + /* pan down by less then viewport height */ + srcbox.x0 = x; + srcbox.y0 = y + bwidget->pany; + srcbox.x1 = srcbox.x0 + width; + srcbox.y1 = srcbox.y0 + height - bwidget->pany; + + dstbox.x0 = x; + dstbox.y0 = y; + dstbox.x1 = dstbox.x0 + width; + dstbox.y1 = dstbox.y0 + height - bwidget->pany; + + /* move part that remains visible down */ + nsfb_plot_copy(nsfb, &srcbox, nsfb, &dstbox); + + /* redraw newly exposed area */ + bwidget->scrolly += bwidget->pany; + fb_queue_redraw(widget, 0, height - bwidget->pany, + width, height); + } + + if (bwidget->panx < 0) { + /* pan left by less then viewport width */ + srcbox.x0 = x; + srcbox.y0 = y; + srcbox.x1 = srcbox.x0 + width + bwidget->panx; + srcbox.y1 = srcbox.y0 + height; + + dstbox.x0 = x - bwidget->panx; + dstbox.y0 = y; + dstbox.x1 = dstbox.x0 + width + bwidget->panx; + dstbox.y1 = dstbox.y0 + height; + + /* move part that remains visible left */ + nsfb_plot_copy(nsfb, &srcbox, nsfb, &dstbox); + + /* redraw newly exposed area */ + bwidget->scrollx += bwidget->panx; + fb_queue_redraw(widget, 0, 0, -bwidget->panx, height); + + } else if (bwidget->panx > 0) { + /* pan right by less then viewport width */ + srcbox.x0 = x + bwidget->panx; + srcbox.y0 = y; + srcbox.x1 = srcbox.x0 + width - bwidget->panx; + srcbox.y1 = srcbox.y0 + height; + + dstbox.x0 = x; + dstbox.y0 = y; + dstbox.x1 = dstbox.x0 + width - bwidget->panx; + dstbox.y1 = dstbox.y0 + height; + + /* move part that remains visible right */ + nsfb_plot_copy(nsfb, &srcbox, nsfb, &dstbox); + + /* redraw newly exposed area */ + bwidget->scrollx += bwidget->panx; + fb_queue_redraw(widget, width - bwidget->panx, 0, + width, height); + } + + bwidget->pan_required = false; + bwidget->panx = 0; + bwidget->pany = 0; +} + +static void +fb_redraw(fbtk_widget_t *widget, + struct browser_widget_s *bwidget, + struct browser_window *bw) +{ + int x; + int y; + int caret_x, caret_y, caret_h; + struct rect clip; + struct redraw_context ctx = { + .interactive = true, + .background_images = true, + .plot = &fb_plotters + }; + nsfb_t *nsfb = fbtk_get_nsfb(widget); + float scale = browser_window_get_scale(bw); + + x = fbtk_get_absx(widget); + y = fbtk_get_absy(widget); + + /* adjust clipping co-ordinates according to window location */ + bwidget->redraw_box.y0 += y; + bwidget->redraw_box.y1 += y; + bwidget->redraw_box.x0 += x; + bwidget->redraw_box.x1 += x; + + nsfb_claim(nsfb, &bwidget->redraw_box); + + /* redraw bounding box is relative to window */ + clip.x0 = bwidget->redraw_box.x0; + clip.y0 = bwidget->redraw_box.y0; + clip.x1 = bwidget->redraw_box.x1; + clip.y1 = bwidget->redraw_box.y1; + + browser_window_redraw(bw, + (x - bwidget->scrollx) / scale, + (y - bwidget->scrolly) / scale, + &clip, &ctx); + + if (fbtk_get_caret(widget, &caret_x, &caret_y, &caret_h)) { + /* This widget has caret, so render it */ + nsfb_bbox_t line; + nsfb_plot_pen_t pen; + + line.x0 = x - bwidget->scrollx + caret_x; + line.y0 = y - bwidget->scrolly + caret_y; + line.x1 = x - bwidget->scrollx + caret_x; + line.y1 = y - bwidget->scrolly + caret_y + caret_h; + + pen.stroke_type = NFSB_PLOT_OPTYPE_SOLID; + pen.stroke_width = 1; + pen.stroke_colour = 0xFF0000FF; + + nsfb_plot_line(nsfb, &line, &pen); + } + + nsfb_update(fbtk_get_nsfb(widget), &bwidget->redraw_box); + + bwidget->redraw_box.y0 = bwidget->redraw_box.x0 = INT_MAX; + bwidget->redraw_box.y1 = bwidget->redraw_box.x1 = INT_MIN; + bwidget->redraw_required = false; +} + +static int +fb_browser_window_redraw(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + struct gui_window *gw = cbi->context; + struct browser_widget_s *bwidget; + + bwidget = fbtk_get_userpw(widget); + if (bwidget == NULL) { + LOG("browser widget from widget %p was null", widget); + return -1; + } + + if (bwidget->pan_required) { + fb_pan(widget, bwidget, gw->bw); + } + + if (bwidget->redraw_required) { + fb_redraw(widget, bwidget, gw->bw); + } else { + bwidget->redraw_box.x0 = 0; + bwidget->redraw_box.y0 = 0; + bwidget->redraw_box.x1 = fbtk_get_width(widget); + bwidget->redraw_box.y1 = fbtk_get_height(widget); + fb_redraw(widget, bwidget, gw->bw); + } + return 0; +} + +static int fb_browser_window_destroy(fbtk_widget_t *widget, + fbtk_callback_info *cbi) +{ + struct browser_widget_s *browser_widget; + + if (widget == NULL) { + return 0; + } + + /* Free private data */ + browser_widget = fbtk_get_userpw(widget); + free(browser_widget); + + return 0; +} + + +static const char *fename; +static int febpp; +static int fewidth; +static int feheight; +static const char *feurl; + +static bool +process_cmdline(int argc, char** argv) +{ + int opt; + int option_index; + static struct option long_options[] = { + {0, 0, 0, 0 } + }; /* no long options */ + + LOG("argc %d, argv %p", argc, argv); + + fename = "sdl"; + febpp = 32; + + fewidth = nsoption_int(window_width); + if (fewidth <= 0) { + fewidth = 800; + } + feheight = nsoption_int(window_height); + if (feheight <= 0) { + feheight = 600; + } + + if ((nsoption_charp(homepage_url) != NULL) && + (nsoption_charp(homepage_url)[0] != '\0')) { + feurl = nsoption_charp(homepage_url); + } else { + feurl = NETSURF_HOMEPAGE; + } + + while((opt = getopt_long(argc, argv, "f:b:w:h:", + long_options, &option_index)) != -1) { + switch (opt) { + case 'f': + fename = optarg; + break; + + case 'b': + febpp = atoi(optarg); + break; + + case 'w': + fewidth = atoi(optarg); + break; + + case 'h': + feheight = atoi(optarg); + break; + + default: + fprintf(stderr, + "Usage: %s [-f frontend] [-b bpp] url\n", + argv[0]); + return false; + } + } + + if (optind < argc) { + feurl = argv[optind]; + } + + return true; +} + +/** + * Set option defaults for framebuffer frontend + * + * @param defaults The option table to update. + * @return error status. + */ +static nserror set_defaults(struct nsoption_s *defaults) +{ + /* Set defaults for absent option strings */ + nsoption_setnull_charp(cookie_file, strdup("~/.netsurf/Cookies")); + nsoption_setnull_charp(cookie_jar, strdup("~/.netsurf/Cookies")); + + if (nsoption_charp(cookie_file) == NULL || + nsoption_charp(cookie_jar) == NULL) { + LOG("Failed initialising cookie options"); + return NSERROR_BAD_PARAMETER; + } + + /* set system colours for framebuffer ui */ + nsoption_set_colour(sys_colour_ActiveBorder, 0x00000000); + nsoption_set_colour(sys_colour_ActiveCaption, 0x00ddddcc); + nsoption_set_colour(sys_colour_AppWorkspace, 0x00eeeeee); + nsoption_set_colour(sys_colour_Background, 0x00aa0000); + nsoption_set_colour(sys_colour_ButtonFace, 0x00dddddd); + nsoption_set_colour(sys_colour_ButtonHighlight, 0x00cccccc); + nsoption_set_colour(sys_colour_ButtonShadow, 0x00bbbbbb); + nsoption_set_colour(sys_colour_ButtonText, 0x00000000); + nsoption_set_colour(sys_colour_CaptionText, 0x00000000); + nsoption_set_colour(sys_colour_GrayText, 0x00777777); + nsoption_set_colour(sys_colour_Highlight, 0x00ee0000); + nsoption_set_colour(sys_colour_HighlightText, 0x00000000); + nsoption_set_colour(sys_colour_InactiveBorder, 0x00000000); + nsoption_set_colour(sys_colour_InactiveCaption, 0x00ffffff); + nsoption_set_colour(sys_colour_InactiveCaptionText, 0x00cccccc); + nsoption_set_colour(sys_colour_InfoBackground, 0x00aaaaaa); + nsoption_set_colour(sys_colour_InfoText, 0x00000000); + nsoption_set_colour(sys_colour_Menu, 0x00aaaaaa); + nsoption_set_colour(sys_colour_MenuText, 0x00000000); + nsoption_set_colour(sys_colour_Scrollbar, 0x00aaaaaa); + nsoption_set_colour(sys_colour_ThreeDDarkShadow, 0x00555555); + nsoption_set_colour(sys_colour_ThreeDFace, 0x00dddddd); + nsoption_set_colour(sys_colour_ThreeDHighlight, 0x00aaaaaa); + nsoption_set_colour(sys_colour_ThreeDLightShadow, 0x00999999); + nsoption_set_colour(sys_colour_ThreeDShadow, 0x00777777); + nsoption_set_colour(sys_colour_Window, 0x00aaaaaa); + nsoption_set_colour(sys_colour_WindowFrame, 0x00000000); + nsoption_set_colour(sys_colour_WindowText, 0x00000000); + + return NSERROR_OK; +} + + +/** + * Ensures output logging stream is correctly configured + */ +static bool nslog_stream_configure(FILE *fptr) +{ + /* set log stream to be non-buffering */ + setbuf(fptr, NULL); + + return true; +} + +static void framebuffer_run(void) +{ + nsfb_event_t event; + int timeout; /* timeout in miliseconds */ + + while (fb_complete != true) { + /* run the scheduler and discover how long to wait for + * the next event. + */ + timeout = schedule_run(); + + /* if redraws are pending do not wait for event, + * return immediately + */ + if (fbtk_get_redraw_pending(fbtk)) + timeout = 0; + + if (fbtk_event(fbtk, &event, timeout)) { + if ((event.type == NSFB_EVENT_CONTROL) && + (event.value.controlcode == NSFB_CONTROL_QUIT)) + fb_complete = true; + } + + fbtk_redraw(fbtk); + } +} + +static void gui_quit(void) +{ + LOG("gui_quit"); + + urldb_save_cookies(nsoption_charp(cookie_jar)); + + framebuffer_finalise(); +} + +/* called back when click in browser window */ +static int +fb_browser_window_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + struct gui_window *gw = cbi->context; + struct browser_widget_s *bwidget = fbtk_get_userpw(widget); + browser_mouse_state mouse; + float scale = browser_window_get_scale(gw->bw); + int x = (cbi->x + bwidget->scrollx) / scale; + int y = (cbi->y + bwidget->scrolly) / scale; + uint64_t time_now; + static struct { + enum { CLICK_SINGLE, CLICK_DOUBLE, CLICK_TRIPLE } type; + uint64_t time; + } last_click; + + if (cbi->event->type != NSFB_EVENT_KEY_DOWN && + cbi->event->type != NSFB_EVENT_KEY_UP) + return 0; + + LOG("browser window clicked at %d,%d", cbi->x, cbi->y); + + switch (cbi->event->type) { + case NSFB_EVENT_KEY_DOWN: + switch (cbi->event->value.keycode) { + case NSFB_KEY_MOUSE_1: + browser_window_mouse_click(gw->bw, + BROWSER_MOUSE_PRESS_1, x, y); + gui_drag.state = GUI_DRAG_PRESSED; + gui_drag.button = 1; + gui_drag.x = x; + gui_drag.y = y; + break; + + case NSFB_KEY_MOUSE_3: + browser_window_mouse_click(gw->bw, + BROWSER_MOUSE_PRESS_2, x, y); + gui_drag.state = GUI_DRAG_PRESSED; + gui_drag.button = 2; + gui_drag.x = x; + gui_drag.y = y; + break; + + case NSFB_KEY_MOUSE_4: + /* scroll up */ + if (browser_window_scroll_at_point(gw->bw, x, y, + 0, -100) == false) + widget_scroll_y(gw, -100, false); + break; + + case NSFB_KEY_MOUSE_5: + /* scroll down */ + if (browser_window_scroll_at_point(gw->bw, x, y, + 0, 100) == false) + widget_scroll_y(gw, 100, false); + break; + + default: + break; + + } + + break; + case NSFB_EVENT_KEY_UP: + + mouse = 0; + nsu_getmonotonic_ms(&time_now); + + switch (cbi->event->value.keycode) { + case NSFB_KEY_MOUSE_1: + if (gui_drag.state == GUI_DRAG_DRAG) { + /* End of a drag, rather than click */ + + if (gui_drag.grabbed_pointer) { + /* need to ungrab pointer */ + fbtk_tgrab_pointer(widget); + gui_drag.grabbed_pointer = false; + } + + gui_drag.state = GUI_DRAG_NONE; + + /* Tell core */ + browser_window_mouse_track(gw->bw, 0, x, y); + break; + } + /* This is a click; + * clear PRESSED state and pass to core */ + gui_drag.state = GUI_DRAG_NONE; + mouse = BROWSER_MOUSE_CLICK_1; + break; + + case NSFB_KEY_MOUSE_3: + if (gui_drag.state == GUI_DRAG_DRAG) { + /* End of a drag, rather than click */ + gui_drag.state = GUI_DRAG_NONE; + + if (gui_drag.grabbed_pointer) { + /* need to ungrab pointer */ + fbtk_tgrab_pointer(widget); + gui_drag.grabbed_pointer = false; + } + + /* Tell core */ + browser_window_mouse_track(gw->bw, 0, x, y); + break; + } + /* This is a click; + * clear PRESSED state and pass to core */ + gui_drag.state = GUI_DRAG_NONE; + mouse = BROWSER_MOUSE_CLICK_2; + break; + + default: + break; + + } + + /* Determine if it's a double or triple click, allowing + * 0.5 seconds (500ms) between clicks + */ + if ((time_now < (last_click.time + 500)) && + (cbi->event->value.keycode != NSFB_KEY_MOUSE_4) && + (cbi->event->value.keycode != NSFB_KEY_MOUSE_5)) { + if (last_click.type == CLICK_SINGLE) { + /* Set double click */ + mouse |= BROWSER_MOUSE_DOUBLE_CLICK; + last_click.type = CLICK_DOUBLE; + + } else if (last_click.type == CLICK_DOUBLE) { + /* Set triple click */ + mouse |= BROWSER_MOUSE_TRIPLE_CLICK; + last_click.type = CLICK_TRIPLE; + } else { + /* Set normal click */ + last_click.type = CLICK_SINGLE; + } + } else { + last_click.type = CLICK_SINGLE; + } + + if (mouse) { + browser_window_mouse_click(gw->bw, mouse, x, y); + } + + last_click.time = time_now; + + break; + default: + break; + + } + return 1; +} + +/* called back when movement in browser window */ +static int +fb_browser_window_move(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + browser_mouse_state mouse = 0; + struct gui_window *gw = cbi->context; + struct browser_widget_s *bwidget = fbtk_get_userpw(widget); + float scale = browser_window_get_scale(gw->bw); + int x = (cbi->x + bwidget->scrollx) / scale; + int y = (cbi->y + bwidget->scrolly) / scale; + + if (gui_drag.state == GUI_DRAG_PRESSED && + (abs(x - gui_drag.x) > 5 || + abs(y - gui_drag.y) > 5)) { + /* Drag started */ + if (gui_drag.button == 1) { + browser_window_mouse_click(gw->bw, + BROWSER_MOUSE_DRAG_1, + gui_drag.x, gui_drag.y); + } else { + browser_window_mouse_click(gw->bw, + BROWSER_MOUSE_DRAG_2, + gui_drag.x, gui_drag.y); + } + gui_drag.grabbed_pointer = fbtk_tgrab_pointer(widget); + gui_drag.state = GUI_DRAG_DRAG; + } + + if (gui_drag.state == GUI_DRAG_DRAG) { + /* set up mouse state */ + mouse |= BROWSER_MOUSE_DRAG_ON; + + if (gui_drag.button == 1) + mouse |= BROWSER_MOUSE_HOLDING_1; + else + mouse |= BROWSER_MOUSE_HOLDING_2; + } + + browser_window_mouse_track(gw->bw, mouse, x, y); + + return 0; +} + + +static int +fb_browser_window_input(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + struct gui_window *gw = cbi->context; + static fbtk_modifier_type modifier = FBTK_MOD_CLEAR; + int ucs4 = -1; + + LOG("got value %d", cbi->event->value.keycode); + + switch (cbi->event->type) { + case NSFB_EVENT_KEY_DOWN: + switch (cbi->event->value.keycode) { + + case NSFB_KEY_DELETE: + browser_window_key_press(gw->bw, NS_KEY_DELETE_RIGHT); + break; + + case NSFB_KEY_PAGEUP: + if (browser_window_key_press(gw->bw, + NS_KEY_PAGE_UP) == false) + widget_scroll_y(gw, -fbtk_get_height( + gw->browser), false); + break; + + case NSFB_KEY_PAGEDOWN: + if (browser_window_key_press(gw->bw, + NS_KEY_PAGE_DOWN) == false) + widget_scroll_y(gw, fbtk_get_height( + gw->browser), false); + break; + + case NSFB_KEY_RIGHT: + if (modifier & FBTK_MOD_RCTRL || + modifier & FBTK_MOD_LCTRL) { + /* CTRL held */ + if (browser_window_key_press(gw->bw, + NS_KEY_LINE_END) == false) + widget_scroll_x(gw, INT_MAX, true); + + } else if (modifier & FBTK_MOD_RSHIFT || + modifier & FBTK_MOD_LSHIFT) { + /* SHIFT held */ + if (browser_window_key_press(gw->bw, + NS_KEY_WORD_RIGHT) == false) + widget_scroll_x(gw, fbtk_get_width( + gw->browser), false); + + } else { + /* no modifier */ + if (browser_window_key_press(gw->bw, + NS_KEY_RIGHT) == false) + widget_scroll_x(gw, 100, false); + } + break; + + case NSFB_KEY_LEFT: + if (modifier & FBTK_MOD_RCTRL || + modifier & FBTK_MOD_LCTRL) { + /* CTRL held */ + if (browser_window_key_press(gw->bw, + NS_KEY_LINE_START) == false) + widget_scroll_x(gw, 0, true); + + } else if (modifier & FBTK_MOD_RSHIFT || + modifier & FBTK_MOD_LSHIFT) { + /* SHIFT held */ + if (browser_window_key_press(gw->bw, + NS_KEY_WORD_LEFT) == false) + widget_scroll_x(gw, -fbtk_get_width( + gw->browser), false); + + } else { + /* no modifier */ + if (browser_window_key_press(gw->bw, + NS_KEY_LEFT) == false) + widget_scroll_x(gw, -100, false); + } + break; + + case NSFB_KEY_UP: + if (browser_window_key_press(gw->bw, + NS_KEY_UP) == false) + widget_scroll_y(gw, -100, false); + break; + + case NSFB_KEY_DOWN: + if (browser_window_key_press(gw->bw, + NS_KEY_DOWN) == false) + widget_scroll_y(gw, 100, false); + break; + + case NSFB_KEY_RSHIFT: + modifier |= FBTK_MOD_RSHIFT; + break; + + case NSFB_KEY_LSHIFT: + modifier |= FBTK_MOD_LSHIFT; + break; + + case NSFB_KEY_RCTRL: + modifier |= FBTK_MOD_RCTRL; + break; + + case NSFB_KEY_LCTRL: + modifier |= FBTK_MOD_LCTRL; + break; + + case NSFB_KEY_y: + case NSFB_KEY_z: + if (cbi->event->value.keycode == NSFB_KEY_z && + (modifier & FBTK_MOD_RCTRL || + modifier & FBTK_MOD_LCTRL) && + (modifier & FBTK_MOD_RSHIFT || + modifier & FBTK_MOD_LSHIFT)) { + /* Z pressed with CTRL and SHIFT held */ + browser_window_key_press(gw->bw, NS_KEY_REDO); + break; + + } else if (cbi->event->value.keycode == NSFB_KEY_z && + (modifier & FBTK_MOD_RCTRL || + modifier & FBTK_MOD_LCTRL)) { + /* Z pressed with CTRL held */ + browser_window_key_press(gw->bw, NS_KEY_UNDO); + break; + + } else if (cbi->event->value.keycode == NSFB_KEY_y && + (modifier & FBTK_MOD_RCTRL || + modifier & FBTK_MOD_LCTRL)) { + /* Y pressed with CTRL held */ + browser_window_key_press(gw->bw, NS_KEY_REDO); + break; + } + /* Z or Y pressed but not undo or redo; + * Fall through to default handling */ + + default: + ucs4 = fbtk_keycode_to_ucs4(cbi->event->value.keycode, + modifier); + if (ucs4 != -1) + browser_window_key_press(gw->bw, ucs4); + break; + } + break; + + case NSFB_EVENT_KEY_UP: + switch (cbi->event->value.keycode) { + case NSFB_KEY_RSHIFT: + modifier &= ~FBTK_MOD_RSHIFT; + break; + + case NSFB_KEY_LSHIFT: + modifier &= ~FBTK_MOD_LSHIFT; + break; + + case NSFB_KEY_RCTRL: + modifier &= ~FBTK_MOD_RCTRL; + break; + + case NSFB_KEY_LCTRL: + modifier &= ~FBTK_MOD_LCTRL; + break; + + default: + break; + } + break; + + default: + break; + } + + return 0; +} + +static void +fb_update_back_forward(struct gui_window *gw) +{ + struct browser_window *bw = gw->bw; + + fbtk_set_bitmap(gw->back, + (browser_window_back_available(bw)) ? + &left_arrow : &left_arrow_g); + fbtk_set_bitmap(gw->forward, + (browser_window_forward_available(bw)) ? + &right_arrow : &right_arrow_g); +} + +/* left icon click routine */ +static int +fb_leftarrow_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + struct gui_window *gw = cbi->context; + struct browser_window *bw = gw->bw; + + if (cbi->event->type != NSFB_EVENT_KEY_UP) + return 0; + + if (browser_window_back_available(bw)) + browser_window_history_back(bw, false); + + fb_update_back_forward(gw); + + return 1; +} + +/* right arrow icon click routine */ +static int +fb_rightarrow_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + struct gui_window *gw = cbi->context; + struct browser_window *bw = gw->bw; + + if (cbi->event->type != NSFB_EVENT_KEY_UP) + return 0; + + if (browser_window_forward_available(bw)) + browser_window_history_forward(bw, false); + + fb_update_back_forward(gw); + return 1; + +} + +/* reload icon click routine */ +static int +fb_reload_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + struct browser_window *bw = cbi->context; + + if (cbi->event->type != NSFB_EVENT_KEY_UP) + return 0; + + browser_window_reload(bw, true); + return 1; +} + +/* stop icon click routine */ +static int +fb_stop_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + struct browser_window *bw = cbi->context; + + if (cbi->event->type != NSFB_EVENT_KEY_UP) + return 0; + + browser_window_stop(bw); + return 0; +} + +static int +fb_osk_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + + if (cbi->event->type != NSFB_EVENT_KEY_UP) + return 0; + + map_osk(); + + return 0; +} + +/* close browser window icon click routine */ +static int +fb_close_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + if (cbi->event->type != NSFB_EVENT_KEY_UP) + return 0; + + fb_complete = true; + + return 0; +} + +static int +fb_scroll_callback(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + struct gui_window *gw = cbi->context; + + switch (cbi->type) { + case FBTK_CBT_SCROLLY: + widget_scroll_y(gw, cbi->y, true); + break; + + case FBTK_CBT_SCROLLX: + widget_scroll_x(gw, cbi->x, true); + break; + + default: + break; + } + return 0; +} + +static int +fb_url_enter(void *pw, char *text) +{ + struct browser_window *bw = pw; + nsurl *url; + nserror error; + + error = nsurl_create(text, &url); + if (error != NSERROR_OK) { + fb_warn_user(messages_get_errorcode(error), 0); + } else { + browser_window_navigate(bw, url, NULL, BW_NAVIGATE_HISTORY, + NULL, NULL, NULL); + nsurl_unref(url); + } + + return 0; +} + +static int +fb_url_move(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + framebuffer_set_cursor(&caret_image); + return 0; +} + +static int +set_ptr_default_move(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + framebuffer_set_cursor(&pointer_image); + return 0; +} + +static int +fb_localhistory_btn_clik(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + struct gui_window *gw = cbi->context; + + if (cbi->event->type != NSFB_EVENT_KEY_UP) + return 0; + + fb_localhistory_map(gw->localhistory); + + return 0; +} + + +/** Create a toolbar window and populate it with buttons. + * + * The toolbar layout uses a character to define buttons type and position: + * b - back + * l - local history + * f - forward + * s - stop + * r - refresh + * u - url bar expands to fit remaining space + * t - throbber/activity indicator + * c - close the current window + * + * The default layout is "blfsrut" there should be no more than a + * single url bar entry or behaviour will be undefined. + * + * @param gw Parent window + * @param toolbar_height The height in pixels of the toolbar + * @param padding The padding in pixels round each element of the toolbar + * @param frame_col Frame colour. + * @param toolbar_layout A string defining which buttons and controls + * should be added to the toolbar. May be empty + * string to disable the bar.. + * + */ +static fbtk_widget_t * +create_toolbar(struct gui_window *gw, + int toolbar_height, + int padding, + colour frame_col, + const char *toolbar_layout) +{ + fbtk_widget_t *toolbar; + fbtk_widget_t *widget; + + int xpos; /* The position of the next widget. */ + int xlhs = 0; /* extent of the left hand side widgets */ + int xdir = 1; /* the direction of movement + or - 1 */ + const char *itmtype; /* type of the next item */ + + if (toolbar_layout == NULL) { + toolbar_layout = NSFB_TOOLBAR_DEFAULT_LAYOUT; + } + + LOG("Using toolbar layout %s", toolbar_layout); + + itmtype = toolbar_layout; + + /* check for the toolbar being disabled */ + if ((*itmtype == 0) || (*itmtype == 'q')) { + return NULL; + } + + toolbar = fbtk_create_window(gw->window, 0, 0, 0, + toolbar_height, + frame_col); + + if (toolbar == NULL) { + return NULL; + } + + fbtk_set_handler(toolbar, + FBTK_CBT_POINTERENTER, + set_ptr_default_move, + NULL); + + + xpos = padding; + + /* loop proceeds creating widget on the left hand side until + * it runs out of layout or encounters a url bar declaration + * wherupon it works backwards from the end of the layout + * untill the space left is for the url bar + */ + while ((itmtype >= toolbar_layout) && + (*itmtype != 0) && + (xdir !=0)) { + + LOG("toolbar adding %c", *itmtype); + + + switch (*itmtype) { + + case 'b': /* back */ + widget = fbtk_create_button(toolbar, + (xdir == 1) ? xpos : + xpos - left_arrow.width, + padding, + left_arrow.width, + -padding, + frame_col, + &left_arrow, + fb_leftarrow_click, + gw); + gw->back = widget; /* keep reference */ + break; + + case 'l': /* local history */ + widget = fbtk_create_button(toolbar, + (xdir == 1) ? xpos : + xpos - history_image.width, + padding, + history_image.width, + -padding, + frame_col, + &history_image, + fb_localhistory_btn_clik, + gw); + gw->history = widget; + break; + + case 'f': /* forward */ + widget = fbtk_create_button(toolbar, + (xdir == 1)?xpos : + xpos - right_arrow.width, + padding, + right_arrow.width, + -padding, + frame_col, + &right_arrow, + fb_rightarrow_click, + gw); + gw->forward = widget; + break; + + case 'c': /* close the current window */ + widget = fbtk_create_button(toolbar, + (xdir == 1)?xpos : + xpos - stop_image_g.width, + padding, + stop_image_g.width, + -padding, + frame_col, + &stop_image_g, + fb_close_click, + gw->bw); + gw->close = widget; + break; + + case 's': /* stop */ + widget = fbtk_create_button(toolbar, + (xdir == 1)?xpos : + xpos - stop_image.width, + padding, + stop_image.width, + -padding, + frame_col, + &stop_image, + fb_stop_click, + gw->bw); + gw->stop = widget; + break; + + case 'r': /* reload */ + widget = fbtk_create_button(toolbar, + (xdir == 1)?xpos : + xpos - reload.width, + padding, + reload.width, + -padding, + frame_col, + &reload, + fb_reload_click, + gw->bw); + gw->reload = widget; + break; + + case 't': /* throbber/activity indicator */ + widget = fbtk_create_bitmap(toolbar, + (xdir == 1)?xpos : + xpos - throbber0.width, + padding, + throbber0.width, + -padding, + frame_col, + &throbber0); + gw->throbber = widget; + break; + + + case 'u': /* url bar*/ + if (xdir == -1) { + /* met the u going backwards add url + * now we know available extent + */ + + widget = fbtk_create_writable_text(toolbar, + xlhs, + padding, + xpos - xlhs, + -padding, + FB_COLOUR_WHITE, + FB_COLOUR_BLACK, + true, + fb_url_enter, + gw->bw); + + fbtk_set_handler(widget, + FBTK_CBT_POINTERENTER, + fb_url_move, gw->bw); + + gw->url = widget; /* keep reference */ + + /* toolbar is complete */ + xdir = 0; + break; + } + /* met url going forwards, note position and + * reverse direction + */ + itmtype = toolbar_layout + strlen(toolbar_layout); + xdir = -1; + xlhs = xpos; + xpos = (2 * fbtk_get_width(toolbar)); + widget = toolbar; + break; + + default: + widget = NULL; + xdir = 0; + LOG("Unknown element %c in toolbar layout", *itmtype); + break; + + } + + if (widget != NULL) { + xpos += (xdir * (fbtk_get_width(widget) + padding)); + } + + LOG("xpos is %d", xpos); + + itmtype += xdir; + } + + fbtk_set_mapping(toolbar, true); + + return toolbar; +} + + +/** Resize a toolbar. + * + * @param gw Parent window + * @param toolbar_height The height in pixels of the toolbar + * @param padding The padding in pixels round each element of the toolbar + * @param toolbar_layout A string defining which buttons and controls + * should be added to the toolbar. May be empty + * string to disable the bar. + */ +static void +resize_toolbar(struct gui_window *gw, + int toolbar_height, + int padding, + const char *toolbar_layout) +{ + fbtk_widget_t *widget; + + int xpos; /* The position of the next widget. */ + int xlhs = 0; /* extent of the left hand side widgets */ + int xdir = 1; /* the direction of movement + or - 1 */ + const char *itmtype; /* type of the next item */ + int x = 0, y = 0, w = 0, h = 0; + + if (gw->toolbar == NULL) { + return; + } + + if (toolbar_layout == NULL) { + toolbar_layout = NSFB_TOOLBAR_DEFAULT_LAYOUT; + } + + itmtype = toolbar_layout; + + if (*itmtype == 0) { + return; + } + + fbtk_set_pos_and_size(gw->toolbar, 0, 0, 0, toolbar_height); + + xpos = padding; + + /* loop proceeds creating widget on the left hand side until + * it runs out of layout or encounters a url bar declaration + * wherupon it works backwards from the end of the layout + * untill the space left is for the url bar + */ + while (itmtype >= toolbar_layout && xdir != 0) { + + switch (*itmtype) { + case 'b': /* back */ + widget = gw->back; + x = (xdir == 1) ? xpos : xpos - left_arrow.width; + y = padding; + w = left_arrow.width; + h = -padding; + break; + + case 'l': /* local history */ + widget = gw->history; + x = (xdir == 1) ? xpos : xpos - history_image.width; + y = padding; + w = history_image.width; + h = -padding; + break; + + case 'f': /* forward */ + widget = gw->forward; + x = (xdir == 1) ? xpos : xpos - right_arrow.width; + y = padding; + w = right_arrow.width; + h = -padding; + break; + + case 'c': /* close the current window */ + widget = gw->close; + x = (xdir == 1) ? xpos : xpos - stop_image_g.width; + y = padding; + w = stop_image_g.width; + h = -padding; + break; + + case 's': /* stop */ + widget = gw->stop; + x = (xdir == 1) ? xpos : xpos - stop_image.width; + y = padding; + w = stop_image.width; + h = -padding; + break; + + case 'r': /* reload */ + widget = gw->reload; + x = (xdir == 1) ? xpos : xpos - reload.width; + y = padding; + w = reload.width; + h = -padding; + break; + + case 't': /* throbber/activity indicator */ + widget = gw->throbber; + x = (xdir == 1) ? xpos : xpos - throbber0.width; + y = padding; + w = throbber0.width; + h = -padding; + break; + + + case 'u': /* url bar*/ + if (xdir == -1) { + /* met the u going backwards add url + * now we know available extent + */ + widget = gw->url; + x = xlhs; + y = padding; + w = xpos - xlhs; + h = -padding; + + /* toolbar is complete */ + xdir = 0; + break; + } + /* met url going forwards, note position and + * reverse direction + */ + itmtype = toolbar_layout + strlen(toolbar_layout); + xdir = -1; + xlhs = xpos; + w = fbtk_get_width(gw->toolbar); + xpos = 2 * w; + widget = gw->toolbar; + break; + + default: + widget = NULL; + break; + + } + + if (widget != NULL) { + if (widget != gw->toolbar) + fbtk_set_pos_and_size(widget, x, y, w, h); + xpos += xdir * (w + padding); + } + + itmtype += xdir; + } +} + +/** Routine called when "stripped of focus" event occours for browser widget. + * + * @param widget The widget reciving "stripped of focus" event. + * @param cbi The callback parameters. + * @return The callback result. + */ +static int +fb_browser_window_strip_focus(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + fbtk_set_caret(widget, false, 0, 0, 0, NULL); + + return 0; +} + +static void +create_browser_widget(struct gui_window *gw, int toolbar_height, int furniture_width) +{ + struct browser_widget_s *browser_widget; + browser_widget = calloc(1, sizeof(struct browser_widget_s)); + + gw->browser = fbtk_create_user(gw->window, + 0, + toolbar_height, + -furniture_width, + -furniture_width, + browser_widget); + + fbtk_set_handler(gw->browser, FBTK_CBT_REDRAW, fb_browser_window_redraw, gw); + fbtk_set_handler(gw->browser, FBTK_CBT_DESTROY, fb_browser_window_destroy, gw); + fbtk_set_handler(gw->browser, FBTK_CBT_INPUT, fb_browser_window_input, gw); + fbtk_set_handler(gw->browser, FBTK_CBT_CLICK, fb_browser_window_click, gw); + fbtk_set_handler(gw->browser, FBTK_CBT_STRIP_FOCUS, fb_browser_window_strip_focus, gw); + fbtk_set_handler(gw->browser, FBTK_CBT_POINTERMOVE, fb_browser_window_move, gw); +} + +static void +resize_browser_widget(struct gui_window *gw, int x, int y, + int width, int height) +{ + fbtk_set_pos_and_size(gw->browser, x, y, width, height); + browser_window_reformat(gw->bw, false, width, height); +} + +static void +create_normal_browser_window(struct gui_window *gw, int furniture_width) +{ + fbtk_widget_t *widget; + fbtk_widget_t *toolbar; + int statusbar_width = 0; + int toolbar_height = nsoption_int(fb_toolbar_size); + + LOG("Normal window"); + + gw->window = fbtk_create_window(fbtk, 0, 0, 0, 0, 0); + + statusbar_width = nsoption_int(toolbar_status_size) * + fbtk_get_width(gw->window) / 10000; + + /* toolbar */ + toolbar = create_toolbar(gw, + toolbar_height, + 2, + FB_FRAME_COLOUR, + nsoption_charp(fb_toolbar_layout)); + gw->toolbar = toolbar; + + /* set the actually created toolbar height */ + if (toolbar != NULL) { + toolbar_height = fbtk_get_height(toolbar); + } else { + toolbar_height = 0; + } + + /* status bar */ + gw->status = fbtk_create_text(gw->window, + 0, + fbtk_get_height(gw->window) - furniture_width, + statusbar_width, furniture_width, + FB_FRAME_COLOUR, FB_COLOUR_BLACK, + false); + fbtk_set_handler(gw->status, FBTK_CBT_POINTERENTER, set_ptr_default_move, NULL); + + LOG("status bar %p at %d,%d", gw->status, fbtk_get_absx(gw->status), fbtk_get_absy(gw->status)); + + /* create horizontal scrollbar */ + gw->hscroll = fbtk_create_hscroll(gw->window, + statusbar_width, + fbtk_get_height(gw->window) - furniture_width, + fbtk_get_width(gw->window) - statusbar_width - furniture_width, + furniture_width, + FB_SCROLL_COLOUR, + FB_FRAME_COLOUR, + fb_scroll_callback, + gw); + + /* fill bottom right area */ + + if (nsoption_bool(fb_osk) == true) { + widget = fbtk_create_text_button(gw->window, + fbtk_get_width(gw->window) - furniture_width, + fbtk_get_height(gw->window) - furniture_width, + furniture_width, + furniture_width, + FB_FRAME_COLOUR, FB_COLOUR_BLACK, + fb_osk_click, + NULL); + widget = fbtk_create_button(gw->window, + fbtk_get_width(gw->window) - furniture_width, + fbtk_get_height(gw->window) - furniture_width, + furniture_width, + furniture_width, + FB_FRAME_COLOUR, + &osk_image, + fb_osk_click, + NULL); + } else { + widget = fbtk_create_fill(gw->window, + fbtk_get_width(gw->window) - furniture_width, + fbtk_get_height(gw->window) - furniture_width, + furniture_width, + furniture_width, + FB_FRAME_COLOUR); + + fbtk_set_handler(widget, FBTK_CBT_POINTERENTER, set_ptr_default_move, NULL); + } + + gw->bottom_right = widget; + + /* create vertical scrollbar */ + gw->vscroll = fbtk_create_vscroll(gw->window, + fbtk_get_width(gw->window) - furniture_width, + toolbar_height, + furniture_width, + fbtk_get_height(gw->window) - toolbar_height - furniture_width, + FB_SCROLL_COLOUR, + FB_FRAME_COLOUR, + fb_scroll_callback, + gw); + + /* browser widget */ + create_browser_widget(gw, toolbar_height, nsoption_int(fb_furniture_size)); + + /* Give browser_window's user widget input focus */ + fbtk_set_focus(gw->browser); +} + +static void +resize_normal_browser_window(struct gui_window *gw, int furniture_width) +{ + bool resized; + int width, height; + int statusbar_width; + int toolbar_height = fbtk_get_height(gw->toolbar); + + /* Resize the main window widget */ + resized = fbtk_set_pos_and_size(gw->window, 0, 0, 0, 0); + if (!resized) + return; + + width = fbtk_get_width(gw->window); + height = fbtk_get_height(gw->window); + statusbar_width = nsoption_int(toolbar_status_size) * width / 10000; + + resize_toolbar(gw, toolbar_height, 2, + nsoption_charp(fb_toolbar_layout)); + fbtk_set_pos_and_size(gw->status, + 0, height - furniture_width, + statusbar_width, furniture_width); + fbtk_reposition_hscroll(gw->hscroll, + statusbar_width, height - furniture_width, + width - statusbar_width - furniture_width, + furniture_width); + fbtk_set_pos_and_size(gw->bottom_right, + width - furniture_width, height - furniture_width, + furniture_width, furniture_width); + fbtk_reposition_vscroll(gw->vscroll, + width - furniture_width, + toolbar_height, furniture_width, + height - toolbar_height - furniture_width); + resize_browser_widget(gw, + 0, toolbar_height, + width - furniture_width, + height - furniture_width - toolbar_height); +} + +static void gui_window_add_to_window_list(struct gui_window *gw) +{ + gw->next = NULL; + gw->prev = NULL; + + if (window_list == NULL) { + window_list = gw; + } else { + window_list->prev = gw; + gw->next = window_list; + window_list = gw; + } +} + +static void gui_window_remove_from_window_list(struct gui_window *gw) +{ + struct gui_window *list; + + for (list = window_list; list != NULL; list = list->next) { + if (list != gw) + continue; + + if (list == window_list) { + window_list = list->next; + if (window_list != NULL) + window_list->prev = NULL; + } else { + list->prev->next = list->next; + if (list->next != NULL) { + list->next->prev = list->prev; + } + } + break; + } +} + + +static struct gui_window * +gui_window_create(struct browser_window *bw, + struct gui_window *existing, + gui_window_create_flags flags) +{ + struct gui_window *gw; + + gw = calloc(1, sizeof(struct gui_window)); + + if (gw == NULL) + return NULL; + + /* associate the gui window with the underlying browser window + */ + gw->bw = bw; + + create_normal_browser_window(gw, nsoption_int(fb_furniture_size)); + gw->localhistory = fb_create_localhistory(bw, fbtk, nsoption_int(fb_furniture_size)); + + /* map and request redraw of gui window */ + fbtk_set_mapping(gw->window, true); + + /* Add it to the window list */ + gui_window_add_to_window_list(gw); + + return gw; +} + +static void +gui_window_destroy(struct gui_window *gw) +{ + gui_window_remove_from_window_list(gw); + + fbtk_destroy_widget(gw->window); + + free(gw); +} + +static void +gui_window_redraw_window(struct gui_window *g) +{ + fb_queue_redraw(g->browser, 0, 0, fbtk_get_width(g->browser), fbtk_get_height(g->browser) ); +} + +static void +gui_window_update_box(struct gui_window *g, const struct rect *rect) +{ + struct browser_widget_s *bwidget = fbtk_get_userpw(g->browser); + fb_queue_redraw(g->browser, + rect->x0 - bwidget->scrollx, + rect->y0 - bwidget->scrolly, + rect->x1 - bwidget->scrollx, + rect->y1 - bwidget->scrolly); +} + +static bool +gui_window_get_scroll(struct gui_window *g, int *sx, int *sy) +{ + struct browser_widget_s *bwidget = fbtk_get_userpw(g->browser); + float scale = browser_window_get_scale(g->bw); + + *sx = bwidget->scrollx / scale; + *sy = bwidget->scrolly / scale; + + return true; +} + +static void +gui_window_set_scroll(struct gui_window *gw, int sx, int sy) +{ + struct browser_widget_s *bwidget = fbtk_get_userpw(gw->browser); + float scale = browser_window_get_scale(gw->bw); + + assert(bwidget); + + widget_scroll_x(gw, sx * scale, true); + widget_scroll_y(gw, sy * scale, true); +} + + +static void +gui_window_get_dimensions(struct gui_window *g, + int *width, + int *height, + bool scaled) +{ + float scale = browser_window_get_scale(g->bw); + + *width = fbtk_get_width(g->browser); + *height = fbtk_get_height(g->browser); + + if (scaled) { + *width /= scale; + *height /= scale; + } +} + +static void +gui_window_update_extent(struct gui_window *gw) +{ + int w, h; + browser_window_get_extents(gw->bw, true, &w, &h); + + fbtk_set_scroll_parameters(gw->hscroll, 0, w, + fbtk_get_width(gw->browser), 100); + + fbtk_set_scroll_parameters(gw->vscroll, 0, h, + fbtk_get_height(gw->browser), 100); +} + +static void +gui_window_set_status(struct gui_window *g, const char *text) +{ + fbtk_set_text(g->status, text); +} + +static void +gui_window_set_pointer(struct gui_window *g, gui_pointer_shape shape) +{ + switch (shape) { + case GUI_POINTER_POINT: + framebuffer_set_cursor(&hand_image); + break; + + case GUI_POINTER_CARET: + framebuffer_set_cursor(&caret_image); + break; + + case GUI_POINTER_MENU: + framebuffer_set_cursor(&menu_image); + break; + + case GUI_POINTER_PROGRESS: + framebuffer_set_cursor(&progress_image); + break; + + case GUI_POINTER_MOVE: + framebuffer_set_cursor(&move_image); + break; + + default: + framebuffer_set_cursor(&pointer_image); + break; + } +} + +static nserror +gui_window_set_url(struct gui_window *g, nsurl *url) +{ + fbtk_set_text(g->url, nsurl_access(url)); + return NSERROR_OK; +} + +static void +throbber_advance(void *pw) +{ + struct gui_window *g = pw; + struct fbtk_bitmap *image; + + switch (g->throbber_index) { + case 0: + image = &throbber1; + g->throbber_index = 1; + break; + + case 1: + image = &throbber2; + g->throbber_index = 2; + break; + + case 2: + image = &throbber3; + g->throbber_index = 3; + break; + + case 3: + image = &throbber4; + g->throbber_index = 4; + break; + + case 4: + image = &throbber5; + g->throbber_index = 5; + break; + + case 5: + image = &throbber6; + g->throbber_index = 6; + break; + + case 6: + image = &throbber7; + g->throbber_index = 7; + break; + + case 7: + image = &throbber8; + g->throbber_index = 0; + break; + + default: + return; + } + + if (g->throbber_index >= 0) { + fbtk_set_bitmap(g->throbber, image); + framebuffer_schedule(100, throbber_advance, g); + } +} + +static void +gui_window_start_throbber(struct gui_window *g) +{ + g->throbber_index = 0; + framebuffer_schedule(100, throbber_advance, g); +} + +static void +gui_window_stop_throbber(struct gui_window *gw) +{ + gw->throbber_index = -1; + fbtk_set_bitmap(gw->throbber, &throbber0); + + fb_update_back_forward(gw); + +} + +static void +gui_window_remove_caret_cb(fbtk_widget_t *widget) +{ + struct browser_widget_s *bwidget = fbtk_get_userpw(widget); + int c_x, c_y, c_h; + + if (fbtk_get_caret(widget, &c_x, &c_y, &c_h)) { + /* browser window already had caret: + * redraw its area to remove it first */ + fb_queue_redraw(widget, + c_x - bwidget->scrollx, + c_y - bwidget->scrolly, + c_x + 1 - bwidget->scrollx, + c_y + c_h - bwidget->scrolly); + } +} + +static void +gui_window_place_caret(struct gui_window *g, int x, int y, int height, + const struct rect *clip) +{ + struct browser_widget_s *bwidget = fbtk_get_userpw(g->browser); + + /* set new pos */ + fbtk_set_caret(g->browser, true, x, y, height, + gui_window_remove_caret_cb); + + /* redraw new caret pos */ + fb_queue_redraw(g->browser, + x - bwidget->scrollx, + y - bwidget->scrolly, + x + 1 - bwidget->scrollx, + y + height - bwidget->scrolly); +} + +static void +gui_window_remove_caret(struct gui_window *g) +{ + int c_x, c_y, c_h; + + if (fbtk_get_caret(g->browser, &c_x, &c_y, &c_h)) { + /* browser window owns the caret, so can remove it */ + fbtk_set_caret(g->browser, false, 0, 0, 0, NULL); + } +} + +static void framebuffer_window_reformat(struct gui_window *gw) +{ + /** @todo if we ever do zooming reformat should be implemented */ + LOG("window:%p", gw); + + /* + browser_window_reformat(gw->bw, false, width, height); + */ +} + +static struct gui_window_table framebuffer_window_table = { + .create = gui_window_create, + .destroy = gui_window_destroy, + .redraw = gui_window_redraw_window, + .update = gui_window_update_box, + .get_scroll = gui_window_get_scroll, + .set_scroll = gui_window_set_scroll, + .get_dimensions = gui_window_get_dimensions, + .update_extent = gui_window_update_extent, + .reformat = framebuffer_window_reformat, + + .set_url = gui_window_set_url, + .set_status = gui_window_set_status, + .set_pointer = gui_window_set_pointer, + .place_caret = gui_window_place_caret, + .remove_caret = gui_window_remove_caret, + .start_throbber = gui_window_start_throbber, + .stop_throbber = gui_window_stop_throbber, +}; + + +static struct gui_misc_table framebuffer_misc_table = { + .schedule = framebuffer_schedule, + .warning = fb_warn_user, + + .quit = gui_quit, +}; + +/** Entry point from OS. + * + * /param argc The number of arguments in the string vector. + * /param argv The argument string vector. + * /return The return code to the OS + */ +int +main(int argc, char** argv) +{ + struct browser_window *bw; + char *options; + char *messages; + nsurl *url; + nserror ret; + nsfb_t *nsfb; + struct netsurf_table framebuffer_table = { + .misc = &framebuffer_misc_table, + .window = &framebuffer_window_table, + .clipboard = framebuffer_clipboard_table, + .fetch = framebuffer_fetch_table, + .utf8 = framebuffer_utf8_table, + .bitmap = framebuffer_bitmap_table, + .layout = framebuffer_layout_table, + }; + + ret = netsurf_register(&framebuffer_table); + if (ret != NSERROR_OK) { + die("NetSurf operation table failed registration"); + } + + respaths = fb_init_resource(NETSURF_FB_RESPATH":"NETSURF_FB_FONTPATH); + + /* initialise logging. Not fatal if it fails but not much we + * can do about it either. + */ + nslog_init(nslog_stream_configure, &argc, argv); + + /* user options setup */ + ret = nsoption_init(set_defaults, &nsoptions, &nsoptions_default); + if (ret != NSERROR_OK) { + die("Options failed to initialise"); + } + options = filepath_find(respaths, "Choices"); + nsoption_read(options, nsoptions); + free(options); + nsoption_commandline(&argc, argv, nsoptions); + + /* message init */ + messages = filepath_find(respaths, "Messages"); + ret = messages_add_from_file(messages); + free(messages); + if (ret != NSERROR_OK) { + fprintf(stderr, "Message translations failed to load\n"); + } + + /* common initialisation */ + ret = netsurf_init(NULL); + if (ret != NSERROR_OK) { + die("NetSurf failed to initialise"); + } + + /* Override, since we have no support for non-core SELECT menu */ + nsoption_set_bool(core_select_menu, true); + + if (process_cmdline(argc,argv) != true) + die("unable to process command line.\n"); + + nsfb = framebuffer_initialise(fename, fewidth, feheight, febpp); + if (nsfb == NULL) + die("Unable to initialise framebuffer"); + + framebuffer_set_cursor(&pointer_image); + + if (fb_font_init() == false) + die("Unable to initialise the font system"); + + fbtk = fbtk_init(nsfb); + + fbtk_enable_oskb(fbtk); + + urldb_load_cookies(nsoption_charp(cookie_file)); + + /* create an initial browser window */ + + LOG("calling browser_window_create"); + + ret = nsurl_create(feurl, &url); + if (ret == NSERROR_OK) { + ret = browser_window_create(BW_CREATE_HISTORY, + url, + NULL, + NULL, + &bw); + nsurl_unref(url); + } + if (ret != NSERROR_OK) { + fb_warn_user(messages_get_errorcode(ret), 0); + } else { + framebuffer_run(); + + browser_window_destroy(bw); + } + + netsurf_exit(); + + if (fb_font_finalise() == false) + LOG("Font finalisation failed."); + + /* finalise options */ + nsoption_finalise(nsoptions, nsoptions_default); + + return 0; +} + +void gui_resize(fbtk_widget_t *root, int width, int height) +{ + struct gui_window *gw; + nsfb_t *nsfb = fbtk_get_nsfb(root); + + /* Enforce a minimum */ + if (width < 300) + width = 300; + if (height < 200) + height = 200; + + if (framebuffer_resize(nsfb, width, height, febpp) == false) { + return; + } + + fbtk_set_pos_and_size(root, 0, 0, width, height); + + fewidth = width; + feheight = height; + + for (gw = window_list; gw != NULL; gw = gw->next) { + resize_normal_browser_window(gw, + nsoption_int(fb_furniture_size)); + } + + fbtk_request_redraw(root); +} + + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/kolibrios/fb/gui.h b/frontends/kolibrios/fb/gui.h new file mode 100644 index 000000000..0de1add69 --- /dev/null +++ b/frontends/kolibrios/fb/gui.h @@ -0,0 +1,84 @@ +/* + * Copyright 2008 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NETSURF_FB_GUI_H +#define NETSURF_FB_GUI_H + + +struct fbtk_widget_s; + +typedef struct fb_cursor_s fb_cursor_t; + +/* bounding box */ +typedef struct nsfb_bbox_s bbox_t; + +struct gui_localhistory { + struct browser_window *bw; + + struct fbtk_widget_s *window; + struct fbtk_widget_s *hscroll; + struct fbtk_widget_s *vscroll; + struct fbtk_widget_s *history; + + int scrollx, scrolly; /**< scroll offsets. */ +}; + +struct gui_window { + struct browser_window *bw; + + struct fbtk_widget_s *window; + struct fbtk_widget_s *back; + struct fbtk_widget_s *forward; + struct fbtk_widget_s *history; + struct fbtk_widget_s *stop; + struct fbtk_widget_s *reload; + struct fbtk_widget_s *close; + struct fbtk_widget_s *url; + struct fbtk_widget_s *status; + struct fbtk_widget_s *throbber; + struct fbtk_widget_s *hscroll; + struct fbtk_widget_s *vscroll; + struct fbtk_widget_s *browser; + struct fbtk_widget_s *toolbar; + struct fbtk_widget_s *bottom_right; + + int throbber_index; + + struct gui_localhistory *localhistory; + + struct gui_window *next; + struct gui_window *prev; +}; + + +extern struct gui_window *window_list; + +struct gui_localhistory *fb_create_localhistory(struct browser_window *bw, + struct fbtk_widget_s *parent, int furniture_width); +void fb_localhistory_map(struct gui_localhistory * glh); + +void gui_resize(struct fbtk_widget_s *root, int width, int height); + + +#endif /* NETSURF_FB_GUI_H */ + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/kolibrios/fb/image_data.h b/frontends/kolibrios/fb/image_data.h new file mode 100644 index 000000000..cf349f59a --- /dev/null +++ b/frontends/kolibrios/fb/image_data.h @@ -0,0 +1,60 @@ +/* + * Copyright 2008 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef FB_IMAGE_DATA +#define FB_IMAGE_DATA + +#include "framebuffer/fbtk.h" + +extern struct fbtk_bitmap left_arrow; +extern struct fbtk_bitmap right_arrow; +extern struct fbtk_bitmap reload; +extern struct fbtk_bitmap stop_image; +extern struct fbtk_bitmap history_image; + +extern struct fbtk_bitmap left_arrow_g; +extern struct fbtk_bitmap right_arrow_g; +extern struct fbtk_bitmap reload_g; +extern struct fbtk_bitmap stop_image_g; +extern struct fbtk_bitmap history_image_g; + +extern struct fbtk_bitmap scrolll; +extern struct fbtk_bitmap scrollr; +extern struct fbtk_bitmap scrollu; +extern struct fbtk_bitmap scrolld; + +extern struct fbtk_bitmap osk_image; + +extern struct fbtk_bitmap pointer_image; +extern struct fbtk_bitmap hand_image; +extern struct fbtk_bitmap caret_image; +extern struct fbtk_bitmap menu_image; +extern struct fbtk_bitmap move_image; +extern struct fbtk_bitmap progress_image; + +extern struct fbtk_bitmap throbber0; +extern struct fbtk_bitmap throbber1; +extern struct fbtk_bitmap throbber2; +extern struct fbtk_bitmap throbber3; +extern struct fbtk_bitmap throbber4; +extern struct fbtk_bitmap throbber5; +extern struct fbtk_bitmap throbber6; +extern struct fbtk_bitmap throbber7; +extern struct fbtk_bitmap throbber8; + +#endif /* FB_IMAGE_DATA */ diff --git a/frontends/kolibrios/fb/localhistory.c b/frontends/kolibrios/fb/localhistory.c new file mode 100644 index 000000000..3192f0747 --- /dev/null +++ b/frontends/kolibrios/fb/localhistory.c @@ -0,0 +1,144 @@ +/* + * Copyright 2010 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf 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 . + */ + +#include +#include +#include + +#include +#include +#include + +#include "desktop/browser_history.h" +#include "netsurf/plotters.h" + +#include "framebuffer/gui.h" +#include "framebuffer/fbtk.h" +#include "framebuffer/framebuffer.h" + +static int +localhistory_redraw(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + struct gui_localhistory *glh = cbi->context; + nsfb_bbox_t rbox; + + struct redraw_context ctx = { + .interactive = true, + .background_images = true, + .plot = &fb_plotters + }; + + rbox.x0 = fbtk_get_absx(widget); + rbox.y0 = fbtk_get_absy(widget); + + rbox.x1 = rbox.x0 + fbtk_get_width(widget); + rbox.y1 = rbox.y0 + fbtk_get_height(widget); + + nsfb_claim(fbtk_get_nsfb(widget), &rbox); + + nsfb_plot_rectangle_fill(fbtk_get_nsfb(widget), &rbox, 0xffffffff); + + browser_window_history_redraw_rectangle(glh->bw, + glh->scrollx, + glh->scrolly, + fbtk_get_width(widget) + glh->scrollx, + fbtk_get_height(widget) + glh->scrolly, + 0, 0, &ctx); + + nsfb_update(fbtk_get_nsfb(widget), &rbox); + + return 0; +} + +static int +localhistory_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + struct gui_localhistory *glh = cbi->context; + + if (cbi->event->type != NSFB_EVENT_KEY_UP) + return 0; + + browser_window_history_click(glh->bw, cbi->x, cbi->y, false); + + fbtk_set_mapping(glh->window, false); + + return 1; +} + +struct gui_localhistory * +fb_create_localhistory(struct browser_window *bw, + fbtk_widget_t *parent, + int furniture_width) +{ + struct gui_localhistory *glh; + glh = calloc(1, sizeof(struct gui_localhistory)); + + if (glh == NULL) + return NULL; + + glh->bw = bw; + + /* container window */ + glh->window = fbtk_create_window(parent, 0, 0, 0, 0, 0); + + glh->history = fbtk_create_user(glh->window, 0, 0, -furniture_width, -furniture_width, glh); + + fbtk_set_handler(glh->history, FBTK_CBT_REDRAW, localhistory_redraw, glh); + fbtk_set_handler(glh->history, FBTK_CBT_CLICK, localhistory_click, glh); + /* + fbtk_set_handler(gw->localhistory, FBTK_CBT_INPUT, fb_browser_window_input, gw); + fbtk_set_handler(gw->localhistory, FBTK_CBT_POINTERMOVE, fb_browser_window_move, bw); + */ + + /* create horizontal scrollbar */ + glh->hscroll = fbtk_create_hscroll(glh->window, + 0, + fbtk_get_height(glh->window) - furniture_width, + fbtk_get_width(glh->window) - furniture_width, + furniture_width, + FB_SCROLL_COLOUR, + FB_FRAME_COLOUR, + NULL, + NULL); + + glh->vscroll = fbtk_create_vscroll(glh->window, + fbtk_get_width(glh->window) - furniture_width, + 0, + furniture_width, + fbtk_get_height(glh->window) - furniture_width, + FB_SCROLL_COLOUR, + FB_FRAME_COLOUR, + NULL, + NULL); + + fbtk_create_fill(glh->window, + fbtk_get_width(glh->window) - furniture_width, + fbtk_get_height(glh->window) - furniture_width, + furniture_width, + furniture_width, + FB_FRAME_COLOUR); + + return glh; +} + +void +fb_localhistory_map(struct gui_localhistory * glh) +{ + fbtk_set_zorder(glh->window, INT_MIN); + fbtk_set_mapping(glh->window, true); +} diff --git a/frontends/kolibrios/fb/options.h b/frontends/kolibrios/fb/options.h new file mode 100644 index 000000000..eee6f4bc6 --- /dev/null +++ b/frontends/kolibrios/fb/options.h @@ -0,0 +1,70 @@ +/* + * Copyright 2012 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _NETSURF_FRAMEBUFFER_OPTIONS_H_ +#define _NETSURF_FRAMEBUFFER_OPTIONS_H_ + +/* currently nothing here */ + +#endif + +/***** surface options *****/ + +NSOPTION_INTEGER(fb_depth, 32) +NSOPTION_INTEGER(fb_refresh, 70) +NSOPTION_STRING(fb_device, NULL) +NSOPTION_STRING(fb_input_devpath, NULL) +NSOPTION_STRING(fb_input_glob, NULL) + +/***** toolkit options *****/ + +/** toolkit furniture size */ +NSOPTION_INTEGER(fb_furniture_size, 18) +/** toolbar furniture size */ +NSOPTION_INTEGER(fb_toolbar_size, 30) +/** toolbar layout */ +NSOPTION_STRING(fb_toolbar_layout, NULL) +/** enable on screen keyboard */ +NSOPTION_BOOL(fb_osk, false) + +/***** font options *****/ + +/** render all fonts monochrome */ +NSOPTION_BOOL(fb_font_monochrome, false) +/** size of font glyph cache in kilobytes. */ +NSOPTION_INTEGER(fb_font_cachesize, 2048) + +/* Font face paths. These are treated as absolute paths if they start + * with a / otherwise the compile time resource path is searched. + */ +NSOPTION_STRING(fb_face_sans_serif, NULL) +NSOPTION_STRING(fb_face_sans_serif_bold, NULL) +NSOPTION_STRING(fb_face_sans_serif_italic, NULL) +NSOPTION_STRING(fb_face_sans_serif_italic_bold, NULL) +NSOPTION_STRING(fb_face_serif, NULL) +NSOPTION_STRING(fb_face_serif_bold, NULL) +NSOPTION_STRING(fb_face_monospace, NULL) +NSOPTION_STRING(fb_face_monospace_bold, NULL) +NSOPTION_STRING(fb_face_cursive, NULL) +NSOPTION_STRING(fb_face_fantasy, NULL) + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/kolibrios/fb/schedule.c b/frontends/kolibrios/fb/schedule.c new file mode 100644 index 000000000..3a3bda63f --- /dev/null +++ b/frontends/kolibrios/fb/schedule.c @@ -0,0 +1,254 @@ +/* + * Copyright 2008 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef _TARGET_IS_KOLIBRIOS +#define _TARGET_IS_KOLIBRIOS +#endif + +#ifdef _TARGET_IS_KOLIBRIOS + + +#define timerclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0) +#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) +#define timercmp(tvp, uvp, cmp) \ + (((tvp)->tv_sec == (uvp)->tv_sec) ? \ + ((tvp)->tv_usec cmp (uvp)->tv_usec) : \ + ((tvp)->tv_sec cmp (uvp)->tv_sec)) +#define timeradd(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \ + if ((vvp)->tv_usec >= 1000000) { \ + (vvp)->tv_sec++; \ + (vvp)->tv_usec -= 1000000; \ + } \ + } while (0) +#define timersub(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ + if ((vvp)->tv_usec < 0) { \ + (vvp)->tv_sec--; \ + (vvp)->tv_usec += 1000000; \ + } \ + } while (0) + +#endif + +#include +#include + +#include "utils/sys_time.h" +#include "utils/log.h" + +#include "framebuffer/schedule.h" + +#ifdef DEBUG_SCHEDULER +#define SRLOG(x...) LOG(x) +#else +#define SRLOG(x...) ((void) 0) +#endif + +/* linked list of scheduled callbacks */ +static struct nscallback *schedule_list = NULL; + +/** + * scheduled callback. + */ +struct nscallback +{ + struct nscallback *next; + struct timeval tv; + void (*callback)(void *p); + void *p; +}; + +/** + * Unschedule a callback. + * + * \param callback callback function + * \param p user parameter, passed to callback function + * + * All scheduled callbacks matching both callback and p are removed. + */ +static nserror schedule_remove(void (*callback)(void *p), void *p) +{ + struct nscallback *cur_nscb; + struct nscallback *prev_nscb; + struct nscallback *unlnk_nscb; + + /* check there is something on the list to remove */ + if (schedule_list == NULL) { + return NSERROR_OK; + } + + SRLOG("removing %p, %p", callback, p); + + cur_nscb = schedule_list; + prev_nscb = NULL; + + while (cur_nscb != NULL) { + if ((cur_nscb->callback == callback) && + (cur_nscb->p == p)) { + /* item to remove */ + + SRLOG("callback entry %p removing %p(%p)", + cur_nscb, cur_nscb->callback, cur_nscb->p); + + /* remove callback */ + unlnk_nscb = cur_nscb; + cur_nscb = unlnk_nscb->next; + + if (prev_nscb == NULL) { + schedule_list = cur_nscb; + } else { + prev_nscb->next = cur_nscb; + } + free (unlnk_nscb); + } else { + /* move to next element */ + prev_nscb = cur_nscb; + cur_nscb = prev_nscb->next; + } + } + + return NSERROR_OK; +} + +/* exported function documented in framebuffer/schedule.h */ +nserror framebuffer_schedule(int tival, void (*callback)(void *p), void *p) +{ + struct nscallback *nscb; + struct timeval tv; + nserror ret; + + /* ensure uniqueness of the callback and context */ + ret = schedule_remove(callback, p); + if ((tival < 0) || (ret != NSERROR_OK)) { + return ret; + } + + SRLOG("Adding %p(%p) in %d", callback, p, tival); + + tv.tv_sec = tival / 1000; /* miliseconds to seconds */ + tv.tv_usec = (tival % 1000) * 1000; /* remainder to microseconds */ + + nscb = calloc(1, sizeof(struct nscallback)); + + gettimeofday(&nscb->tv, NULL); + timeradd(&nscb->tv, &tv, &nscb->tv); + + nscb->callback = callback; + nscb->p = p; + + /* add to list front */ + nscb->next = schedule_list; + schedule_list = nscb; + + return NSERROR_OK; +} + +/* exported function documented in framebuffer/schedule.h */ +int schedule_run(void) +{ + struct timeval tv; + struct timeval nexttime; + struct timeval rettime; + struct nscallback *cur_nscb; + struct nscallback *prev_nscb; + struct nscallback *unlnk_nscb; + + if (schedule_list == NULL) + return -1; + + /* reset enumeration to the start of the list */ + cur_nscb = schedule_list; + prev_nscb = NULL; + nexttime = cur_nscb->tv; + + gettimeofday(&tv, NULL); + + while (cur_nscb != NULL) { + if (timercmp(&tv, &cur_nscb->tv, >)) { + /* scheduled time */ + + /* remove callback */ + unlnk_nscb = cur_nscb; + + if (prev_nscb == NULL) { + schedule_list = unlnk_nscb->next; + } else { + prev_nscb->next = unlnk_nscb->next; + } + + unlnk_nscb->callback(unlnk_nscb->p); + + free(unlnk_nscb); + + /* need to deal with callback modifying the list. */ + if (schedule_list == NULL) + return -1; /* no more callbacks scheduled */ + + /* reset enumeration to the start of the list */ + cur_nscb = schedule_list; + prev_nscb = NULL; + nexttime = cur_nscb->tv; + } else { + /* if the time to the event is sooner than the + * currently recorded soonest event record it + */ + if (timercmp(&nexttime, &cur_nscb->tv, >)) { + nexttime = cur_nscb->tv; + } + /* move to next element */ + prev_nscb = cur_nscb; + cur_nscb = prev_nscb->next; + } + } + + /* make rettime relative to now */ + timersub(&nexttime, &tv, &rettime); + + SRLOG("returning time to next event as %ldms",(rettime.tv_sec * 1000) + (rettime.tv_usec / 1000)); + + /* return next event time in milliseconds (24days max wait) */ + return (rettime.tv_sec * 1000) + (rettime.tv_usec / 1000); +} + +void list_schedule(void) +{ + struct timeval tv; + struct nscallback *cur_nscb; + + gettimeofday(&tv, NULL); + + LOG("schedule list at %ld:%ld", tv.tv_sec, tv.tv_usec); + + cur_nscb = schedule_list; + + while (cur_nscb != NULL) { + LOG("Schedule %p at %ld:%ld", cur_nscb, cur_nscb->tv.tv_sec, cur_nscb->tv.tv_usec); + cur_nscb = cur_nscb->next; + } +} + + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/kolibrios/fb/schedule.h b/frontends/kolibrios/fb/schedule.h new file mode 100644 index 000000000..4e94da68e --- /dev/null +++ b/frontends/kolibrios/fb/schedule.h @@ -0,0 +1,45 @@ +/* + * Copyright 2008 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf 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; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef FRAMEBUFFER_SCHEDULE_H +#define FRAMEBUFFER_SCHEDULE_H + +/** + * Schedule a callback. + * + * \param tival interval before the callback should be made in ms + * \param callback callback function + * \param p user parameter, passed to callback function + * + * The callback function will be called as soon as possible after t ms have + * passed. + */ + +nserror framebuffer_schedule(int tival, void (*callback)(void *p), void *p); + +/** + * Process scheduled callbacks up to current time. + * + * @return The number of milliseconds untill the next scheduled event + * or -1 for no event. + */ +int schedule_run(void); + +void list_schedule(void); + +#endif -- cgit v1.2.3