summaryrefslogtreecommitdiff
path: root/framebuffer/fbtk.c
diff options
context:
space:
mode:
Diffstat (limited to 'framebuffer/fbtk.c')
-rw-r--r--framebuffer/fbtk.c1337
1 files changed, 1337 insertions, 0 deletions
diff --git a/framebuffer/fbtk.c b/framebuffer/fbtk.c
new file mode 100644
index 000000000..21e796eac
--- /dev/null
+++ b/framebuffer/fbtk.c
@@ -0,0 +1,1337 @@
+/*
+ * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
+ *
+ * Framebuffer windowing toolkit
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include <libnsfb.h>
+#include <libnsfb_plot.h>
+#include <libnsfb_plot_util.h>
+#include <libnsfb_event.h>
+#include <libnsfb_cursor.h>
+
+#include "utils/log.h"
+#include "css/css.h"
+#include "desktop/browser.h"
+#include "desktop/plotters.h"
+
+#include "framebuffer/gui.h"
+#include "framebuffer/fbtk.h"
+#include "framebuffer/bitmap.h"
+#include "framebuffer/image_data.h"
+
+static struct css_style root_style;
+
+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,
+};
+
+typedef struct fbtk_widget_list_s fbtk_widget_list_t;
+
+/* wrapper struct for all widget types */
+struct fbtk_widget_s {
+ /* Generic properties */
+ int x;
+ int y;
+ int width;
+ int height;
+ colour bg;
+ colour fg;
+
+ /* handlers */
+ fbtk_mouseclick_t click;
+ void *clickpw; /* private data for callback */
+
+ fbtk_input_t input;
+ void *inputpw; /* private data for callback */
+
+ fbtk_move_t move;
+ void *movepw; /* private data for callback */
+
+ fbtk_redraw_t redraw;
+ void *redrawpw; /* private data for callback */
+
+ bool redraw_required;
+
+ fbtk_widget_t *parent; /* parent widget */
+
+ /* Widget specific */
+ enum fbtk_widgettype_e type;
+
+ union {
+ /* toolkit base handle */
+ struct {
+ nsfb_t *fb;
+ fbtk_widget_t *rootw;
+ fbtk_widget_t *input;
+ } root;
+
+ /* window */
+ struct {
+ /* widgets associated with this window */
+ fbtk_widget_list_t *widgets; /* begining of list */
+ fbtk_widget_list_t *widgets_end; /* end of list */
+ } window;
+
+ /* bitmap */
+ struct {
+ struct bitmap *bitmap;
+ } bitmap;
+
+ /* text */
+ struct {
+ char* text;
+ bool outline;
+ fbtk_enter_t enter;
+ void *pw;
+ int idx;
+ } text;
+
+ /* application driven widget */
+ struct {
+ void *pw; /* private data for user widget */
+ } user;
+
+ struct {
+ int pos;
+ int pct;
+ } scroll;
+
+ } u;
+};
+
+/* widget list */
+struct fbtk_widget_list_s {
+ struct fbtk_widget_list_s *next;
+ struct fbtk_widget_list_s *prev;
+ fbtk_widget_t *widget;
+} ;
+
+enum {
+ POINT_LEFTOF_REGION = 1,
+ POINT_RIGHTOF_REGION = 2,
+ POINT_ABOVE_REGION = 4,
+ POINT_BELOW_REGION = 8,
+};
+
+#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) )
+
+#define SWAP(a, b) do { int t; t=(a); (a)=(b); (b)=t; } while(0)
+
+/* clip a rectangle to another rectangle */
+bool fbtk_clip_rect(const bbox_t * restrict clip, bbox_t * restrict box)
+{
+ uint8_t region1;
+ uint8_t region2;
+
+ 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;
+}
+
+
+/* creates a new widget of a given type */
+static fbtk_widget_t *
+new_widget(enum fbtk_widgettype_e type)
+{
+ fbtk_widget_t *neww;
+ neww = calloc(1, sizeof(fbtk_widget_t));
+ neww->type = type;
+ return neww;
+}
+
+
+/* find the root widget from any widget in the toolkits hierarchy */
+static fbtk_widget_t *
+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;
+}
+
+
+/* set widget to be redrawn */
+void
+fbtk_request_redraw(fbtk_widget_t *widget)
+{
+ widget->redraw_required = 1;
+
+ if (widget->type == FB_WIDGET_TYPE_WINDOW) {
+ fbtk_widget_list_t *lent = widget->u.window.widgets;
+
+ while (lent != NULL) {
+ lent->widget->redraw_required = 1;
+ lent = lent->next;
+ }
+ }
+
+ while (widget->parent != NULL) {
+ widget = widget->parent;
+ widget->redraw_required = 1;
+ }
+}
+
+static fbtk_widget_t *
+add_widget_to_window(fbtk_widget_t *window, fbtk_widget_t *widget)
+{
+ fbtk_widget_list_t *newent;
+ fbtk_widget_list_t **nextent;
+ fbtk_widget_list_t *prevent; /* previous entry pointer */
+
+ if (window->type == FB_WIDGET_TYPE_WINDOW) {
+ /* caller attached widget to a window */
+
+ nextent = &window->u.window.widgets;
+ prevent = NULL;
+ while (*nextent != NULL) {
+ prevent = (*nextent);
+ nextent = &(prevent->next);
+ }
+
+ newent = calloc(1, sizeof(struct fbtk_widget_list_s));
+
+ newent->widget = widget;
+ newent->next = NULL;
+ newent->prev = prevent;
+
+ *nextent = newent;
+
+ window->u.window.widgets_end = newent;
+ }
+ widget->parent = window;
+
+ fbtk_request_redraw(widget);
+
+ return widget;
+}
+
+static void
+remove_widget_from_window(fbtk_widget_t *window, fbtk_widget_t *widget)
+{
+ fbtk_widget_list_t *lent = window->u.window.widgets;
+
+ while ((lent != NULL) && (lent->widget != widget)) {
+ lent = lent->next;
+ }
+
+ if (lent != NULL) {
+ if (lent->prev == NULL) {
+ window->u.window.widgets = lent->next;
+ } else {
+ lent->prev->next = lent->next;
+ }
+ if (lent->next == NULL) {
+ window->u.window.widgets_end = lent->prev;
+ } else {
+ lent->next->prev = lent->prev;
+ }
+ free(lent);
+ }
+}
+
+static void
+fbtk_redraw_widget(fbtk_widget_t *root, fbtk_widget_t *widget)
+{
+ nsfb_bbox_t saved_plot_ctx;
+ nsfb_bbox_t plot_ctx;
+
+ //LOG(("widget %p type %d", widget, widget->type));
+ if (widget->redraw_required == false)
+ return;
+
+ widget->redraw_required = false;
+
+ /* ensure there is a redraw handler */
+ if (widget->redraw == NULL)
+ return;
+
+ /* get the current clipping rectangle */
+ nsfb_plot_get_clip(root->u.root.fb, &saved_plot_ctx);
+
+ plot_ctx.x0 = fbtk_get_x(widget);
+ plot_ctx.y0 = fbtk_get_y(widget);
+ plot_ctx.x1 = plot_ctx.x0 + widget->width;
+ plot_ctx.y1 = plot_ctx.y0 + widget->height;
+
+ /* clip widget to the current area and redraw if its exposed */
+ if (nsfb_plot_clip(&saved_plot_ctx, &plot_ctx )) {
+
+ nsfb_plot_set_clip(root->u.root.fb, &plot_ctx);
+
+ /* do our drawing according to type */
+ widget->redraw(root, widget, widget->redrawpw);
+
+ /* restore clipping rectangle */
+ nsfb_plot_set_clip(root->u.root.fb, &saved_plot_ctx);
+
+ //LOG(("OS redrawing %d,%d %d,%d", fb_plot_ctx.x0, fb_plot_ctx.y0, fb_plot_ctx.x1, fb_plot_ctx.y1));
+ }
+
+}
+
+/*************** redraw widgets **************/
+
+static int
+fb_redraw_fill(fbtk_widget_t *root, fbtk_widget_t *widget, void *pw)
+{
+ nsfb_bbox_t bbox;
+ fbtk_get_bbox(widget, &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);
+ }
+
+ nsfb_release(root->u.root.fb, &bbox);
+ return 0;
+}
+
+static int
+fb_redraw_hscroll(fbtk_widget_t *root, fbtk_widget_t *widget, void *pw)
+{
+ int hscroll;
+ int hpos;
+ nsfb_bbox_t bbox;
+ nsfb_bbox_t rect;
+
+ fbtk_get_bbox(widget, &bbox);
+
+ nsfb_claim(root->u.root.fb, &bbox);
+
+ rect = bbox;
+
+ nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg);
+
+ rect.x0 = bbox.x0 + 1;
+ rect.y0 = bbox.y0 + 3;
+ rect.x1 = bbox.x1 - 1;
+ rect.y1 = bbox.y1 - 3;
+
+ nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->fg);
+
+ rect.x0 = bbox.x0;
+ rect.y0 = bbox.y0 + 2;
+ rect.x1 = bbox.x1 - 1;
+ rect.y1 = bbox.y1 - 5;
+
+ nsfb_plot_rectangle(root->u.root.fb, &rect, 1, 0xFF000000, false, false);
+
+ hscroll = ((widget->width - 4) * widget->u.scroll.pct) / 100 ;
+ hpos = ((widget->width - 4) * widget->u.scroll.pos) / 100 ;
+
+ 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_release(root->u.root.fb, &bbox);
+
+ return 0;
+}
+
+static int
+fb_redraw_vscroll(fbtk_widget_t *root, fbtk_widget_t *widget, void *pw)
+{
+ int vscroll;
+ int vpos;
+
+ nsfb_bbox_t bbox;
+ nsfb_bbox_t rect;
+
+ fbtk_get_bbox(widget, &bbox);
+
+ nsfb_claim(root->u.root.fb, &bbox);
+
+ rect = bbox;
+
+ nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg);
+
+ rect.x0 = bbox.x0 + 1;
+ rect.y0 = bbox.y0 + 3;
+ rect.x1 = bbox.x1 - 1;
+ rect.y1 = bbox.y1 - 3;
+
+ nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->fg);
+
+ rect.x0 = bbox.x0;
+ rect.y0 = bbox.y0 + 2;
+ rect.x1 = bbox.x1 - 1;
+ rect.y1 = bbox.y1 - 5;
+
+ nsfb_plot_rectangle(root->u.root.fb, &rect, 1, 0xFF000000, false, false);
+
+ vscroll = ((widget->height - 4) * widget->u.scroll.pct) / 100 ;
+ vpos = ((widget->height - 4) * widget->u.scroll.pos) / 100 ;
+
+ LOG(("scroll %d",vscroll));
+
+ rect.x0 = bbox.x0 + 3 ;
+ rect.y0 = bbox.y0 + 5 + vpos;
+ rect.x1 = bbox.x0 + widget->width - 3;
+ rect.y1 = bbox.y0 + vscroll + vpos - 5;
+
+ nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg);
+
+ nsfb_release(root->u.root.fb, &bbox);
+
+ return 0;
+}
+
+static int
+fb_redraw_bitmap(fbtk_widget_t *root, fbtk_widget_t *widget, void *pw)
+{
+ nsfb_bbox_t bbox;
+ nsfb_bbox_t rect;
+
+ 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);
+ }
+
+ /* plot the image */
+ nsfb_plot_bitmap(root->u.root.fb, &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_release(root->u.root.fb, &bbox);
+
+ return 0;
+}
+
+static int
+fbtk_window_default_redraw(fbtk_widget_t *root, fbtk_widget_t *window, void *pw)
+{
+ fbtk_widget_list_t *lent;
+ int res = 0;
+
+ if (!window->redraw)
+ return res;
+
+ /* get the list of widgets */
+ lent = window->u.window.widgets;
+
+ while (lent != NULL) {
+ fbtk_redraw_widget(root, lent->widget);
+ lent = lent->next;
+ }
+ return res;
+}
+
+static int
+fbtk_window_default_move(fbtk_widget_t *window, int x, int y, void *pw)
+{
+ fbtk_widget_list_t *lent;
+ fbtk_widget_t *widget;
+ int res = 0;
+
+ /* get the list of widgets */
+ lent = window->u.window.widgets_end;
+
+ while (lent != NULL) {
+ widget = lent->widget;
+
+ if ((x > widget->x) &&
+ (y > widget->y) &&
+ (x < widget->x + widget->width) &&
+ (y < widget->y + widget->height)) {
+ if (widget->move != NULL) {
+ res = widget->move(widget,
+ x - widget->x,
+ y - widget->y,
+ widget->movepw);
+ }
+ break;
+ }
+ lent = lent->prev;
+ }
+ return res;
+}
+
+static int
+fbtk_window_default_click(fbtk_widget_t *window, nsfb_event_t *event, int x, int y, void *pw)
+{
+ fbtk_widget_list_t *lent;
+ fbtk_widget_t *widget;
+ int res = 0;
+
+ /* get the list of widgets */
+ lent = window->u.window.widgets;
+
+ while (lent != NULL) {
+ widget = lent->widget;
+
+ if ((x > widget->x) &&
+ (y > widget->y) &&
+ (x < widget->x + widget->width) &&
+ (y < widget->y + widget->height)) {
+ if (widget->input != NULL) {
+ fbtk_widget_t *root = get_root_widget(widget);
+ root->u.root.input = widget;
+ }
+
+ if (widget->click != NULL) {
+ res = widget->click(widget,
+ event,
+ x - widget->x,
+ y - widget->y,
+ widget->clickpw);
+ break;
+ }
+
+
+
+ }
+ lent = lent->next;
+ }
+ return res;
+}
+
+static int
+fb_redraw_text(fbtk_widget_t *root, fbtk_widget_t *widget, void *pw)
+{
+ nsfb_bbox_t bbox;
+ nsfb_bbox_t rect;
+
+ 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);
+ }
+
+
+ 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) {
+ plot.text(bbox.x0 + 3,
+ bbox.y0 + 17,
+ &root_style,
+ widget->u.text.text,
+ strlen(widget->u.text.text),
+ widget->bg,
+ widget->fg);
+ }
+
+ nsfb_release(root->u.root.fb, &bbox);
+
+ return 0;
+}
+
+
+
+
+static int
+text_input(fbtk_widget_t *widget, nsfb_event_t *event, void *pw)
+{
+ int value;
+ if (event == NULL) {
+ /* gain focus */
+ if (widget->u.text.text == NULL)
+ widget->u.text.text = calloc(1,1);
+ widget->u.text.idx = strlen(widget->u.text.text);
+
+ fbtk_request_redraw(widget);
+
+ return 0;
+
+ }
+
+ if (event->type != NSFB_EVENT_KEY_DOWN)
+ return 0;
+
+ value = event->value.keycode;
+ switch (value) {
+ case NSFB_KEY_BACKSPACE:
+ if (widget->u.text.idx <= 0)
+ break;
+ widget->u.text.idx--;
+ widget->u.text.text[widget->u.text.idx] = 0;
+ break;
+
+ case NSFB_KEY_RETURN:
+ widget->u.text.enter(widget->u.text.pw, widget->u.text.text);
+ break;
+
+ default:
+ /* allow for new character and null */
+ widget->u.text.text = realloc(widget->u.text.text, widget->u.text.idx + 2);
+ widget->u.text.text[widget->u.text.idx] = value;
+ widget->u.text.text[widget->u.text.idx + 1] = '\0';
+ widget->u.text.idx++;
+ break;
+ }
+
+ fbtk_request_redraw(widget);
+
+ return 0;
+}
+
+/* sets the enter action on a writable icon */
+void
+fbtk_writable_text(fbtk_widget_t *widget, fbtk_enter_t enter, void *pw)
+{
+ widget->u.text.enter = enter;
+ widget->u.text.pw = pw;
+
+ widget->input = text_input;
+ widget->inputpw = widget;
+}
+
+
+/********** acessors ***********/
+int
+fbtk_get_height(fbtk_widget_t *widget)
+{
+ return widget->height;
+}
+
+int
+fbtk_get_width(fbtk_widget_t *widget)
+{
+ return widget->width;
+}
+
+int
+fbtk_get_x(fbtk_widget_t *widget)
+{
+ int x = widget->x;
+
+ while (widget->parent != NULL) {
+ widget = widget->parent;
+ x += widget->x;
+ }
+
+ return x;
+}
+
+int
+fbtk_get_y(fbtk_widget_t *widget)
+{
+ int y = widget->y;
+
+ while (widget->parent != NULL) {
+ widget = widget->parent;
+ y += widget->y;
+ }
+
+ return y;
+}
+
+/* get widgets bounding box in screen co-ordinates */
+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;
+
+ while (widget->parent != NULL) {
+ widget = widget->parent;
+ bbox->x0 += widget->x;
+ bbox->y0 += widget->y;
+ bbox->x1 += widget->x;
+ bbox->y1 += widget->y;
+ }
+
+ return true;
+}
+
+void
+fbtk_set_handler_click(fbtk_widget_t *widget, fbtk_mouseclick_t click, void *pw)
+{
+ widget->click = click;
+ widget->clickpw = pw;
+}
+
+void
+fbtk_set_handler_input(fbtk_widget_t *widget, fbtk_input_t input, void *pw)
+{
+ widget->input = input;
+ widget->inputpw = pw;
+}
+
+void
+fbtk_set_handler_redraw(fbtk_widget_t *widget, fbtk_redraw_t redraw, void *pw)
+{
+ widget->redraw = redraw;
+ widget->redrawpw = pw;
+}
+
+void
+fbtk_set_handler_move(fbtk_widget_t *widget, fbtk_move_t move, void *pw)
+{
+ widget->move = move;
+ widget->movepw = pw;
+}
+
+void *
+fbtk_get_userpw(fbtk_widget_t *widget)
+{
+ if ((widget == NULL) || (widget->type != FB_WIDGET_TYPE_USER))
+ return NULL;
+
+ return widget->u.user.pw;
+}
+
+void
+fbtk_set_text(fbtk_widget_t *widget, const char *text)
+{
+ 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.idx = strlen(text);
+
+ fbtk_request_redraw(widget);
+}
+
+void
+fbtk_set_scroll(fbtk_widget_t *widget, int pct)
+{
+ if (widget == NULL)
+ return;
+
+ if ((widget->type == FB_WIDGET_TYPE_HSCROLL) ||
+ (widget->type == FB_WIDGET_TYPE_VSCROLL)) {
+
+ widget->u.scroll.pct = pct;
+ fbtk_request_redraw(widget);
+ }
+}
+
+void
+fbtk_set_scroll_pos(fbtk_widget_t *widget, int pos)
+{
+ if (widget == NULL)
+ return;
+
+ if ((widget->type == FB_WIDGET_TYPE_HSCROLL) ||
+ (widget->type == FB_WIDGET_TYPE_VSCROLL)) {
+
+ widget->u.scroll.pos = pos;
+
+ fbtk_request_redraw(widget);
+ }
+}
+
+void
+fbtk_set_bitmap(fbtk_widget_t *widget, struct bitmap *image)
+{
+ if ((widget == NULL) || (widget->type != FB_WIDGET_TYPE_BITMAP))
+ return;
+
+ widget->u.bitmap.bitmap = image;
+
+ fbtk_request_redraw(widget);
+}
+
+void
+fbtk_set_pos_and_size(fbtk_widget_t *widget, int x, int y, int width, int height)
+{
+ 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;
+ fbtk_request_redraw(widget);
+ LOG(("%d,%d %d,%d",x,y,width,height));
+ }
+}
+
+int
+fbtk_count_children(fbtk_widget_t *widget)
+{
+ int count = 0;
+ fbtk_widget_list_t *lent;
+
+ if (widget->type != FB_WIDGET_TYPE_WINDOW) {
+ if (widget->type != FB_WIDGET_TYPE_ROOT)
+ return -1;
+ widget = widget->u.root.rootw;
+ }
+
+ lent = widget->u.window.widgets;
+
+ while (lent != NULL) {
+ count++;
+ lent = lent->next;
+ }
+
+ return count;
+}
+
+
+void
+fbtk_input(fbtk_widget_t *root, nsfb_event_t *event)
+{
+ fbtk_widget_t *input;
+
+ root = get_root_widget(root);
+
+ /* obtain widget with input focus */
+ input = root->u.root.input;
+ if (input == NULL)
+ return; /* no widget with input */
+
+ if (input->input == NULL)
+ return;
+
+ /* call the widgets input method */
+ input->input(input, event, input->inputpw);
+}
+
+void
+fbtk_click(fbtk_widget_t *widget, nsfb_event_t *event)
+{
+ fbtk_widget_t *root;
+ fbtk_widget_t *window;
+ nsfb_bbox_t cloc;
+
+ /* ensure we have the root widget */
+ root = get_root_widget(widget);
+
+ nsfb_cursor_loc_get(root->u.root.fb, &cloc);
+
+ /* get the root window */
+ window = root->u.root.rootw;
+ LOG(("click %d, %d",cloc.x0,cloc.y0));
+ if (window->click != NULL)
+ window->click(window, event, cloc.x0, cloc.y0, window->clickpw);
+}
+
+
+
+void
+fbtk_move_pointer(fbtk_widget_t *widget, int x, int y, bool relative)
+{
+ fbtk_widget_t *root;
+ fbtk_widget_t *window;
+ nsfb_bbox_t cloc;
+
+ /* ensure we have the root widget */
+ root = 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;
+ }
+
+ root->redraw_required = true;
+
+ nsfb_cursor_loc_set(root->u.root.fb, &cloc);
+
+ /* get the root window */
+ window = root->u.root.rootw;
+
+ if (window->move != NULL)
+ window->move(window, cloc.x0, cloc.y0, window->movepw);
+
+}
+
+int
+fbtk_redraw(fbtk_widget_t *widget)
+{
+ fbtk_widget_t *root;
+
+ /* ensure we have the root widget */
+ root = get_root_widget(widget);
+
+ if (!root->redraw_required)
+ return 0;
+
+ fbtk_redraw_widget(root, root->u.root.rootw);
+
+ return 1;
+}
+
+/****** widget destruction ********/
+int fbtk_destroy_widget(fbtk_widget_t *widget)
+{
+ if (widget->type == FB_WIDGET_TYPE_WINDOW) {
+ /* TODO: walk child widgets and destroy them */
+ }
+
+ remove_widget_from_window(widget->parent, widget);
+ free(widget);
+
+ return 0;
+}
+
+
+/************** Widget creation *************/
+fbtk_widget_t *
+fbtk_create_text(fbtk_widget_t *window,
+ int x, int y,
+ int width, int height,
+ colour bg, colour fg,
+ bool outline)
+{
+ fbtk_widget_t *newt = new_widget(FB_WIDGET_TYPE_TEXT);
+
+ newt->x = x;
+ newt->y = y;
+ newt->width = width;
+ newt->height = height;
+ newt->u.text.outline = outline;
+
+ newt->fg = fg;
+ newt->bg = bg;
+
+ newt->redraw = fb_redraw_text;
+
+ return add_widget_to_window(window, newt);
+}
+
+fbtk_widget_t *
+fbtk_create_bitmap(fbtk_widget_t *window, int x, int y, colour c, struct bitmap *image)
+{
+ fbtk_widget_t *newb = new_widget(FB_WIDGET_TYPE_BITMAP);
+
+ newb->x = x;
+ newb->y = y;
+ newb->width = image->width;
+ newb->height = image->height;
+ newb->bg = c;
+
+ newb->u.bitmap.bitmap = image;
+
+ newb->redraw = fb_redraw_bitmap;
+
+ return add_widget_to_window(window, newb);
+}
+
+static void
+fbtk_width_height(fbtk_widget_t *parent, int x, int y, int *width, int *height)
+{
+ /* make widget fit inside parent */
+ if (*width == 0) {
+ *width = parent->width - x;
+ } else if (*width < 0) {
+ *width = parent->width + *width;
+ }
+ if ((*width + x) > parent->width) {
+ *width = parent->width - x;
+ }
+
+ if (*height == 0) {
+ *height = parent->height - y;
+ } else if (*height < 0) {
+ *height = parent->height + *height;
+ }
+ if ((*height + y) > parent->height) {
+ *height = parent->height - y;
+ }
+}
+
+fbtk_widget_t *
+fbtk_create_fill(fbtk_widget_t *window, int x, int y, int width, int height, colour c)
+{
+ fbtk_widget_t *neww = new_widget(FB_WIDGET_TYPE_FILL);
+
+ neww->x = x;
+ neww->y = y;
+ neww->width = width;
+ neww->height = height;
+
+ fbtk_width_height(window, x, y, &neww->width, &neww->height);
+
+ neww->bg = c;
+
+ neww->redraw = fb_redraw_fill;
+
+ return add_widget_to_window(window, neww);
+}
+
+fbtk_widget_t *
+fbtk_create_hscroll(fbtk_widget_t *window, int x, int y, int width, int height, colour fg, colour bg)
+{
+ fbtk_widget_t *neww = new_widget(FB_WIDGET_TYPE_HSCROLL);
+
+ neww->x = x;
+ neww->y = y;
+ neww->width = width;
+ neww->height = height;
+ neww->fg = fg;
+ neww->bg = bg;
+
+ neww->redraw = fb_redraw_hscroll;
+
+ return add_widget_to_window(window, neww);
+}
+
+fbtk_widget_t *
+fbtk_create_vscroll(fbtk_widget_t *window, int x, int y, int width, int height, colour fg, colour bg)
+{
+ fbtk_widget_t *neww = new_widget(FB_WIDGET_TYPE_VSCROLL);
+
+ neww->x = x;
+ neww->y = y;
+ neww->width = width;
+ neww->height = height;
+ neww->fg = fg;
+ neww->bg = bg;
+
+ neww->redraw = fb_redraw_vscroll;
+
+ return add_widget_to_window(window, neww);
+}
+
+fbtk_widget_t *
+fbtk_create_button(fbtk_widget_t *window,
+ int x, int y,
+ colour c,
+ struct bitmap *image,
+ fbtk_mouseclick_t click,
+ void *pw)
+{
+ fbtk_widget_t *newb = fbtk_create_bitmap(window, x, y, c, image);
+
+ newb->click = click;
+ newb->clickpw = pw;
+
+ return newb;
+}
+
+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)
+{
+ fbtk_widget_t *newt = fbtk_create_text(window, x, y, width, height, bg,fg,outline);
+ newt->u.text.enter = enter;
+ newt->u.text.pw = pw;
+
+ newt->input = text_input;
+ newt->inputpw = newt;
+ return newt;
+}
+
+/* create user widget
+ *
+ * @param x coord relative to parent
+ */
+fbtk_widget_t *
+fbtk_create_user(fbtk_widget_t *window,
+ int x, int y,
+ int width, int height,
+ void *pw)
+{
+ fbtk_widget_t *newu = new_widget(FB_WIDGET_TYPE_USER);
+
+
+ /* make new window fit inside parent */
+ if (width == 0) {
+ width = window->width - x;
+ } else if (width < 0) {
+ width = window->width + width;
+ }
+ if ((width + x) > window->width) {
+ width = window->width - x;
+ }
+
+ if (height == 0) {
+ height = window->height - y;
+ } else if (height < 0) {
+ height = window->height + height;
+ }
+ if ((height + y) > window->height) {
+ height = window->height - y;
+ }
+
+ newu->x = x;
+ newu->y = y;
+ newu->width = width;
+ newu->height = height;
+
+ newu->u.user.pw = pw;
+
+ return add_widget_to_window(window, newu);
+}
+
+
+/* create new window
+ *
+ * @param x coord relative to parent
+ */
+fbtk_widget_t *
+fbtk_create_window(fbtk_widget_t *parent,
+ int x, int y, int width, int height)
+{
+ fbtk_widget_t *newwin;
+
+ LOG(("Creating window %p %d,%d %d,%d",parent,x,y,width,height));
+ if (parent == NULL)
+ return NULL;
+
+ if ((parent->type == FB_WIDGET_TYPE_ROOT) &&
+ (parent->u.root.rootw != NULL)) {
+ LOG(("Using root window"));
+ parent = parent->u.root.rootw;
+ }
+
+ newwin = new_widget(FB_WIDGET_TYPE_WINDOW);
+
+ /* make new window fit inside parent */
+ if (width == 0) {
+ width = parent->width - x;
+ } else if (width < 0) {
+ width = parent->width + width;
+ }
+ if ((width + x) > parent->width) {
+ width = parent->width - x;
+ }
+
+ if (height == 0) {
+ height = parent->height - y;
+ } else if (height < 0) {
+ height = parent->height + height;
+ }
+ if ((height + y) > parent->height) {
+ height = parent->height - y;
+ }
+
+ newwin->x = x;
+ newwin->y = y;
+ newwin->width = width;
+ newwin->height = height;
+
+ newwin->redraw = fbtk_window_default_redraw;
+ newwin->move = fbtk_window_default_move;
+ newwin->click = fbtk_window_default_click;
+
+ LOG(("Created window %p %d,%d %d,%d",newwin,x,y,width,height));
+
+ return add_widget_to_window(parent, newwin);
+}
+
+bool fbtk_event(fbtk_widget_t *root, nsfb_event_t *event, int timeout)
+{
+ bool unused = false; /* is the event available */
+
+ /* ensure we have the root widget */
+ root = get_root_widget(root);
+
+ //LOG(("Reading event with timeout %d",timeout));
+
+ if (nsfb_event(root->u.root.fb, event, timeout) == false)
+ return false;
+
+ switch (event->type) {
+ case NSFB_EVENT_KEY_DOWN:
+ case NSFB_EVENT_KEY_UP:
+ if ((event->value.controlcode >= NSFB_KEY_MOUSE_1) &&
+ (event->value.controlcode <= 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:
+ fbtk_move_pointer(root, event->value.vector.x, event->value.vector.y, true);
+ break;
+
+ case NSFB_EVENT_MOVE_ABSOLUTE:
+ fbtk_move_pointer(root, event->value.vector.x, event->value.vector.y, false);
+ break;
+
+ default:
+ break;
+
+ }
+ return unused;
+}
+
+
+nsfb_t *
+fbtk_get_nsfb(fbtk_widget_t *widget)
+{
+ fbtk_widget_t *root;
+
+ /* ensure we have the root widget */
+ root = get_root_widget(widget);
+
+ return root->u.root.fb;
+}
+
+/* Initialise toolkit for use */
+fbtk_widget_t *
+fbtk_init(nsfb_t *fb)
+{
+ fbtk_widget_t *root = new_widget(FB_WIDGET_TYPE_ROOT);
+
+ nsfb_get_geometry(fb, &root->width, &root->height, NULL);
+
+ LOG(("width %d height %d",root->width, root->height));
+ root->u.root.fb = fb;
+ root->x = 0;
+ root->y = 0;
+ root->u.root.rootw = fbtk_create_window(root, 0, 0, 0, 0);
+
+ root_style.font_size.value.length.unit = CSS_UNIT_PX;
+ root_style.font_size.value.length.value = 14;
+
+ return root;
+}
+
+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 */
+};
+
+
+/* performs character mapping */
+int fbtk_keycode_to_ucs4(int code, uint8_t mods)
+{
+ int ucs4 = -1;
+
+ if (mods) {
+ if ((code >= 0) && (code < sizeof(sh_keymap)))
+ ucs4 = sh_keymap[code];
+ } else {
+ if ((code >= 0) && (code < sizeof(keymap)))
+ ucs4 = keymap[code];
+ }
+ return ucs4;
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset:8
+ * End:
+ */