mirror of
https://github.com/netsurf-browser/netsurf
synced 2024-12-24 13:06:49 +03:00
69d9d5dd22
The reformat API locking semantics are challenging to understand, efectiveky browser_window_reformat should never be called by frontend code directly except explicitly on the reformat callback. Otherwise the content may already be locked and the system winds up asserting.
626 lines
13 KiB
C
626 lines
13 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_schedule_reformat(gw->bw);
|
|
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;
|
|
}
|