netsurf/framebuffer/gui.c
Vincent Sanders 14e2829489 remove the die API from the core.
The die() API for abnormal termination does not belong within the core
of netsurf and instead errors are propogated back to the callers.

This is the final part of this change and the API is now only used within
some parts of the frontends
2014-10-26 12:42:53 +00:00

2202 lines
51 KiB
C

/*
* Copyright 2008, 2014 Vincent Sanders <vince@netsurf-browser.org>
*
* 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 <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <limits.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <libnsfb.h>
#include <libnsfb_plot.h>
#include <libnsfb_event.h>
#include "utils/utils.h"
#include "utils/nsoption.h"
#include "utils/filepath.h"
#include "utils/log.h"
#include "utils/messages.h"
#include "utils/types.h"
#include "desktop/browser.h"
#include "desktop/textinput.h"
#include "desktop/browser_history.h"
#include "desktop/plotters.h"
#include "desktop/gui_window.h"
#include "desktop/gui_misc.h"
#include "desktop/netsurf.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 "content/urldb.h"
#include "content/fetch.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)
{
LOG(("%s", error));
exit(1);
}
/* 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);
LOG(("%d,%d to %d,%d",
bwidget->redraw_box.x0,
bwidget->redraw_box.y0,
bwidget->redraw_box.x1,
bwidget->redraw_box.y1));
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;
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(argc, argv, "f:b:w:h:")) != -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;
unsigned int time_now;
static struct {
enum { CLICK_SINGLE, CLICK_DOUBLE, CLICK_TRIPLE } type;
unsigned int 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;
time_now = wallclock();
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 (50cs) between clicks */
if (time_now < last_click.time + 50 &&
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, KEY_DELETE_RIGHT);
break;
case NSFB_KEY_PAGEUP:
if (browser_window_key_press(gw->bw,
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,
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,
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,
KEY_WORD_RIGHT) == false)
widget_scroll_x(gw, fbtk_get_width(
gw->browser), false);
} else {
/* no modifier */
if (browser_window_key_press(gw->bw,
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,
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,
KEY_WORD_LEFT) == false)
widget_scroll_x(gw, -fbtk_get_width(
gw->browser), false);
} else {
/* no modifier */
if (browser_window_key_press(gw->bw,
KEY_LEFT) == false)
widget_scroll_x(gw, -100, false);
}
break;
case NSFB_KEY_UP:
if (browser_window_key_press(gw->bw,
KEY_UP) == false)
widget_scroll_y(gw, -100, false);
break;
case NSFB_KEY_DOWN:
if (browser_window_key_press(gw->bw,
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, 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, 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, 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) {
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;
if (*itmtype == 0) {
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 void
gui_window_set_url(struct gui_window *g, const char *url)
{
fbtk_set_text(g->url, url);
}
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_browser_table framebuffer_browser_table = {
.schedule = framebuffer_schedule,
.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 = {
.browser = &framebuffer_browser_table,
.window = &framebuffer_window_table,
.clipboard = framebuffer_clipboard_table,
.fetch = framebuffer_fetch_table,
.utf8 = framebuffer_utf8_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);
/* common initialisation */
messages = filepath_find(respaths, "Messages");
ret = netsurf_init(messages, NULL);
free(messages);
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) {
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:
*/