netsurf/windows/drawable.c
Vincent Sanders c105738fa3 Change LOG() macro to be varadic
This changes the LOG macro to be varadic removing the need for all
callsites to have double bracketing and allows for future improvement
on how we use the logging macros.

The callsites were changed with coccinelle and the changes checked by
hand. Compile tested for several frontends but not all.

A formatting annotation has also been added which allows the compiler
to check the parameters and types passed to the logging.
2015-05-28 16:08:46 +01:00

626 lines
14 KiB
C

/*
* Copyright 2011 Vincent Sanders <vince@simtec.co.uk>
*
* 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 <stdbool.h>
#include "utils/config.h"
#include <windows.h>
#include <windowsx.h>
#include "desktop/browser.h"
#include "desktop/textinput.h"
#include "desktop/plotters.h"
#include "utils/errors.h"
#include "utils/log.h"
#include "utils/utils.h"
#include "windows/windbg.h"
#include "windows/plot.h"
#include "windows/window.h"
#include "windows/localhistory.h"
#include "windows/drawable.h"
static const char windowclassname_drawable[] = "nswsdrawablewindow";
/**
* Handle wheel scroll messages.
*/
static LRESULT
nsws_drawable_wheel(struct gui_window *gw, HWND hwnd, WPARAM wparam)
{
int i, z = GET_WHEEL_DELTA_WPARAM(wparam) / WHEEL_DELTA;
int key = LOWORD(wparam);
DWORD command;
unsigned int newmessage = WM_VSCROLL;
if (key == MK_SHIFT) {
command = (z > 0) ? SB_LINERIGHT : SB_LINELEFT;
newmessage = WM_HSCROLL;
} else {
/* add MK_CONTROL -> zoom */
command = (z > 0) ? SB_LINEUP : SB_LINEDOWN;
}
z = (z < 0) ? -1 * z : z;
for (i = 0; i < z; i++) {
SendMessage(hwnd, newmessage, MAKELONG(command, 0), 0);
}
return 0;
}
/**
* Handle vertical scroll messages.
*/
static LRESULT
nsws_drawable_vscroll(struct gui_window *gw, HWND hwnd, WPARAM wparam)
{
int width, height;
SCROLLINFO si;
int mem;
LOG("VSCROLL %d", gw->requestscrolly);
if (gw->requestscrolly != 0)
return 0;
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
GetScrollInfo(hwnd, SB_VERT, &si);
mem = si.nPos;
switch (LOWORD(wparam)) {
case SB_TOP:
si.nPos = si.nMin;
break;
case SB_BOTTOM:
si.nPos = si.nMax;
break;
case SB_LINEUP:
si.nPos -= 30;
break;
case SB_LINEDOWN:
si.nPos += 30;
break;
case SB_PAGEUP:
si.nPos -= gw->height;
break;
case SB_PAGEDOWN:
si.nPos += gw->height;
break;
case SB_THUMBTRACK:
si.nPos = si.nTrackPos;
break;
default:
break;
}
si.fMask = SIF_POS;
if ((gw->bw != NULL) &&
(browser_window_get_extents(gw->bw, true,
&width, &height) == NSERROR_OK)) {
si.nPos = min(si.nPos, height - gw->height);
}
si.nPos = max(si.nPos, 0);
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
GetScrollInfo(hwnd, SB_VERT, &si);
if (si.nPos != mem) {
win32_window_set_scroll(gw, gw->scrollx, gw->scrolly +
gw->requestscrolly + si.nPos - mem);
}
return 0;
}
/**
* Handle horizontal scroll messages.
*/
static LRESULT
nsws_drawable_hscroll(struct gui_window *gw, HWND hwnd, WPARAM wparam)
{
int width, height;
SCROLLINFO si;
int mem;
LOG("HSCROLL %d", gw->requestscrollx);
if (gw->requestscrollx != 0)
return 0;
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
GetScrollInfo(hwnd, SB_HORZ, &si);
mem = si.nPos;
switch (LOWORD(wparam)) {
case SB_LINELEFT:
si.nPos -= 30;
break;
case SB_LINERIGHT:
si.nPos += 30;
break;
case SB_PAGELEFT:
si.nPos -= gw->width;
break;
case SB_PAGERIGHT:
si.nPos += gw->width;
break;
case SB_THUMBTRACK:
si.nPos = si.nTrackPos;
break;
default:
break;
}
si.fMask = SIF_POS;
if ((gw->bw != NULL) &&
(browser_window_get_extents(gw->bw, true,
&width, &height) == NSERROR_OK)) {
si.nPos = min(si.nPos, width - gw->width);
}
si.nPos = max(si.nPos, 0);
SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
GetScrollInfo(hwnd, SB_HORZ, &si);
if (si.nPos != mem) {
win32_window_set_scroll(gw,
gw->scrollx + gw->requestscrollx + si.nPos - mem,
gw->scrolly);
}
return 0;
}
/**
* Handle resize events.
*/
static LRESULT
nsws_drawable_resize(struct gui_window *gw)
{
browser_window_reformat(gw->bw, false, gw->width, gw->height);
return 0;
}
/**
* Handle key press messages.
*/
static LRESULT
nsws_drawable_key(struct gui_window *gw, HWND hwnd, WPARAM wparam)
{
if (GetFocus() != hwnd)
return 0 ;
uint32_t i;
bool shift = ((GetKeyState(VK_SHIFT) & 0x8000) == 0x8000);
bool capslock = ((GetKeyState(VK_CAPITAL) & 1) == 1);
switch(wparam) {
case VK_LEFT:
i = NS_KEY_LEFT;
if (shift)
SendMessage(hwnd, WM_HSCROLL,
MAKELONG(SB_LINELEFT, 0), 0);
break;
case VK_RIGHT:
i = NS_KEY_RIGHT;
if (shift)
SendMessage(hwnd, WM_HSCROLL,
MAKELONG(SB_LINERIGHT, 0), 0);
break;
case VK_UP:
i = NS_KEY_UP;
if (shift)
SendMessage(hwnd, WM_VSCROLL,
MAKELONG(SB_LINEUP, 0), 0);
break;
case VK_DOWN:
i = NS_KEY_DOWN;
if (shift)
SendMessage(hwnd, WM_VSCROLL,
MAKELONG(SB_LINEDOWN, 0), 0);
break;
case VK_HOME:
i = NS_KEY_LINE_START;
if (shift)
SendMessage(hwnd, WM_HSCROLL,
MAKELONG(SB_PAGELEFT, 0), 0);
break;
case VK_END:
i = NS_KEY_LINE_END;
if (shift)
SendMessage(hwnd, WM_HSCROLL,
MAKELONG(SB_PAGERIGHT, 0), 0);
break;
case VK_DELETE:
i = NS_KEY_DELETE_RIGHT;
break;
case VK_NEXT:
i = wparam;
SendMessage(hwnd, WM_VSCROLL, MAKELONG(SB_PAGEDOWN, 0),
0);
break;
case VK_PRIOR:
i = wparam;
SendMessage(hwnd, WM_VSCROLL, MAKELONG(SB_PAGEUP, 0),
0);
break;
default:
i = wparam;
break;
}
if ((i >= 'A') &&
(i <= 'Z') &&
(((!capslock) && (!shift)) || ((capslock) && (shift)))) {
i += 'a' - 'A';
}
if (gw != NULL)
browser_window_key_press(gw->bw, i);
return 0;
}
/**
* Handle paint messages.
*/
static LRESULT
nsws_drawable_paint(struct gui_window *gw, HWND hwnd)
{
struct rect clip;
PAINTSTRUCT ps;
struct redraw_context ctx = {
.interactive = true,
.background_images = true,
.plot = &win_plotters
};
BeginPaint(hwnd, &ps);
if (gw != NULL) {
plot_hdc = ps.hdc;
clip.x0 = ps.rcPaint.left;
clip.y0 = ps.rcPaint.top;
clip.x1 = ps.rcPaint.right;
clip.y1 = ps.rcPaint.bottom;
browser_window_redraw(gw->bw,
-gw->scrollx / gw->scale,
-gw->scrolly / gw->scale,
&clip, &ctx);
}
EndPaint(hwnd, &ps);
return 0;
}
/**
* Handle mouse button up messages.
*/
static LRESULT
nsws_drawable_mouseup(struct gui_window *gw,
int x,
int y,
browser_mouse_state press,
browser_mouse_state click)
{
bool shift = ((GetKeyState(VK_SHIFT) & 0x8000) == 0x8000);
bool ctrl = ((GetKeyState(VK_CONTROL) & 0x8000) == 0x8000);
bool alt = ((GetKeyState(VK_MENU) & 0x8000) == 0x8000);
if ((gw == NULL) ||
(gw->mouse == NULL) ||
(gw->bw == NULL))
return 0;
LOG("state 0x%x, press 0x%x", gw->mouse->state, press);
if ((gw->mouse->state & press) != 0) {
gw->mouse->state &= ~press;
gw->mouse->state |= click;
}
if (((gw->mouse->state & BROWSER_MOUSE_MOD_1) != 0) && !shift)
gw->mouse->state &= ~BROWSER_MOUSE_MOD_1;
if (((gw->mouse->state & BROWSER_MOUSE_MOD_2) != 0) && !ctrl)
gw->mouse->state &= ~BROWSER_MOUSE_MOD_2;
if (((gw->mouse->state & BROWSER_MOUSE_MOD_3) != 0) && !alt)
gw->mouse->state &= ~BROWSER_MOUSE_MOD_3;
if ((gw->mouse->state & click) != 0) {
LOG("mouse click bw %p, state 0x%x, x %f, y %f", gw->bw, gw->mouse->state, (x + gw->scrollx) / gw->scale, (y + gw->scrolly) / gw->scale);
browser_window_mouse_click(gw->bw,
gw->mouse->state,
(x + gw->scrollx) / gw->scale,
(y + gw->scrolly) / gw->scale);
} else {
browser_window_mouse_track(gw->bw,
0,
(x + gw->scrollx) / gw->scale,
(y + gw->scrolly) / gw->scale);
}
gw->mouse->state = 0;
return 0;
}
/**
* Handle mouse button down messages.
*/
static LRESULT
nsws_drawable_mousedown(struct gui_window *gw,
int x, int y,
browser_mouse_state button)
{
if ((gw == NULL) ||
(gw->mouse == NULL) ||
(gw->bw == NULL)) {
nsws_localhistory_close(gw);
return 0;
}
gw->mouse->state = button;
if ((GetKeyState(VK_SHIFT) & 0x8000) == 0x8000)
gw->mouse->state |= BROWSER_MOUSE_MOD_1;
if ((GetKeyState(VK_CONTROL) & 0x8000) == 0x8000)
gw->mouse->state |= BROWSER_MOUSE_MOD_2;
if ((GetKeyState(VK_MENU) & 0x8000) == 0x8000)
gw->mouse->state |= BROWSER_MOUSE_MOD_3;
gw->mouse->pressed_x = (x + gw->scrollx) / gw->scale;
gw->mouse->pressed_y = (y + gw->scrolly) / gw->scale;
LOG("mouse click bw %p, state %x, x %f, y %f", gw->bw, gw->mouse->state, (x + gw->scrollx) / gw->scale, (y + gw->scrolly) / gw->scale);
browser_window_mouse_click(gw->bw, gw->mouse->state,
(x + gw->scrollx) / gw->scale,
(y + gw->scrolly) / gw->scale);
return 0;
}
/**
* Handle mouse movement messages.
*/
static LRESULT
nsws_drawable_mousemove(struct gui_window *gw, int x, int y)
{
bool shift = ((GetKeyState(VK_SHIFT) & 0x8000) == 0x8000);
bool ctrl = ((GetKeyState(VK_CONTROL) & 0x8000) == 0x8000);
bool alt = ((GetKeyState(VK_MENU) & 0x8000) == 0x8000);
if ((gw == NULL) || (gw->mouse == NULL) || (gw->bw == NULL))
return 0;
/* scale co-ordinates */
x = (x + gw->scrollx) / gw->scale;
y = (y + gw->scrolly) / gw->scale;
/* if mouse button held down and pointer moved more than
* minimum distance drag is happening */
if (((gw->mouse->state & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2)) != 0) &&
(abs(x - gw->mouse->pressed_x) >= 5) &&
(abs(y - gw->mouse->pressed_y) >= 5)) {
LOG("Drag start state 0x%x", gw->mouse->state);
if ((gw->mouse->state & BROWSER_MOUSE_PRESS_1) != 0) {
browser_window_mouse_click(gw->bw, BROWSER_MOUSE_DRAG_1,
gw->mouse->pressed_x,
gw->mouse->pressed_y);
gw->mouse->state &= ~BROWSER_MOUSE_PRESS_1;
gw->mouse->state |= BROWSER_MOUSE_HOLDING_1 |
BROWSER_MOUSE_DRAG_ON;
}
else if ((gw->mouse->state & BROWSER_MOUSE_PRESS_2) != 0) {
browser_window_mouse_click(gw->bw, BROWSER_MOUSE_DRAG_2,
gw->mouse->pressed_x,
gw->mouse->pressed_y);
gw->mouse->state &= ~BROWSER_MOUSE_PRESS_2;
gw->mouse->state |= BROWSER_MOUSE_HOLDING_2 |
BROWSER_MOUSE_DRAG_ON;
}
}
if (((gw->mouse->state & BROWSER_MOUSE_MOD_1) != 0) && !shift)
gw->mouse->state &= ~BROWSER_MOUSE_MOD_1;
if (((gw->mouse->state & BROWSER_MOUSE_MOD_2) != 0) && !ctrl)
gw->mouse->state &= ~BROWSER_MOUSE_MOD_2;
if (((gw->mouse->state & BROWSER_MOUSE_MOD_3) != 0) && !alt)
gw->mouse->state &= ~BROWSER_MOUSE_MOD_3;
browser_window_mouse_track(gw->bw, gw->mouse->state, x, y);
return 0;
}
/**
* Called when activity occours within the drawable window.
*/
static LRESULT CALLBACK
nsws_window_drawable_event_callback(HWND hwnd,
UINT msg,
WPARAM wparam,
LPARAM lparam)
{
struct gui_window *gw;
LOG_WIN_MSG(hwnd, msg, wparam, lparam);
gw = nsws_get_gui_window(hwnd);
if (gw == NULL) {
LOG("Unable to find gui window structure for hwnd %p", hwnd);
return DefWindowProc(hwnd, msg, wparam, lparam);
}
switch(msg) {
case WM_MOUSEMOVE:
return nsws_drawable_mousemove(gw,
GET_X_LPARAM(lparam),
GET_Y_LPARAM(lparam));
case WM_LBUTTONDOWN:
nsws_drawable_mousedown(gw,
GET_X_LPARAM(lparam),
GET_Y_LPARAM(lparam),
BROWSER_MOUSE_PRESS_1);
SetFocus(hwnd);
nsws_localhistory_close(gw);
return 0;
break;
case WM_RBUTTONDOWN:
nsws_drawable_mousedown(gw,
GET_X_LPARAM(lparam),
GET_Y_LPARAM(lparam),
BROWSER_MOUSE_PRESS_2);
SetFocus(hwnd);
return 0;
break;
case WM_LBUTTONUP:
return nsws_drawable_mouseup(gw,
GET_X_LPARAM(lparam),
GET_Y_LPARAM(lparam),
BROWSER_MOUSE_PRESS_1,
BROWSER_MOUSE_CLICK_1);
case WM_RBUTTONUP:
return nsws_drawable_mouseup(gw,
GET_X_LPARAM(lparam),
GET_Y_LPARAM(lparam),
BROWSER_MOUSE_PRESS_2,
BROWSER_MOUSE_CLICK_2);
case WM_ERASEBKGND: /* ignore as drawable window is redrawn on paint */
return 0;
case WM_PAINT: /* redraw the exposed part of the window */
return nsws_drawable_paint(gw, hwnd);
case WM_KEYDOWN:
return nsws_drawable_key(gw, hwnd, wparam);
case WM_SIZE:
return nsws_drawable_resize(gw);
case WM_HSCROLL:
return nsws_drawable_hscroll(gw, hwnd, wparam);
case WM_VSCROLL:
return nsws_drawable_vscroll(gw, hwnd, wparam);
case WM_MOUSEWHEEL:
return nsws_drawable_wheel(gw, hwnd, wparam);
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
/**
* Create a drawable window.
*/
HWND
nsws_window_create_drawable(HINSTANCE hinstance,
HWND hparent,
struct gui_window *gw)
{
HWND hwnd;
hwnd = CreateWindow(windowclassname_drawable,
NULL,
WS_VISIBLE | WS_CHILD,
0, 0, 0, 0,
hparent,
NULL,
hinstance,
NULL);
if (hwnd == NULL) {
win_perror("WindowCreateDrawable");
LOG("Window creation failed");
return NULL;
}
/* set the gui window associated with this toolbar */
SetProp(hwnd, TEXT("GuiWnd"), (HANDLE)gw);
return hwnd;
}
/**
* Create the drawable window class.
*/
nserror
nsws_create_drawable_class(HINSTANCE hinstance) {
nserror ret = NSERROR_OK;
WNDCLASSEX w;
/* drawable area */
w.cbSize = sizeof(WNDCLASSEX);
w.style = 0;
w.lpfnWndProc = nsws_window_drawable_event_callback;
w.cbClsExtra = 0;
w.cbWndExtra = 0;
w.hInstance = hinstance;
w.hIcon = NULL;
w.hCursor = NULL;
w.hbrBackground = (HBRUSH)(COLOR_MENU + 1);
w.lpszMenuName = NULL;
w.lpszClassName = windowclassname_drawable;
w.hIconSm = NULL;
if (RegisterClassEx(&w) == 0) {
win_perror("DrawableClass");
ret = NSERROR_INIT_FAILED;
}
return ret;
}