2026-02-19 21:31:49 +01:00

339 lines
10 KiB
C++

// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "util.h"
#include <windows.h>
#include <tchar.h>
#include <tlhelp32.h>
#include <windows.h>
#include <iostream>
#include <map>
#include <sstream>
#ifdef USING_JAVA
#include "client_handler.h"
#include "jni_util.h"
#include "temp_window.h"
#endif
#include "include/base/cef_callback.h"
#include "include/cef_path_util.h"
#define XBUTTON1_HI (XBUTTON1 << 16)
#undef MOUSE_MOVED
namespace util {
int GetPid() {
return (int)GetCurrentProcessId();
}
int GetParentPid() {
DWORD pid = GetCurrentProcessId();
int ppid = 0;
HANDLE hProcess;
PROCESSENTRY32 pe32;
hProcess = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcess == INVALID_HANDLE_VALUE)
return ppid;
pe32.dwSize = sizeof(PROCESSENTRY32);
if (!Process32First(hProcess, &pe32)) {
CloseHandle(hProcess);
return ppid;
}
do {
if (pe32.th32ProcessID == pid) {
ppid = (int)pe32.th32ParentProcessID;
break;
}
} while (Process32Next(hProcess, &pe32));
CloseHandle(hProcess);
return ppid;
}
std::string GetTempFileName(const std::string& identifer, bool useParentId) {
std::stringstream tmpName;
CefString tmpPath;
if (!CefGetPath(PK_DIR_TEMP, tmpPath)) {
TCHAR lpPathBuffer[MAX_PATH];
GetTempPath(MAX_PATH, lpPathBuffer);
tmpPath.FromWString(lpPathBuffer);
}
tmpName << tmpPath.ToString().c_str() << "\\";
tmpName << "jcef-p" << (useParentId ? util::GetParentPid() : util::GetPid());
tmpName << (identifer.empty() ? "" : "_") << identifer.c_str() << ".tmp";
return tmpName.str();
}
#ifdef USING_JAVA
static int getModifiers(BOOLEAN forceShift) {
ScopedJNIEnv env;
if (!env)
return 0;
int modifiers = 0;
const char* inputEventClassName = "java/awt/event/InputEvent";
JNI_STATIC_DEFINE_INT_RV(env, ScopedJNIClass(env, inputEventClassName),
ALT_DOWN_MASK, 0);
JNI_STATIC_DEFINE_INT_RV(env, ScopedJNIClass(env, inputEventClassName),
CTRL_DOWN_MASK, 0);
JNI_STATIC_DEFINE_INT_RV(env, ScopedJNIClass(env, inputEventClassName),
SHIFT_DOWN_MASK, 0);
JNI_STATIC_DEFINE_INT_RV(env, ScopedJNIClass(env, inputEventClassName),
BUTTON1_DOWN_MASK, 0);
JNI_STATIC_DEFINE_INT_RV(env, ScopedJNIClass(env, inputEventClassName),
BUTTON2_DOWN_MASK, 0);
JNI_STATIC_DEFINE_INT_RV(env, ScopedJNIClass(env, inputEventClassName),
BUTTON3_DOWN_MASK, 0);
if ((GetKeyState(VK_MENU) & 0x8000) != 0)
modifiers |= JNI_STATIC(ALT_DOWN_MASK);
if ((GetKeyState(VK_CONTROL) & 0x8000) != 0)
modifiers |= JNI_STATIC(CTRL_DOWN_MASK);
if (forceShift || (GetKeyState(VK_SHIFT) & 0x8000) != 0)
modifiers |= JNI_STATIC(SHIFT_DOWN_MASK);
if ((GetAsyncKeyState(VK_LBUTTON) & 0x8000) != 0)
modifiers |= JNI_STATIC(BUTTON1_DOWN_MASK);
if ((GetAsyncKeyState(VK_MBUTTON) & 0x8000) != 0)
modifiers |= JNI_STATIC(BUTTON2_DOWN_MASK);
if ((GetAsyncKeyState(VK_RBUTTON) & 0x8000) != 0)
modifiers |= JNI_STATIC(BUTTON3_DOWN_MASK);
return modifiers;
}
static int getMouseButton(WPARAM wParam) {
ScopedJNIEnv env;
if (!env)
return 0;
const char* mouseEventClassName = "java/awt/event/MouseEvent";
JNI_STATIC_DEFINE_INT_RV(env, ScopedJNIClass(env, mouseEventClassName),
BUTTON1, 0);
JNI_STATIC_DEFINE_INT_RV(env, ScopedJNIClass(env, mouseEventClassName),
BUTTON2, 0);
JNI_STATIC_DEFINE_INT_RV(env, ScopedJNIClass(env, mouseEventClassName),
BUTTON3, 0);
JNI_STATIC_DEFINE_INT_RV(env, ScopedJNIClass(env, mouseEventClassName),
NOBUTTON, 0);
switch (wParam) {
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_LBUTTONDBLCLK:
return JNI_STATIC(BUTTON1);
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MBUTTONDBLCLK:
return JNI_STATIC(BUTTON2);
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_RBUTTONDBLCLK:
return JNI_STATIC(BUTTON3);
default:
return JNI_STATIC(NOBUTTON);
}
}
static int getMouseEvent(WPARAM wParam) {
ScopedJNIEnv env;
if (!env)
return 0;
const char* mouseEventClassName = "java/awt/event/MouseEvent";
JNI_STATIC_DEFINE_INT_RV(env, ScopedJNIClass(env, mouseEventClassName),
MOUSE_MOVED, 0);
JNI_STATIC_DEFINE_INT_RV(env, ScopedJNIClass(env, mouseEventClassName),
MOUSE_PRESSED, 0);
JNI_STATIC_DEFINE_INT_RV(env, ScopedJNIClass(env, mouseEventClassName),
MOUSE_RELEASED, 0);
JNI_STATIC_DEFINE_INT_RV(env, ScopedJNIClass(env, mouseEventClassName),
MOUSE_WHEEL, 0);
switch (wParam) {
case WM_MOUSEMOVE:
return JNI_STATIC(MOUSE_MOVED);
// Handle a double click like a single click.
case WM_LBUTTONDBLCLK:
case WM_MBUTTONDBLCLK:
case WM_RBUTTONDBLCLK:
// FALL THRU
// Handle button down and up events.
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
return JNI_STATIC(MOUSE_PRESSED);
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
return JNI_STATIC(MOUSE_RELEASED);
// Handle horizontal mouse wheel event.
// The vertical mouse wheel is already recognized in Java.
// case WM_MOUSEWHEEL:
case WM_MOUSEHWHEEL:
return JNI_STATIC(MOUSE_WHEEL);
default:
return 0;
}
}
static std::map<CefWindowHandle, CefRefPtr<CefBrowser>> g_browsers_;
static HANDLE g_browsers_lock_ = CreateMutex(NULL, FALSE, NULL);
static HHOOK g_mouse_monitor_ = NULL;
static HHOOK g_proc_monitor_ = NULL;
static int g_mouse_monitor_refs_ = 0;
static BOOLEAN g_once_ = TRUE;
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode != HC_ACTION || lParam == NULL)
return CallNextHookEx(NULL, nCode, wParam, lParam);
MOUSEHOOKSTRUCT* pStruct = (MOUSEHOOKSTRUCT*)lParam;
// Find the CefBrowser associated with the current window hierarchy.
CefWindowHandle windowHandle = pStruct->hwnd;
CefRefPtr<CefBrowser> cefBrowser;
std::map<CefWindowHandle, CefRefPtr<CefBrowser>>::iterator it;
WaitForSingleObject(g_browsers_lock_, INFINITE);
while (windowHandle != NULL) {
it = g_browsers_.find(windowHandle);
if (it != g_browsers_.end()) {
cefBrowser = it->second;
break;
}
windowHandle = GetParent(windowHandle);
}
ReleaseMutex(g_browsers_lock_);
if (!cefBrowser)
return CallNextHookEx(NULL, nCode, wParam, lParam);
// We're creating the scoped JNI env here so it can be re-used for
// the following subfunctions, which all need an env internally,
// without having the invoking thread constantly attaching and
// detaching from the JVM (which is ugly, especially if a debugger
// is attached, but also generally detrimental to performance)
ScopedJNIEnv env;
if (!env)
return CallNextHookEx(NULL, nCode, wParam, lParam);
int mouseButton = getMouseButton(wParam);
// Horizonal wheel event is the same as vertical wheel event
// with pressed shift button.
int modifiers = getModifiers(wParam == WM_MOUSEHWHEEL);
int mouseEvent = getMouseEvent(wParam);
switch (wParam) {
// Handle horizontal mouse wheel event.
// The vertical mouse wheel is already recognized in Java.
// case WM_MOUSEWHEEL:
case WM_MOUSEHWHEEL:
mouseButton =
GET_WHEEL_DELTA_WPARAM(((MOUSEHOOKSTRUCTEX*)pStruct)->mouseData);
break;
default:
break;
}
if (mouseEvent != 0) {
// Forward params to the CefWindowHandle of Java-Canvas
CefRefPtr<ClientHandler> client =
(ClientHandler*)cefBrowser->GetHost()->GetClient().get();
CefRefPtr<WindowHandler> handler = client->GetWindowHandler();
handler->OnMouseEvent(cefBrowser, mouseEvent, pStruct->pt.x, pStruct->pt.y,
modifiers, mouseButton);
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
void AddCefBrowser(CefRefPtr<CefBrowser> browser) {
if (!browser.get())
return;
CefWindowHandle browserHandle = browser->GetHost()->GetWindowHandle();
if (!browserHandle)
return;
WaitForSingleObject(g_browsers_lock_, INFINITE);
std::pair<CefWindowHandle, CefRefPtr<CefBrowser>> pair =
std::make_pair(browserHandle, browser);
g_browsers_.insert(pair);
ReleaseMutex(g_browsers_lock_);
if (g_mouse_monitor_ == NULL) {
DWORD threadId = GetWindowThreadProcessId(browserHandle, NULL);
g_mouse_monitor_ =
SetWindowsHookEx(WH_MOUSE, util::MouseProc, NULL, threadId);
g_mouse_monitor_refs_ = 1;
} else {
g_mouse_monitor_refs_++;
}
}
void DestroyCefBrowser(CefRefPtr<CefBrowser> browser) {
if (!browser.get())
return;
CefWindowHandle browserHandle = browser->GetHost()->GetWindowHandle();
if (!browserHandle)
return;
WaitForSingleObject(g_browsers_lock_, INFINITE);
size_t erased = g_browsers_.erase(browserHandle);
DCHECK_EQ(1U, erased);
ReleaseMutex(g_browsers_lock_);
::DestroyWindow(browserHandle);
if (g_mouse_monitor_ == NULL)
return;
g_mouse_monitor_refs_--;
if (g_mouse_monitor_refs_ <= 0) {
UnhookWindowsHookEx(g_mouse_monitor_);
g_mouse_monitor_ = NULL;
}
}
CefWindowHandle GetWindowHandle(JNIEnv* env, jobject canvas) {
return GetHwndOfCanvas(canvas, env);
}
void SetParent(CefWindowHandle browserHandle,
CefWindowHandle parentHandle,
base::OnceClosure callback) {
if (parentHandle == kNullWindowHandle)
parentHandle = TempWindow::GetWindowHandle();
if (parentHandle != kNullWindowHandle && browserHandle != kNullWindowHandle)
::SetParent(browserHandle, parentHandle);
std::move(callback).Run();
}
void SetWindowBounds(CefWindowHandle browserHandle,
const CefRect& contentRect) {
HRGN contentRgn = CreateRectRgn(contentRect.x, contentRect.y,
contentRect.x + contentRect.width,
contentRect.y + contentRect.height);
SetWindowRgn(GetParent(browserHandle), contentRgn, TRUE);
}
void SetWindowSize(CefWindowHandle browserHandle, int width, int height) {
SetWindowPos(browserHandle, NULL, 0, 0, width, height,
SWP_NOZORDER | SWP_NOMOVE);
}
#endif // USING_JAVA
} // namespace util