remote/handlers/RemoteRenderHandler.cpp (311 lines of code) (raw):
#include "RemoteRenderHandler.h"
#include "RemoteClientHandler.h"
#include "../CefUtils.h"
#include "../Utils.h"
#include "../browser/RemoteBrowser.h"
#include "../log/Log.h"
using namespace boost::interprocess;
// TODO: Optimize RemoteRenderHandler.
// Need to perform all calculations on server. Client should regularly update data on server.
const bool doTrace = getBoolEnv("CEF_SERVER_TRACE_RemoteRenderHandler");
RemoteRenderHandler::RemoteRenderHandler(std::shared_ptr<RpcExecutor> service): myService(service) {}
bool RemoteRenderHandler::GetRootScreenRect(CefRefPtr<CefBrowser> browser,
CefRect& rect) {
GetViewRect(browser, rect);
return rect.width > 1;
}
void fillDummy(CefRect& rect) {
rect.x = 0;
rect.y = 0;
rect.width = 300;
rect.height = 200;
}
void RemoteRenderHandler::GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) {
fillDummy(rect);
thrift_codegen::Rect result;
result.w = -1; // invalidate
FIND_BID_OR_RETURN();
myService->exec([&](const JavaService& s){
s->RenderHandler_GetViewRect(result, bid);
});
if (result.w < 0) {
if (doTrace)
Log::trace("RemoteRenderHandler::GetViewRect: bid=%d, result.w = %d", bid, result.w);
return;
}
rect.x = result.x;
rect.y = result.y;
rect.width = result.w;
rect.height = result.h;
if (rect.width < 1 || rect.height < 1) {
Log::trace("RemoteRenderHandler::GetViewRect: bid=%d, small size %d %d", bid, rect.width, rect.height);
fillDummy(rect);
}
if (doTrace)
Log::trace("RemoteRenderHandler::GetViewRect: bid=%d, result: %d %d %d %d", bid, rect.x, rect.y, rect.width, rect.height);
}
void fillDummy(CefScreenInfo& screen_info) {
screen_info.device_scale_factor = 2;
screen_info.depth = 1;
screen_info.depth_per_component = 1;
screen_info.is_monochrome = false;
screen_info.rect.x = 0;
screen_info.rect.y = 0;
screen_info.rect.width = 500;
screen_info.rect.height = 700;
screen_info.available_rect.x = 0;
screen_info.available_rect.y = 0;
screen_info.available_rect.width = 500;
screen_info.available_rect.height = 700;
}
///
// Called to allow the client to fill in the CefScreenInfo object with
// appropriate values. Return true if the |screen_info| structure has been
// modified.
//
// If the screen info rectangle is left empty the rectangle from GetViewRect
// will be used. If the rectangle is still empty or invalid popups may not be
// drawn correctly.
///
/*--cef()--*/
bool RemoteRenderHandler::GetScreenInfo(CefRefPtr<CefBrowser> browser,
CefScreenInfo& screen_info) {
fillDummy(screen_info);
thrift_codegen::ScreenInfo result;
result.depth = -1;// invalidate
FIND_BID_OR_RETURN_VAL(false);
myService->exec([&](const JavaService& s){
s->RenderHandler_GetScreenInfo(result, bid);
});
if (result.depth == -1) {
if (doTrace)
Log::trace("RemoteRenderHandler::GetScreenInfo: bid=%d, result.depth == -1", bid);
return false;
}
screen_info.device_scale_factor =
static_cast<float>(result.device_scale_factor);
screen_info.depth = result.depth;
screen_info.depth_per_component = result.depth_per_component;
screen_info.is_monochrome = result.is_monochrome;
screen_info.rect.x = result.rect.x;
screen_info.rect.y = result.rect.y;
screen_info.rect.width = result.rect.w;
screen_info.rect.height = result.rect.h;
screen_info.available_rect.x = result.available_rect.x;
screen_info.available_rect.y = result.available_rect.y;
screen_info.available_rect.width = result.available_rect.w;
screen_info.available_rect.height = result.available_rect.h;
if (doTrace)
Log::trace("RemoteRenderHandler::GetScreenInfo: bid=%d, result: rc %d %d %d %d, avail %d %d %d %d", bid, result.rect.x, result.rect.y, result.rect.w, result.rect.h, result.available_rect.x, result.available_rect.y, result.available_rect.w, result.available_rect.w);
return true;
}
bool RemoteRenderHandler::GetScreenPoint(CefRefPtr<CefBrowser> browser,
int viewX,
int viewY,
int& screenX,
int& screenY) {
thrift_codegen::Point result;
result.x = INT32_MIN;// invalidate
FIND_BID_OR_RETURN_VAL(false);
myService->exec([&](const JavaService& s){
s->RenderHandler_GetScreenPoint(result, bid, viewX, viewY);
});
if (result.x == INT32_MIN) {
if (doTrace)
Log::trace("RemoteRenderHandler::GetScreenPoint: bid=%d, result.x == INT32_MIN", bid);
return false;
}
screenX = result.x;
screenY = result.y;
if (doTrace)
Log::trace("RemoteRenderHandler::GetScreenPoint: bid=%d, result: %d %d", result.x, result.y, bid);
return true;
}
void RemoteRenderHandler::OnPopupShow(CefRefPtr<CefBrowser> browser, bool show) {
FIND_BID_OR_RETURN();
if (doTrace)
Log::trace("RemoteRenderHandler::OnPopupShow: bid=%d, show=%d", bid, show ? 1 : 0);
myService->exec([&](const JavaService& s){
s->RenderHandler_OnPopupShow(bid, show);
});
}
void RemoteRenderHandler::OnPopupSize(CefRefPtr<CefBrowser> browser,
const CefRect& rect) {
FIND_BID_OR_RETURN();
if (doTrace)
Log::trace("RemoteRenderHandler::OnPopupSize: bid=%d, x=%d y=%d w=%d h=%d)", bid, rect.x, rect.y, rect.width, rect.height);
myService->exec([&](const JavaService& s) {
thrift_codegen::Rect size;
size.x = rect.x;
size.y = rect.y;
size.w = rect.width;
size.h = rect.height;
s->RenderHandler_OnPopupSize(bid, size);
});
}
//
// Debug methods
//
inline void fillRect(unsigned char * dst, int stride, int y, int x, int dx, int dy, int r, int g, int b, int a, int width, int height) {
if (y >= height)
return;
if (x >= width)
return;
if (y < 0) {
dy += y;
y = 0;
}
if (y + dy >= height)
dy = height - y;
if (x < 0) {
dx += x;
x = 0;
}
if (x + dx >= width)
dx = width - x;
for (int yy = y, yEnd = y + dy; yy < yEnd; ++yy) {
const int offset = yy*stride;
for (int xx = x, xEnd = x + dx; xx < xEnd; ++xx) {
dst[offset + xx*4] = a; // alpha
dst[offset + xx*4 + 1] = r; // red
dst[offset + xx*4 + 2] = g; // green
dst[offset + xx*4 + 3] = b; // blue
}
}
}
inline void drawLineX(unsigned char * dst, int stride, int y, int x, int dx, int r, int g, int b, int a, int width, int height) {
if (y < 0)
return;
if (y >= height)
return;
if (x >= width)
return;
if (x < 0) {
dx += x;
x = 0;
}
if (x + dx >= width)
dx = width - x;
const int offset = y*stride;
for (int xx = x, xEnd = x + dx; xx < xEnd; ++xx) {
dst[offset + xx*4] = a; // alpha
dst[offset + xx*4 + 1] = r; // red
dst[offset + xx*4 + 2] = g; // green
dst[offset + xx*4 + 3] = b; // blue
}
}
inline void drawLineY(unsigned char * dst, int stride, int y, int x, int dy, int r, int g, int b, int a, int width, int height) {
if (x < 0)
return;
if (y >= height)
return;
if (x >= width)
return;
if (y < 0) {
dy += y;
y = 0;
}
if (y + dy >= height)
dy = height - y;
for (int yy = y, yEnd = y + dy; yy < yEnd; ++yy) {
const int offset = yy*stride;
dst[offset + x*4] = a; // alpha
dst[offset + x*4 + 1] = r; // red
dst[offset + x*4 + 2] = g; // green
dst[offset + x*4 + 3] = b; // blue
}
}
inline void drawRect(unsigned char * dst, int stride, int y, int x, int width, int height, int r, int g, int b, int a, int totalWidth, int totalHeight) {
const int thickness = 50;
fillRect(dst, stride, y - thickness/2, x, width, thickness, r, g, b, a, totalWidth, totalHeight);
fillRect(dst, stride, y, x + width - thickness/2, thickness, height, r, g, b, a, totalWidth, totalHeight);
fillRect(dst, stride, y + height - thickness/2, x, width, thickness, r, g, b, a, totalWidth, totalHeight);
fillRect(dst, stride, y, x - thickness/2, thickness, height, r, g, b, a, totalWidth, totalHeight);
}
inline void semifillRect(unsigned char * dst, int stride, int y, int x, int width, int height, int r, int g, int b, int a, int totalWidth, int totalHeight) {
for (int xEnd = x + width; x < xEnd; x += 2)
drawLineY(dst, stride, y, x, height, r, g, b, a, totalWidth, totalHeight);
}
void RemoteRenderHandler::OnPaint(CefRefPtr<CefBrowser> browser,
PaintElementType type,
const RectList& dirtyRects,
const void* buffer,
int width,
int height) {
FIND_BID_OR_RETURN();
if (doTrace)
Log::trace("RemoteRenderHandler::OnPaint: bid=%d, width=%d height=%d, dirty rects count %d", bid, width, height, dirtyRects.size());
const int rasterPixCount = width * height;
const size_t extendedRectsCount = dirtyRects.size() < 10 ? 10 : dirtyRects.size();
SharedBufferManager& bufferManager =
type == PET_POPUP ? rb->popup() : rb->page();
SharedBuffer* buff = bufferManager.getLockedBuffer(
rasterPixCount * 4 + 4 * 4 * extendedRectsCount);
if (buff == nullptr)
return; // NOTE: error was logged in getLockedBuffer()
if (buff->ptr() == nullptr) {
Log::error("SharedBuffer is empty.");
return;
}
::memcpy((char*)buff->ptr(), (char*)buffer, rasterPixCount*4);
int32_t * sharedRects = (int32_t *)buff->ptr() + rasterPixCount;
for (const CefRect& r : dirtyRects) {
*(sharedRects++) = r.x;
*(sharedRects++) = r.y;
*(sharedRects++) = r.width;
*(sharedRects++) = r.height;
}
{ // Draw debug
static int drawDebug = -1;
if (drawDebug < 0)
drawDebug = getBoolEnv("CEF_SERVER_DRAW_DEBUG") ? 1 : 0;
if (drawDebug > 0) {
const int stride = width*4;
const int th = 30;
fillRect((unsigned char *)buff->ptr(), stride, 0, 0, th, th, 255, 0, 0, 255, width, height);
fillRect((unsigned char *)buff->ptr(), stride, 0, width - th, th, th, 0, 255, 0, 255, width, height);
fillRect((unsigned char *)buff->ptr(), stride, height - th, width - th, th, th, 0, 0, 255, 255, width, height);
fillRect((unsigned char *)buff->ptr(), stride, height - th, 0, th, th, 255, 0, 255, 255, width, height);
}
}
buff->unlock();
myService->exec([&](const JavaService& s){
s->RenderHandler_OnPaint(bid, type != PET_VIEW, static_cast<int>(dirtyRects.size()),
buff->uid(), buff->handle(),
width, height);
});
}
bool RemoteRenderHandler::StartDragging(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDragData> drag_data,
DragOperationsMask allowed_ops,
int x,
int y) {
Log::error("Unimplemented.");
return false;
}
void RemoteRenderHandler::UpdateDragCursor(CefRefPtr<CefBrowser> browser,
DragOperation operation) {
Log::error("Unimplemented.");
}
void RemoteRenderHandler::OnImeCompositionRangeChanged(
CefRefPtr<CefBrowser> browser,
const CefRange& cef_selected_range,
const RectList& cef_character_bounds) {
FIND_BID_OR_RETURN();
if (doTrace)
Log::trace("RemoteRenderHandler::OnImeCompositionRangeChanged: bid=%d, cef_character_bounds.size=%d", bid, cef_character_bounds.size());
myService->exec([&](const JavaService& s) {
thrift_codegen::Range selected_range;
selected_range.from = cef_selected_range.from;
selected_range.to = cef_selected_range.to;
std::vector<thrift_codegen::Rect> character_bounds;
for (const auto& r : cef_character_bounds) {
character_bounds.emplace_back();
character_bounds.back().x = r.x;
character_bounds.back().y = r.y;
character_bounds.back().w = r.width;
character_bounds.back().h = r.height;
}
s->RenderHandler_OnImeCompositionRangeChanged(bid, selected_range,
character_bounds);
});
}
void RemoteRenderHandler::OnTextSelectionChanged(
CefRefPtr<CefBrowser> browser,
const CefString& selected_text,
const CefRange& cef_selected_range) {
FIND_BID_OR_RETURN();
if (doTrace)
Log::trace("RemoteRenderHandler::OnTextSelectionChanged: bid=%d", bid);
thrift_codegen::Range selected_range;
selected_range.from = cef_selected_range.from;
selected_range.to = cef_selected_range.to;
myService->exec([&](const JavaService& s) {
s->RenderHandler_OnTextSelectionChanged(bid, selected_text.ToString(), selected_range);
});
}