diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 440a5fcd3..6ff68433d 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -131,6 +131,11 @@ ENDIF(WITH_STATIC) INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules/PCHSupport.cmake) +IF(WITH_CEF3) + ADD_DEFINITIONS(-DWITH_CEF3=1) + FIND_PACKAGE(CEF3 REQUIRED) +ENDIF(WITH_CEF3) + IF(FINAL_VERSION) ADD_DEFINITIONS(-DFINAL_VERSION=1) ENDIF(FINAL_VERSION) diff --git a/code/CMakeModules/FindCEF3.cmake b/code/CMakeModules/FindCEF3.cmake new file mode 100644 index 000000000..869c354b7 --- /dev/null +++ b/code/CMakeModules/FindCEF3.cmake @@ -0,0 +1,56 @@ +# +# - Locate CEF3 library +# This module defines +# CEF3_LIBRARIES the libraries to link against +# CEF3_INCLUDE_DIR where to find headers. +# + +IF(CEF3_LIBRARIES) + SET(CEF3_FIND_QUIETLY TRUE) +ENDIF() + +FIND_PATH(CEF3_INCLUDE_DIR + NAMES include/cef_version.h + PATHS ${CEF3_ROOT}) +SET(CEF3_INCLUDE_DIR ${CEF3_INCLUDE_DIR} ${CEF3_INCLUDE_DIR}/include) + +FIND_LIBRARY(CEF3_LIBRARY_RELEASE + NAMES cef + PATHS ${CEF3_ROOT} PATH_SUFFIXES Release) + +FIND_LIBRARY(CEF3_LIBRARY_DEBUG + NAMES cef + PATHS ${CEF3_ROOT} PATH_SUFFIXES Debug) +#SET(CEF3_LIBRARY +# optimized ${CEF3_LIBRARY_RELEASE} +# debug ${CEF3_LIBRARY_DEBUG}) +SET(CEF3_LIBRARY ${CEF3_LIBRARY_RELEASE}) + +# +# libcef_dll_wrapper.a +# +FIND_LIBRARY(CEF3_LIBRARY_WRAPPER_RELEASE + NAMES cef_dll_wrapper + PATHS ${CEF3_ROOT} PATH_SUFFIXES Release) + +FIND_LIBRARY(CEF3_LIBRARY_WRAPPER_DEBUG + NAMES cef_dll_wrapper + PATHS ${CEF3_ROOT} PATH_SUFFIXES Debug) +#SET(CEF3_LIBRARY_WRAPPER +# optimized ${CEF3_LIBRARY_WRAPPER_RELEASE} +# debug ${CEF3_LIBRARY_WRAPPER_DEBUG}) +SET(CEF3_LIBRARY_WRAPPER ${CEF3_LIBRARY_WRAPPER_RELEASE}) + +SET(CEF3_LIBRARIES ${CEF3_LIBRARIES} ${CEF3_LIBRARY} ${CEF3_LIBRARY_WRAPPER}) + +IF(CEF3_LIBRARY AND CEF3_INCLUDE_DIR) + SET(CEF3_FOUND "YES") + IF(NOT CEF3_FIND_QUIETLY) + MESSAGE(STATUS "Found Cef3: ${CEF3_LIBRARIES}") + ENDIF() +ELSE() + IF(NOT CEF3_FIND_QUIETLY) + MESSAGE(STATUS "Warning: Unable to find Cef3!") + ENDIF() +ENDIF() + diff --git a/code/nel/include/nel/gui/group_webkit.h b/code/nel/include/nel/gui/group_webkit.h new file mode 100644 index 000000000..9eeb4015f --- /dev/null +++ b/code/nel/include/nel/gui/group_webkit.h @@ -0,0 +1,186 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef CL_GROUP_WEBKIT_H +#define CL_GROUP_WEBKIT_H + +#include "nel/misc/types_nl.h" +#include "nel/gui/interface_group.h" +#include "nel/gui/ctrl_button.h" +#include "nel/gui/view_pointer.h" +#include "nel/gui/widget_manager.h" +#include "nel/gui/reflect.h" +#include "nel/3d/u_material.h" +#include "nel/3d/u_texture.h" + +#include "nel/gui/webkit_handler.h" + +#include + +namespace NLGUI { + + // *************************************************************************** + /** + * Chromium Embedded Framework browser impl + * + * \author Meelis Mägi + * \date 2015 + */ + class CGroupWebkit : public CInterfaceGroup + { + public: + DECLARE_UI_CLASS(CGroupWebkit) + + // Constructor + CGroupWebkit(const TCtorParam ¶m); + ~CGroupWebkit(); + + // Get Home url + std::string home(); + + // Get current url + std::string getURL() const { return _URL; } + + // Set window title + void setTitle(const std::string &title); + + // Load url into browser + void browse(const char *url); + + // Reload _URL into browser, ignoring cache + void refresh(); + + // parse html string into browser + void parseHtml(const std::string &html); + + // Browser history + void browseUndo(); + void browseRedo(); + void clearUndoRedo(); + + // set browser zoom factor + void browserZoomIn(); + void browserZoomOut(); + void browserZoomReset(); + + // browser status bar + void showHideStatus(bool show); + void setStatusMessage(const std::string &msg); + void setLoadingState(bool loading, bool undo, bool redo); + void setTooltip(const std::string &tt); + + // + virtual bool handleEvent(const NLGUI::CEventDescriptor& event); + virtual void checkCoords(); + virtual void invalidateCoords(); + virtual void onInvalidateContent(); + virtual bool parse(xmlNodePtr cur, CInterfaceGroup *parentGroup); + virtual void draw (); + + // + bool isKeyboardCaptured() const; + void setKeyboardCapture(bool capture); + void setGrabKeyboard(bool grab); + bool getGrabKeyboard() const { return _GrabKeyboard; } + void setGrabKeyboardButtonPushed(bool pushed); + + // CefRenderHandler::OnPopupSize + void setPopupSize(const CefRect &rect); + // CefRenderHandler::OnPopupShow + void setPopupVisibility(bool visible); + // CefRenderHandler::OnPaint + void drawIntoTexture(const CefRenderHandler::RectList &dirtyRects, const void *buffer, sint width, sint height, bool popup); + // CefRenderHandler::GetViewRect, GetScreenInfo + sint getTextureWidth() const { return _Texture ? _Texture->getImageWidth() : 0; }; + sint getTextureHeight() const { return _Texture ? _Texture->getImageHeight() : 0; }; + + int luaBrowse(CLuaState &ls); + int luaBrowseUndo(CLuaState &ls); + int luaBrowseRedo(CLuaState &ls); + int luaRefresh(CLuaState &ls); + int luaParseHtml(CLuaState &ls); + int luaZoomIn(CLuaState &ls); + int luaZoomOut(CLuaState &ls); + int luaZoomReset(CLuaState &ls); + + REFLECT_EXPORT_START(CGroupWebkit, CInterfaceGroup) + REFLECT_LUA_METHOD("browse", luaBrowse) + REFLECT_LUA_METHOD("browseUndo", luaBrowseUndo) + REFLECT_LUA_METHOD("browseRedo", luaBrowseRedo) + REFLECT_LUA_METHOD("refresh", luaRefresh) + REFLECT_LUA_METHOD("zoomIn", luaZoomIn) + REFLECT_LUA_METHOD("zoomOut", luaZoomOut) + REFLECT_LUA_METHOD("zoomReset", luaZoomReset) + REFLECT_LUA_METHOD("parseHtml", luaParseHtml) + REFLECT_BOOL("grab_keyboard", getGrabKeyboard, setGrabKeyboard) + REFLECT_STRING("url", getURL, browse) + REFLECT_EXPORT_END + + protected: + bool handleKeyEvent(const NLGUI::CEventDescriptorKey &event); + bool handleMouseEvent(const NLGUI::CEventDescriptorMouse &event); + void handle(); + + void setFocus(bool state); + void releaseKeyboard(); + void captureKeyboard(); + + void setProperty(const std::string &name, const std::string &value); + + private: + CefRefPtr _Browser; + CefRefPtr _BrowserHandler; + CefKeyEvent _KeyEvent; + + // used to draw _Texture and _PopupTexture + NL3D::UMaterial _Material; + // main browser window content + NL3D::UTextureMem *_Texture; + // popup (select/combo box) content + NL3D::UTextureMem *_PopupTexture; + sint _PopupX; + sint _PopupY; + + // true if mouse is over browser window + bool _Focused; + // if keyboard should always be captured when mouse is over window + bool _GrabKeyboard; + // if keyboard should be captured after gaining focus again + bool _RestoreKeyboardCapture; + // tracks window visibility + bool _LastActive; + + // + std::string _TitlePrefix; + // url that is loaded when doing ::browse('home') + std::string _Home; + // currently loaded page url + std::string _URL; + + // detect resize event + sint32 _LastParentW; + sint32 _LastParentH; + + // Status message from Cef + std::string _StatusMessage; + std::string _TooltipString; + bool _Loading; + }; + +}// namespace + +#endif // CL_GROUP_WEBKIT_H + diff --git a/code/nel/include/nel/gui/webkit_app.h b/code/nel/include/nel/gui/webkit_app.h new file mode 100644 index 000000000..545f59c2d --- /dev/null +++ b/code/nel/include/nel/gui/webkit_app.h @@ -0,0 +1,72 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef CL_WEBKIT_APP_H +#define CL_WEBKIT_APP_H + +#include "nel/misc/types_nl.h" +#include + +namespace NLGUI +{ + + extern const char kFocusedNodeChangedMessage[]; + + // *************************************************************************** + /** + * Cef client app + * + * \author Meelis Mägi + * \date 2015 + */ + class CWebkitApp: public CefApp, + public CefRenderProcessHandler + { + public: + CWebkitApp(): _LastIsEditable(false) + { + } + + ~CWebkitApp(); + + // CefApp + virtual CefRefPtr GetRenderProcessHandler() OVERRIDE + { + return this; + } + + // CefRenderProcessHandler + virtual void OnFocusedNodeChanged(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr node) OVERRIDE; + + // Chrome/39.0.2171.95 + static std::string getChromeVersion(); + + private: + bool _LastIsEditable; + + IMPLEMENT_REFCOUNTING(CWebkitApp); + }; + + // Initializes CEF3 for main thread + bool webkitInitialize(const CefMainArgs &args, const std::string &locale, const std::string &userAgent); + void webkitShutdown(); + +} // namespace + +#endif // CL_WEBKIT_APP_H + diff --git a/code/nel/include/nel/gui/webkit_handler.h b/code/nel/include/nel/gui/webkit_handler.h new file mode 100644 index 000000000..fd573a37d --- /dev/null +++ b/code/nel/include/nel/gui/webkit_handler.h @@ -0,0 +1,132 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef CL_WEBKIT_HANDLER_H +#define CL_WEBKIT_HANDLER_H + +#include +#include + +namespace NLGUI { + + class CGroupWebkit; + + // *************************************************************************** + /** + * Cef browser client + * + * \author Meelis Mägi + * \date 2015 + */ + class CWebkitHandler: public CefClient, + public CefLifeSpanHandler, + public CefLoadHandler, + public CefDisplayHandler, + public CefJSDialogHandler, + public CefRenderHandler + { + public: + CWebkitHandler(); + + void setWindow(CGroupWebkit *win); + + // CefClient + virtual CefRefPtr GetRenderHandler() { return this; } + virtual CefRefPtr GetLifeSpanHandler(){ return this; } + virtual CefRefPtr GetLoadHandler() { return this; } + virtual CefRefPtr GetDisplayHandler() { return this; } + virtual CefRefPtr GetJSDialogHandler() { return this; } + + // CefClient + virtual bool OnProcessMessageReceived(CefRefPtr browser, + CefProcessId source_process, + CefRefPtr message) OVERRIDE; + + // CefLifeSpanHandler + virtual bool OnBeforePopup(CefRefPtr browser, + CefRefPtr frame, + const CefString& target_url, + const CefString& target_frame_name, + const CefPopupFeatures& popupFeatures, + CefWindowInfo& windowInfo, + CefRefPtr& client, + CefBrowserSettings& settings, + bool* no_javascript_access) OVERRIDE; + + // CefLoadHandler + void OnLoadingStateChange(CefRefPtr browser, + bool isLoading, + bool canGoBack, + bool canGoForward) OVERRIDE; + + // CefLoadHandler + void OnLoadError(CefRefPtr browser, + CefRefPtr frame, + ErrorCode errorCode, + const CefString &errorText, + const CefString &failedUrl) OVERRIDE; + + // CefDisplayHandler + virtual void OnTitleChange(CefRefPtr browser, const CefString& title) OVERRIDE; + + // CefDisplayHandler + virtual bool OnTooltip(CefRefPtr browser, CefString &text) OVERRIDE; + + // CefDisplayHandler + virtual void OnStatusMessage(CefRefPtr browser, const CefString& value) OVERRIDE; + + // CefDisplayHandler + virtual bool OnConsoleMessage(CefRefPtr browser, + const CefString& message, + const CefString& source, + int line) OVERRIDE; + + // CefJSDialogHandler + virtual bool OnJSDialog(CefRefPtr browser, + const CefString& origin_url, + const CefString& accept_lang, + JSDialogType dialog_type, + const CefString& message_text, + const CefString& default_prompt_text, + CefRefPtr callback, + bool& suppress_message) OVERRIDE; + + //CefJSDialogHandler + virtual void OnResetDialogState(CefRefPtr browser) OVERRIDE; + + // CefRenderHandler + virtual bool GetScreenInfo(CefRefPtr browser, CefScreenInfo& screen_info) OVERRIDE; + virtual bool GetViewRect(CefRefPtr browser, CefRect &rect) OVERRIDE; + virtual void OnPopupShow(CefRefPtr browser, bool show) OVERRIDE; + virtual void OnPopupSize(CefRefPtr browser, const CefRect& rect) OVERRIDE; + virtual void OnPaint(CefRefPtr browser, PaintElementType type, const RectList &dirtyRects, const void *buffer, int width, int height) OVERRIDE; + virtual void OnCursorChange(CefRefPtr browser, CefCursorHandle cursor, CursorType type, const CefCursorInfo& custom_cursor_info) OVERRIDE; + + // called from action handler after ui dialog is closed + static void jsDialogContinue(bool success, const std::string &input); + + private: + CGroupWebkit *_Window; + + static CefRefPtr _JSDialogCallback; + + IMPLEMENT_REFCOUNTING(CWebkitHandler); + }; + +}// namespace + +#endif // CL_WEBKIT_HANDLER_H + diff --git a/code/nel/src/gui/CMakeLists.txt b/code/nel/src/gui/CMakeLists.txt index 6e82b2240..3c3e5b45f 100644 --- a/code/nel/src/gui/CMakeLists.txt +++ b/code/nel/src/gui/CMakeLists.txt @@ -1,6 +1,20 @@ FILE(GLOB SRC *.cpp *.h) FILE(GLOB HEADERS ../../include/nel/gui/*.h) +# Chromium Embbed Framework +IF(NOT WITH_CEF3) + LIST(REMOVE_ITEM SRC + ${CMAKE_CURRENT_SOURCE_DIR}/group_webkit.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/webkit_app.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/webkit_handler.cpp + ) + LIST(REMOVE_ITEM HEADERS + ${CMAKE_CURRENT_SOURCE_DIR}/../../include/nel/gui/group_webkit.h + ${CMAKE_CURRENT_SOURCE_DIR}/../../include/nel/gui/webkit_app.h + ${CMAKE_CURRENT_SOURCE_DIR}/../../include/nel/gui/webkit_handler.h + ) +ENDIF(NOT WITH_CEF3) + SOURCE_GROUP("include" FILES ${HEADERS}) SOURCE_GROUP("src" FILES ${SRC}) @@ -9,6 +23,11 @@ NL_TARGET_LIB(nelgui ${SRC} ${HEADERS}) INCLUDE_DIRECTORIES(${LUA_INCLUDE_DIR} ${LUABIND_INCLUDE_DIR} ${CURL_INCLUDE_DIRS}) TARGET_LINK_LIBRARIES(nelgui nelmisc nel3d ${LUA_LIBRARIES} ${LUABIND_LIBRARIES} ${LIBXML2_LIBRARIES} ${CURL_LIBRARIES}) +IF(WITH_CEF3) + INCLUDE_DIRECTORIES(${CEF3_INCLUDE_DIR}) + TARGET_LINK_LIBRARIES(nelgui ${CEF3_LIBRARIES}) +ENDIF(WITH_CEF3) + SET_TARGET_PROPERTIES(nelgui PROPERTIES LINK_INTERFACE_LIBRARIES "") NL_DEFAULT_PROPS(nelgui "NeL, Library: NeL GUI") NL_ADD_RUNTIME_FLAGS(nelgui) diff --git a/code/nel/src/gui/group_webkit.cpp b/code/nel/src/gui/group_webkit.cpp new file mode 100644 index 000000000..965aedb1d --- /dev/null +++ b/code/nel/src/gui/group_webkit.cpp @@ -0,0 +1,976 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "stdpch.h" + +#include "nel/gui/group_webkit.h" +#include "nel/gui/webkit_handler.h" + +#include "nel/gui/group_container.h" +#include "nel/gui/group_editbox.h" +#include "nel/gui/view_text.h" +#include "nel/gui/view_bitmap.h" +#include "nel/gui/lua_ihm.h" + +#include + +using namespace std; +using namespace NL3D; +using namespace NLMISC; +using namespace NLGUI; + +NLMISC_REGISTER_OBJECT(CViewBase, CGroupWebkit, std::string, "webkit"); + + CGroupWebkit::CGroupWebkit(const TCtorParam ¶m) +: CInterfaceGroup(param) +{ + CViewRenderer &rVR = *CViewRenderer::getInstance(); + _Material = rVR.getDriver()->createMaterial(); + _Material.initUnlit(); + _Material.setDoubleSided(); + _Material.setZWrite(false); + _Material.setZFunc(UMaterial::always); + _Material.setBlend(true); + _Material.setBlendFunc (UMaterial::srcalpha, UMaterial::invsrcalpha); + _Material.setColor(CRGBA::White); + _Material.setTexture(0, NULL); + _Material.setTexture(1, NULL); + _Material.setTexture(2, NULL); + _Material.setTexture(3, NULL); + _Material.setZBias(0); + + _Texture = NULL; + _PopupTexture = NULL; + _PopupX = 0; + _PopupY = 0; + + _Focused = false; + _GrabKeyboard = false; + _RestoreKeyboardCapture = false; + _LastActive = false; + + _TitlePrefix = ""; + _Home = ""; + _URL = ""; + + _LastParentW = 0; + _LastParentH = 0; + + _StatusMessage = ""; + _TooltipString = ""; + _Loading = false; + + CefBrowserSettings browserSettings; + CefString(&browserSettings.default_encoding).FromASCII("UTF-8"); + browserSettings.image_shrink_standalone_to_fit = STATE_ENABLED; + + CefWindowInfo window_info; + window_info.SetAsWindowless(0, true); + + // cef handler(s) + _BrowserHandler = new CWebkitHandler(); + // there is no texture yet, but set window handler for other events + _BrowserHandler->setWindow(this); + + _Browser = CefBrowserHost::CreateBrowserSync(window_info, _BrowserHandler.get(), "about:blank", browserSettings, NULL); + + // Cef message loop is depending on this + CWidgetManager::getInstance()->registerClockMsgTarget(this); +} + +// *************************************************************************** + +CGroupWebkit::~CGroupWebkit() +{ + // clear texture from CefRenderHandler + _BrowserHandler->setWindow(NULL); + // force close browser + _Browser->GetHost()->CloseBrowser(true); + + _Browser = NULL; + _BrowserHandler = NULL; + + CViewRenderer &rVR = *CViewRenderer::getInstance(); + + if (_Texture) + rVR.getDriver()->deleteTextureMem(_Texture); + + if (_PopupTexture) + rVR.getDriver()->deleteTextureMem(_PopupTexture); + + _Texture = NULL; + _PopupTexture = NULL; +} + +// *************************************************************************** + +string CGroupWebkit::home () +{ + return _Home; +} + +// *************************************************************************** +void CGroupWebkit::browse(const char *url) +{ + _URL = url; + + if (_URL.empty() || _URL == "home") + _URL = home(); + + _Browser->GetMainFrame()->LoadURL(_URL); +} + +// *************************************************************************** +void CGroupWebkit::browseUndo() +{ + _Browser->GoBack(); +} + +// *************************************************************************** +void CGroupWebkit::browseRedo() +{ + _Browser->GoForward(); +} + + +// *************************************************************************** +void CGroupWebkit::refresh() +{ + _Browser->ReloadIgnoreCache(); +} + +// *************************************************************************** +void CGroupWebkit::parseHtml(const std::string &html) +{ + _Browser->GetMainFrame()->LoadString(html, ":"); +} + +// *************************************************************************** +void CGroupWebkit::clearUndoRedo() +{ + // FIXME: missing in cef3? +} + +// *************************************************************************** +void CGroupWebkit::setProperty(const std::string &name, const std::string &value) +{ + if (name == "title_prefix") + { + if (!value.empty()) + _TitlePrefix = value + " - "; + else + _TitlePrefix = ""; + } + else + if (name == "url") + _URL = value; + else + if (name == "home") + _Home = value; + else + if (name == "grab_keyboard") + { + bool b; + if (fromString(value, b)) + _GrabKeyboard = b; + } +} + +// *************************************************************************** +bool CGroupWebkit::parse(xmlNodePtr cur,CInterfaceGroup *parentGroup) +{ + CInterfaceGroup::parse(cur, parentGroup); + + std::string props[] = { + "title_prefix", "url", "home", "grab_keyboard" + }; + uint size = sizeof(props) / sizeof(props[0]); + + CXMLAutoPtr ptr; + for (uint i=0; iGetHost()->SendKeyEvent(_KeyEvent); + return true; + } + if (event.getKeyEventType() == NLGUI::CEventDescriptorKey::keydown) + { + _KeyEvent.type = KEYEVENT_KEYDOWN; + _KeyEvent.native_key_code = event.getKey(); + _KeyEvent.windows_key_code = event.getKey(); + + _Browser->GetHost()->SendKeyEvent(_KeyEvent); + return true; + } + if (event.getKeyEventType() == NLGUI::CEventDescriptorKey::keychar) + { + _KeyEvent.character = event.getChar(); + + _KeyEvent.type = KEYEVENT_KEYUP; + _Browser->GetHost()->SendKeyEvent(_KeyEvent); + + _KeyEvent.type = KEYEVENT_CHAR; + _Browser->GetHost()->SendKeyEvent(_KeyEvent); + + return true; + } + if (event.getKeyEventType() == NLGUI::CEventDescriptorKey::keystring) + { + // CEF handles this itself when ctrl+v is pressed + + return true; + } + + return false; +} + +// *************************************************************************** +bool CGroupWebkit::handleMouseEvent(const NLGUI::CEventDescriptorMouse &event) +{ + // make sure event is for our group and not just in a window + if (!isIn(event.getX(), event.getY())) + return false; + + sint32 x = event.getX() - _XReal; + sint32 y = _YReal - event.getY() + _HReal; + + CefMouseEvent mouse_event; + mouse_event.x = x; + mouse_event.y = y; + + _Browser->GetHost()->SendMouseMoveEvent(mouse_event, false); + + if (event.getEventTypeExtended() == NLGUI::CEventDescriptorMouse::mousewheel) + { + int dx = 0; + int dy = event.getWheel()*50; + + if (_KeyEvent.type == KEYEVENT_KEYDOWN && _KeyEvent.modifiers & EVENTFLAG_CONTROL_DOWN) + { + if (dy > 0) + browserZoomIn(); + else + browserZoomOut(); + } + else + _Browser->GetHost()->SendMouseWheelEvent(mouse_event, dx, dy); + + return true; + } + + sint32 type = event.getEventTypeExtended(); + + // FIXME: double click - interpreted as up/down/up/down + if (type == NLGUI::CEventDescriptorMouse::mouseleftup || + type == NLGUI::CEventDescriptorMouse::mouseleftdown || + type == NLGUI::CEventDescriptorMouse::mouseleftdblclk) + { + bool up = (event.getEventTypeExtended() == NLGUI::CEventDescriptorMouse::mouseleftup); + + uint8 count = (type == NLGUI::CEventDescriptorMouse::mouseleftdblclk) ? 2 : 1; + + mouse_event.modifiers |= EVENTFLAG_LEFT_MOUSE_BUTTON; + _Browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, up, count); + + return true; + } + + if (type == NLGUI::CEventDescriptorMouse::mouserightup || + type == NLGUI::CEventDescriptorMouse::mouserightdown || + type == NLGUI::CEventDescriptorMouse::mouserightdblclk) + { + bool up = (event.getEventTypeExtended() == NLGUI::CEventDescriptorMouse::mouserightup); + uint8 count = (type == NLGUI::CEventDescriptorMouse::mouserightdblclk) ? 2 : 1; + + mouse_event.modifiers |= EVENTFLAG_RIGHT_MOUSE_BUTTON; + _Browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_RIGHT, up, count); + + return true; + } + + return false; +} + +// *************************************************************************** +bool CGroupWebkit::handleEvent(const NLGUI::CEventDescriptor& event) +{ + // FIXME: subscribe to keyboard event to know shift/alt/ctrl state when we dont have keyboard focus + // + if (event.getType() == NLGUI::CEventDescriptor::system) + { + const NLGUI::CEventDescriptorSystem &systemEvent = (const NLGUI::CEventDescriptorSystem &) event; + if (systemEvent.getEventTypeExtended() == NLGUI::CEventDescriptorSystem::clocktick) + { + handle(); + } + } + + if (event.getType() == NLGUI::CEventDescriptor::key) + { + if (handleKeyEvent((const NLGUI::CEventDescriptorKey &)event)) + return true; + } + + if (event.getType() == NLGUI::CEventDescriptor::mouse) + { + if (handleMouseEvent((const NLGUI::CEventDescriptorMouse &)event)) + return true; + } + + if (CInterfaceGroup::handleEvent(event)) return true; + + return false; +} + +// *************************************************************************** +bool CGroupWebkit::isKeyboardCaptured() const +{ + return (CWidgetManager::getInstance()->getCaptureKeyboard() == this); +} + +// *************************************************************************** +void CGroupWebkit::releaseKeyboard() +{ + setGrabKeyboardButtonPushed(false); + + if (! isKeyboardCaptured()) + return; + + _KeyEvent.modifiers = EVENTFLAG_NONE; + CWidgetManager::getInstance()->resetCaptureKeyboard(); +} + +// *************************************************************************** +void CGroupWebkit::captureKeyboard() +{ + setGrabKeyboardButtonPushed(true); + + if (isKeyboardCaptured()) + return; + + CWidgetManager::getInstance()->setCaptureKeyboard(this); +} + +// *************************************************************************** +void CGroupWebkit::setKeyboardCapture(bool capture) +{ + _RestoreKeyboardCapture = _GrabKeyboard || capture; + + // ignore release request if window still has mouse over it + if (capture || (_GrabKeyboard && _Focused)) + captureKeyboard(); + else + releaseKeyboard(); +} + +// *************************************************************************** +void CGroupWebkit::setGrabKeyboard(bool grab) +{ + _GrabKeyboard = grab; + setKeyboardCapture(grab); +} + +// *************************************************************************** +void CGroupWebkit::setGrabKeyboardButtonPushed(bool pushed) +{ + CInterfaceGroup* group = getParentContainer(); + if (group) + { + CCtrlBaseButton *btn = dynamic_cast(group->getCtrl("grab_keyboard")); + if (btn) + btn->setPushed(pushed); + } +} + +// *************************************************************************** +void CGroupWebkit::setFocus(bool state) +{ + if (!_Active) + return; + + if (state && ! _Focused) + { + _Focused = true; + _Browser->GetHost()->SendFocusEvent(true); + + if (_RestoreKeyboardCapture) + captureKeyboard(); + } + else + if (! state && _Focused) + { + _Focused = false; + _Browser->GetHost()->SendFocusEvent(false); + + if (isKeyboardCaptured()) + releaseKeyboard(); + } + + setGrabKeyboardButtonPushed(_Focused && _RestoreKeyboardCapture); +} + +// *************************************************************************** +void CGroupWebkit::setTitle(const std::string &title) +{ + CInterfaceElement *parent = getParent(); + if (parent) + { + if ((parent = parent->getParent())) + { + CGroupContainer *container = dynamic_cast(parent); + if (container) + { + container->setUCTitle(ucstring(_TitlePrefix + title)); + } + } + } +} + +// *************************************************************************** +void CGroupWebkit::draw () +{ + if (!_Active) + return; + + CRGBA color(CRGBA::White); + /* // CtrlQuad::draw + + if (getModulateGlobalColor()) + color.modulateFromColor(CRGBA::White, CWidgetManager::getInstance()->getGlobalColorForContent()); + else + { + color = CRGBA::White; + color.A = (uint8)(((sint32)color.A*((sint32)CWidgetManager::getInstance()->getGlobalColorForContent().A+1))>>8); + } + */ + + // get parent container content alpha value + CInterfaceGroup *gr = getParent(); + while (gr) + { + if (gr->isGroupContainer()) + { + CGroupContainer *gc = static_cast(gr); + color.A = gc->getCurrentContainerAlpha(); + break; + } + gr = gr->getParent(); + } + + CViewRenderer &rVR = *CViewRenderer::getInstance(); + if (_Texture) + { + _Material.setTexture(0, _Texture); + rVR.drawCustom(_XReal, _YReal, _WReal, _HReal, color, _Material); + } + + if (_PopupTexture) + { + // calculate bottom-left corner for popup texture inside window + sint popx = _XReal + _PopupX; + sint popy = _YReal + _HReal - _PopupY - _PopupTexture->getImageHeight(); + + _Material.setTexture(0, _PopupTexture); + rVR.drawCustom(popx, popy, _PopupTexture->getImageWidth(), _PopupTexture->getImageHeight(), + color, _Material); + } + + CInterfaceGroup::draw(); +} + +static void copyBGRAtoRGBA( + // destination buffer and it's size + uint8 *dst, sint dstw, sint dsth, + // source buffer and it's size + const uint8 *src, sint srcw, sint srch, + // rectangle to be copied + const CefRect &rect) +{ + uint bytePerPixel = 4; + + // make sure we dont overflow either buffers + sint xmax = std::min(dstw, std::min(rect.x + rect.width, rect.x + srcw)); + sint ymax = std::min(dsth, std::min(rect.y + rect.height, rect.y + srch)); + + for(sint x = rect.x; x < xmax; ++x) + { + for(sint y = rect.y; y < ymax; ++y) + { + sint pos = (x + y * srcw) * bytePerPixel; + + // RGBA <- BGRA + dst[pos + 0] = src[pos + 2]; + dst[pos + 1] = src[pos + 1]; + dst[pos + 2] = src[pos]; + dst[pos + 3] = src[pos + 3]; + } + } +} + +// *************************************************************************** +void CGroupWebkit::drawIntoTexture(const CefRenderHandler::RectList &dirtyRects, const void *buffer, sint width, sint height, bool popup) +{ + // this can be optimized by copying full buffer into texture using memcpy + // and when drawing, setting up vertex buffer with BGRA format and use driver->renderRawQuads() to draw it + + UTextureMem *tex = NULL; + if (popup) + tex = _PopupTexture; + else + tex = _Texture; + + if (tex) + { + // dirtyRects should contain only single rect (largest region to update) + if (dirtyRects.size() == 1) + { + const CefRect &rect = dirtyRects[0]; + copyBGRAtoRGBA((uint8 *)tex->getPointer(), tex->getImageWidth(), tex->getImageHeight(), + (uint8 *)buffer, width, height, + rect); + } + else + { + CefRenderHandler::RectList::const_iterator i = dirtyRects.begin(); + for(; i != dirtyRects.end(); ++i) + { + const CefRect &rect = *i; + copyBGRAtoRGBA((uint8 *)tex->getPointer(), tex->getImageWidth(), tex->getImageHeight(), + (uint8 *)buffer, width, height, + rect); + } + } + + tex->touch(); + } + else + nldebug("missing %s texture\n", popup ? "main" : "popup"); +} + +// *************************************************************************** +void CGroupWebkit::setPopupVisibility(bool visible) +{ + // hide popup by making sure texture is not set + if (_PopupTexture && !visible) + { + CViewRenderer &rVR = *CViewRenderer::getInstance(); + rVR.getDriver()->deleteTextureMem(_PopupTexture); + _PopupTexture = NULL; + } +} + +// *************************************************************************** +void CGroupWebkit::setPopupSize(const CefRect &rect) +{ + // always get rid of old popup + CViewRenderer &rVR = *CViewRenderer::getInstance(); + if (_PopupTexture) + { + rVR.getDriver()->deleteTextureMem(_PopupTexture); + _PopupTexture = NULL; + } + + if (rect.width == 0 || rect.height == 0) + return; + + // setup new popup with new texture + _PopupTexture = rVR.getDriver()->createTextureMem(rect.width, rect.height, CBitmap::RGBA); + _PopupX = rect.x; + _PopupY = rect.y; +} + +// *************************************************************************** +void CGroupWebkit::invalidateCoords() +{ + if (!_Active && _LastActive) + { + _Browser->GetHost()->SetWindowVisibility(false); + _LastActive = false; + } + else + if (_Active && !_LastActive) + { + _Browser->GetHost()->SetWindowVisibility(true); + _LastActive = true; + } + CInterfaceGroup::invalidateCoords(); +} + +// *************************************************************************** +void CGroupWebkit::checkCoords() +{ + if (!_Active) + { + if (_LastActive) + _Browser->GetHost()->SetWindowVisibility(false); + + _LastActive = false; + releaseKeyboard(); + + return; + } + + // called on each frame + if (_Parent) + { + // switch focus only when not being resized + if (CWidgetManager::getInstance()->getCapturePointerLeft() == NULL) + { + CViewPointerBase *mousePointer = CWidgetManager::getInstance()->getPointer(); + if (mousePointer) + { + if (isIn(mousePointer->getX(), mousePointer->getY())) + { + if (!_Focused) + { + // get top-most window and see if any of it belongs to us + CInterfaceGroup *ig = CWidgetManager::getInstance()->getCurrentWindowUnder(); + while(ig) + { + if (ig == _Parent->getRootWindow()) + { + setFocus(true); + break; + } + ig = ig->getParent(); + } + } + } + else + if (_Focused) + setFocus(false); + } + } + + // resize event + sint parentWidth = std::min(_Parent->getMaxWReal(), _Parent->getWReal()); + sint parentHeight = std::min(_Parent->getMaxHReal(), _Parent->getHReal()); + if (_LastParentW != (sint)parentWidth || _LastParentH != (sint)parentHeight) + { + CCtrlBase *pCB = CWidgetManager::getInstance()->getCapturePointerLeft(); + if (pCB != NULL) + { + CCtrlResizer *pCR = dynamic_cast(pCB); + if (pCR != NULL) + { + // resize in progress + } + else + { + _LastParentW = parentWidth; + _LastParentH = parentHeight; + invalidateContent(); + } + } + else + { + _LastParentW = parentWidth; + _LastParentH = parentHeight; + invalidateContent(); + } + } + } + CInterfaceGroup::checkCoords(); +} + +// *************************************************************************** +void CGroupWebkit::onInvalidateContent() +{ + if (!_Active) + return; + + CViewRenderer &rVR = *CViewRenderer::getInstance(); + + if (_Texture) + { + rVR.getDriver()->deleteTextureMem(_Texture); + _Texture = NULL; + } + else + { + // must be first call as there is no texture yet + browse(_URL.c_str()); + } + + if (_WReal > 0 && _HReal > 0) + { + // create texture same size as window + _Texture = rVR.getDriver()->createTextureMem(_WReal, _HReal, CBitmap::RGBA); + _Texture->setWrapS(NL3D::UTexture::Clamp); + _Texture->setWrapT(NL3D::UTexture::Clamp); + + _Browser->GetHost()->WasResized(); + } +} + +// *************************************************************************** +void CGroupWebkit::handle() +{ + // FIXME: auto hide status bar on timeout + //const CWidgetManager::SInterfaceTimes × = CWidgetManager::getInstance()->getInterfaceTimes(); + // times.thisFrameMs / 1000.0f; + + CefDoMessageLoopWork(); +} + +// *************************************************************************** +void CGroupWebkit::showHideStatus(bool show) +{ + CInterfaceGroup* group = getParentContainer(); + if (group) + { + CInterfaceGroup *pGroup = dynamic_cast(group->getGroup("status_bar")); + if (pGroup) + pGroup->setActive(show); + } +} + +// *************************************************************************** +void CGroupWebkit::setStatusMessage(const std::string &msg) +{ + if (_StatusMessage == msg) + return; + + _StatusMessage = msg; + showHideStatus(! _StatusMessage.empty()); + + CInterfaceGroup* group = getParentContainer(); + if (group) + { + CViewText *pView = dynamic_cast(group->getView("status")); + if (pView) + pView->setText(ucstring(msg)); + } +} + +// *************************************************************************** +void CGroupWebkit::setTooltip(const std::string &tt) +{ + if (_TooltipString == tt) + return; + + _TooltipString = tt; + setDefaultContextHelp(tt); +} + +// *************************************************************************** +void CGroupWebkit::setLoadingState(bool loading, bool undo, bool redo) +{ + if (_Loading == loading) + return; + + _Loading = loading; + + CInterfaceGroup* group = getParentContainer(); + if (group) + { + CViewBitmap *pView = dynamic_cast(group->getView("loading")); + if (pView) + { + if (loading) + { + pView->setTexture("w_online.tga"); + setTitle(_TitlePrefix + CI18N::get("uiPleaseWait").toString()); + setStatusMessage(CI18N::get("uiPleaseWait").toString()); + } + else + pView->setTexture("w_offline.tga"); + } + + CCtrlBaseButton *btnRefresh = dynamic_cast(group->getCtrl("browse_refresh")); + if (btnRefresh) + btnRefresh->setFrozen(loading); + + CCtrlBaseButton *btnUndo = dynamic_cast(group->getCtrl("browse_undo")); + if (btnUndo) + btnUndo->setFrozen(!undo); + + CCtrlBaseButton *btnRedo = dynamic_cast(group->getCtrl("browse_redo")); + if (btnRedo) + btnRedo->setFrozen(!redo); + } +} + +// *************************************************************************** +void CGroupWebkit::browserZoomIn() +{ + _Browser->GetHost()->SetZoomLevel(_Browser->GetHost()->GetZoomLevel() + 0.5); +} + +// *************************************************************************** +void CGroupWebkit::browserZoomOut() +{ + _Browser->GetHost()->SetZoomLevel(_Browser->GetHost()->GetZoomLevel() - 0.5); +} + +// *************************************************************************** +void CGroupWebkit::browserZoomReset() +{ + _Browser->GetHost()->SetZoomLevel(0.0f); +} + +// *************************************************************************** +int CGroupWebkit::luaBrowse(CLuaState &ls) +{ + const char *funcName = "browse"; + CLuaIHM::checkArgCount(ls, funcName, 1); + CLuaIHM::checkArgType(ls, funcName, 1, LUA_TSTRING); + browse(ls.toString(1)); + return 0; +} + +// *************************************************************************** +int CGroupWebkit::luaBrowseUndo(CLuaState &ls) +{ + const char *funcName = "browseUndo"; + CLuaIHM::checkArgCount(ls, funcName, 0); + browseUndo(); + return 0; +} + +// *************************************************************************** +int CGroupWebkit::luaBrowseRedo(CLuaState &ls) +{ + const char *funcName = "browseRedo"; + CLuaIHM::checkArgCount(ls, funcName, 0); + browseRedo(); + return 0; +} + +// *************************************************************************** +int CGroupWebkit::luaRefresh(CLuaState &ls) +{ + const char *funcName = "refresh"; + CLuaIHM::checkArgCount(ls, funcName, 0); + refresh(); + return 0; +} + +// *************************************************************************** +int CGroupWebkit::luaZoomIn(CLuaState &ls) +{ + const char *funcName = "zoomIn"; + CLuaIHM::checkArgCount(ls, funcName, 0); + browserZoomIn(); + return 0; +} + +// *************************************************************************** +int CGroupWebkit::luaZoomOut(CLuaState &ls) +{ + const char *funcName = "zoomOut"; + CLuaIHM::checkArgCount(ls, funcName, 0); + browserZoomOut(); + return 0; +} + +// *************************************************************************** +int CGroupWebkit::luaZoomReset(CLuaState &ls) +{ + const char *funcName = "zoomReset"; + CLuaIHM::checkArgCount(ls, funcName, 0); + browserZoomReset(); + return 0; +} + +// *************************************************************************** +int CGroupWebkit::luaParseHtml(CLuaState &ls) +{ + const char *funcName = "parseHtml"; + CLuaIHM::checkArgCount(ls, funcName, 1); + CLuaIHM::checkArgType(ls, funcName, 1, LUA_TSTRING); + std::string html = ls.toString(1); + + parseHtml(html); + + return 0; +} + +// *************************************************************************** +class CHandlerWebkitJSDialog : public IActionHandler +{ + void execute(CCtrlBase *pCaller, const std::string &sParams) + { + std::string btn = getParam(sParams, "button"); + bool success = (btn == "ok"); + std::string input; + + // get dialog window + CInterfaceGroup* group = pCaller->getRootWindow(); + if (group) + { + // if there is edit box, then it was a prompt dialog + CGroupEditBox* eb = dynamic_cast(group->getGroup("prompt:eb")); + if (eb) + input = eb->getInputStringAsUtf8(); + } + + CWebkitHandler::jsDialogContinue(success, input); + CWidgetManager::getInstance()->popModalWindow(); + } +}; +REGISTER_ACTION_HANDLER( CHandlerWebkitJSDialog, "webkit_jsdialog"); + +// *************************************************************************** +class CHandlerWebkitJSDialogCancel : public IActionHandler +{ + void execute(CCtrlBase *pCaller, const std::string &sParams) + { + // execute js dialog callback when modal is closed by ESC key for example + CWebkitHandler::jsDialogContinue(false, ""); + //CWidgetManager::getInstance()->popModalWindow(); + } +}; +REGISTER_ACTION_HANDLER( CHandlerWebkitJSDialogCancel, "webkit_jsdialog_cancel"); + diff --git a/code/nel/src/gui/reflect_register.cpp b/code/nel/src/gui/reflect_register.cpp index f7f47b1f9..022b18e2f 100644 --- a/code/nel/src/gui/reflect_register.cpp +++ b/code/nel/src/gui/reflect_register.cpp @@ -49,6 +49,10 @@ #include "nel/gui/group_html.h" #include "nel/gui/group_header.h" +#ifdef WITH_CEF3 +#include "nel/gui/group_webkit.h" +#endif + namespace NLGUI { void CReflectableRegister::registerClasses() @@ -88,6 +92,9 @@ namespace NLGUI REGISTER_REFLECTABLE_CLASS(CGroupTree::SNode, CReflectable); REGISTER_REFLECTABLE_CLASS(CGroupList, CInterfaceGroup); REGISTER_REFLECTABLE_CLASS(CGroupHeader, CGroupList); +#ifdef WITH_CEF3 + REGISTER_REFLECTABLE_CLASS(CGroupWebkit, CInterfaceGroup); +#endif } } diff --git a/code/nel/src/gui/webkit_app.cpp b/code/nel/src/gui/webkit_app.cpp new file mode 100644 index 000000000..fe22bc20c --- /dev/null +++ b/code/nel/src/gui/webkit_app.cpp @@ -0,0 +1,107 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "nel/gui/webkit_app.h" + +#include + +namespace NLGUI { + + // *************************************************************** + static bool isWebkitInitialized = false; + bool webkitInitialize(const CefMainArgs &args, const std::string &locale, const std::string &userAgent) + { + if (isWebkitInitialized) + { + nlerror("webkitInitialize called twice"); + return false; + } + isWebkitInitialized = true; + + // FIXME: could use lazy loading and disable webkit in CGroupWebkit when initialize fails + //printf(">> Cef initialize\n"); + + CefSettings settings; + + // Specify the path for the sub-process webhelper executable. + //CefString(&settings.browser_subprocess_path).FromASCII("/path/to/webhelper.exe"); + + // Use own cache directory that is not in ryzom search path + CefString(&settings.cache_path).FromASCII("cache_cef3"); + + CefString(&settings.locale).FromString(locale); + + // FIXME: cannot use 'Ryzom' as useragent, because that is used by webpages to detect ingame browser + //CefString(&settings.product_version).FromString(userAgent); + + CefRefPtr app(new CWebkitApp); + bool success = CefInitialize(args, settings, app.get(), NULL); + + return success; + } + + // Automatically called from CWebkitApp destructor + void webkitShutdown() + { + //printf(">> CEF shutdown\n"); + if (!isWebkitInitialized) + return; + + isWebkitInitialized = false; + CefShutdown(); + } + + // message from render thread + const char kFocusedNodeChangedMessage[] = "ClientRenderer.FocusedNodeChanged"; + + // shutdown Cef if it was initialized + CWebkitApp::~CWebkitApp() + { + if (isWebkitInitialized) + webkitShutdown(); + } + + // CefRenderProcessHandler + void CWebkitApp::OnFocusedNodeChanged(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr node) + { + //CEF_REQUIRE_RENDERER_THREAD(); + + bool is_editable = (node.get() && node->IsEditable()); + if (is_editable != _LastIsEditable) + { + //printf(">> WebkitApp::OnFocusedNodeChanged\n"); + _LastIsEditable = is_editable; + + CefRefPtr message = CefProcessMessage::Create(kFocusedNodeChangedMessage); + message->GetArgumentList()->SetBool(0, is_editable); + browser->SendProcessMessage(PID_BROWSER, message); + } + } + + // return CEF version + std::string CWebkitApp::getChromeVersion() + { + // Chrome/39.0.2171.95 + char buffer[256]; + sprintf(buffer, "Chrome/%d.%d.%d.%d", CHROME_VERSION_MAJOR, CHROME_VERSION_MINOR, CHROME_VERSION_BUILD, CHROME_VERSION_PATCH); + + return std::string(buffer); + } + +} // namespace + diff --git a/code/nel/src/gui/webkit_handler.cpp b/code/nel/src/gui/webkit_handler.cpp new file mode 100644 index 000000000..2857a8b18 --- /dev/null +++ b/code/nel/src/gui/webkit_handler.cpp @@ -0,0 +1,363 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "stdpch.h" + +#include "nel/gui/webkit_app.h" +#include "nel/gui/webkit_handler.h" +#include "nel/gui/group_webkit.h" +#include "nel/gui/view_text.h" +#include "nel/gui/group_editbox.h" + +// gui window id for javascript dialog +#define WIN_WEBKIT_JS_ALERT "ui:interface:webkit_js_alert" +#define WIN_WEBKIT_JS_CONFIRM "ui:interface:webkit_js_confirm" +#define WIN_WEBKIT_JS_PROMPT "ui:interface:webkit_js_prompt" + +namespace NLGUI { + + // global pointer for last javscript dialog callback + CefRefPtr CWebkitHandler::_JSDialogCallback = NULL; + + CWebkitHandler::CWebkitHandler(): + _Window(NULL) + { + } + + void CWebkitHandler::setWindow(CGroupWebkit *win) + { + _Window = win; + } + + // CefClient + bool CWebkitHandler::OnProcessMessageReceived(CefRefPtr browser, + CefProcessId source_process, + CefRefPtr message) + { + CEF_REQUIRE_UI_THREAD(); + + // message from render thread + //printf(">> OnProcessMessageReceived: process id:%d, name: %s\n", + // source_process, message->GetName().ToString().c_str()); + + std::string name = message->GetName(); + if (name == kFocusedNodeChangedMessage) + { + bool is_editable = message->GetArgumentList()->GetBool(0); + if (_Window) + _Window->setKeyboardCapture(is_editable); + return true; + } + + return false; + } + + // CefLifeSpanHandler + bool CWebkitHandler::OnBeforePopup(CefRefPtr browser, + CefRefPtr frame, + const CefString& target_url, + const CefString& target_frame_name, + const CefPopupFeatures& popupFeatures, + CefWindowInfo& windowInfo, + CefRefPtr& client, + CefBrowserSettings& settings, + bool* no_javascript_access) + { + CEF_REQUIRE_IO_THREAD(); + + // FIXME: inject new browser using template xml, set url and set active, centered + std::string url(target_url); + + // load popup into main window + if (target_url.size() > 0) + browser->GetMainFrame()->LoadURL(target_url); + + // block popup + return true; + } + + // CefDisplayHandler + void CWebkitHandler::OnTitleChange(CefRefPtr browser, const CefString& title) + { + CEF_REQUIRE_UI_THREAD(); + + if (_Window) + _Window->setTitle(title.ToString()); + } + + // CefDisplayHandler + bool CWebkitHandler::OnTooltip(CefRefPtr browser, CefString &text) + { + CEF_REQUIRE_UI_THREAD(); + + if (_Window) + _Window->setTooltip(text.ToString()); + + return true; + } + + // CefRenderHandler + bool CWebkitHandler::GetScreenInfo(CefRefPtr browser, CefScreenInfo& screen_info) + { + CEF_REQUIRE_UI_THREAD(); + + CefRect rect = CefRect(0, 0, 0, 0); + if (_Window) + { + rect.width = _Window->getTextureWidth(); + rect.height = _Window->getTextureHeight(); + } + + screen_info = CefScreenInfo( + 1.0f, // scale factor + 32, // depth + 8, // bits per component + false, // monochrome + rect, // rect - maximum size + rect // available rect - fullscreen maximum size + ); + + return true; + } + + // CefRenderHandler + bool CWebkitHandler::GetViewRect(CefRefPtr browser, CefRect &rect) + { + CEF_REQUIRE_UI_THREAD(); + + rect = CefRect(0, 0, 0, 0); + if (_Window) + { + rect.width = _Window->getTextureWidth(); + rect.height = _Window->getTextureHeight(); + } + + return true; + } + + // CefRenderHandler + void CWebkitHandler::OnPopupShow(CefRefPtr browser, bool show) + { + CEF_REQUIRE_UI_THREAD(); + + if (_Window) + _Window->setPopupVisibility(show); + } + + // CefRenderHandler + void CWebkitHandler::OnPopupSize(CefRefPtr browser, const CefRect& rect) + { + CEF_REQUIRE_UI_THREAD(); + + if (_Window) + _Window->setPopupSize(rect); + } + + // CefRenderHandler + void CWebkitHandler::OnPaint(CefRefPtr browser, PaintElementType type, const RectList &dirtyRects, const void *buffer, int width, int height) + { + CEF_REQUIRE_UI_THREAD(); + + if (_Window) + _Window->drawIntoTexture(dirtyRects, buffer, width, height, type == PET_POPUP); + } + + // CefRenderHandler + void CWebkitHandler::OnCursorChange(CefRefPtr browser, + CefCursorHandle cursor, + CursorType type, + const CefCursorInfo& custom_cursor_info) + { + //printf(">> OnCursorChange: %d\n", type); + // FIXME: type=CT_CUSTOM, custom_cursor_info has cursor image + std::string cursorTexture; + switch(type) + { + case CT_CROSS: + cursorTexture = "curs_default.tga"; + break; + case CT_HAND: + cursorTexture = "curs_pick.tga"; + break; + case CT_IBEAM: + cursorTexture = "curs_pick_dup.tga"; + break; + case CT_WAIT: + cursorTexture = "curs_rotate.tga"; + break; + case CT_HELP: + cursorTexture = "curs_help.tga"; + break; + default: + cursorTexture = "curs_default.tga"; + } + + // FIXME: works on login screen, but not ingame + CViewPointer *vp = dynamic_cast(CWidgetManager::getInstance()->getPointer()); + if(vp) + { + //printf(">> set cursor to [%s]\n", cursorTexture.c_str()); + vp->setStringMode(false); + vp->setCursor(cursorTexture); + } + else + printf(">> error: cursor pointer\n"); + } + + // CefLoadHandler + void CWebkitHandler::OnLoadingStateChange(CefRefPtr browser, + bool isLoading, + bool canGoBack, + bool canGoForward) + { + CEF_REQUIRE_UI_THREAD(); + + if (_Window) + _Window->setLoadingState(isLoading, canGoBack, canGoForward); + } + + // CefLoadHandler + void CWebkitHandler::OnLoadError(CefRefPtr browser, + CefRefPtr frame, + ErrorCode errorCode, + const CefString &errorText, + const CefString &failedUrl) + { + CEF_REQUIRE_UI_THREAD(); + + std::stringstream ss; + ss << "" + "

Failed to load URL " << std::string(failedUrl) << " with error " << std::string(errorText) << " (" << errorCode << ")

" + ""; + frame->LoadString(ss.str(), failedUrl); + } + + // CefDisplayHandler + void CWebkitHandler::OnStatusMessage(CefRefPtr browser, const CefString& value) + { + CEF_REQUIRE_UI_THREAD(); + + if (_Window) + _Window->setStatusMessage(value.ToString()); + } + + // CefDisplayHandler + bool CWebkitHandler::OnConsoleMessage(CefRefPtr browser, + const CefString& message, + const CefString& source, + int line) + { + // Log a console message... + printf("[%d] %s: %s\n", line, source.ToString().c_str(), message.ToString().c_str()); + + return false; + } + + // CefJSDialogHandler + bool CWebkitHandler::OnJSDialog(CefRefPtr browser, + const CefString& origin_url, + const CefString& accept_lang, + JSDialogType dialog_type, + const CefString& message_text, + const CefString& default_prompt_text, + CefRefPtr callback, + bool& suppress_message) + { + CEF_REQUIRE_UI_THREAD(); + + CInterfaceGroup* group; + _JSDialogCallback = NULL; + switch(dialog_type) + { + case JSDIALOGTYPE_ALERT: + group = CWidgetManager::getInstance()->getWindowFromId(WIN_WEBKIT_JS_ALERT); + if (group) + { + CViewText* text = dynamic_cast(group->getView("text")); + if (text) + text->setText(ucstring(message_text.ToString())); + + _JSDialogCallback = callback; + CWidgetManager::getInstance()->enableModalWindow(NULL, WIN_WEBKIT_JS_ALERT); + } + else + nldebug("window (%s) for javascript alert dialog not found", WIN_WEBKIT_JS_ALERT); + break; + case JSDIALOGTYPE_CONFIRM: + group = CWidgetManager::getInstance()->getWindowFromId(WIN_WEBKIT_JS_CONFIRM); + if (group) + { + CViewText* text = dynamic_cast(group->getView("text")); + if (text) + text->setText(ucstring(message_text.ToString())); + + _JSDialogCallback = callback; + CWidgetManager::getInstance()->enableModalWindow(NULL, WIN_WEBKIT_JS_CONFIRM); + } + else + nldebug("window (%s) for javascript confirm dialog not found", WIN_WEBKIT_JS_CONFIRM); + break; + case JSDIALOGTYPE_PROMPT: + group = CWidgetManager::getInstance()->getWindowFromId(WIN_WEBKIT_JS_PROMPT); + if (group) + { + CViewText* text = dynamic_cast(group->getView("text")); + if (text) + text->setText(ucstring(message_text.ToString())); + + CGroupEditBox* eb = dynamic_cast(group->getGroup("prompt:eb")); + if (eb) + eb->setInputStringAsUtf8(default_prompt_text.ToString()); + + _JSDialogCallback = callback; + CWidgetManager::getInstance()->enableModalWindow(NULL, WIN_WEBKIT_JS_PROMPT); + } + else + nldebug("window (%s) for javascript prompt dialog not found", WIN_WEBKIT_JS_PROMPT); + break; + default: + // ignore dialog + break; + } + + return _JSDialogCallback != NULL; + } + + // CefJSDialogHandler + void CWebkitHandler::OnResetDialogState(CefRefPtr browser) + { + CEF_REQUIRE_UI_THREAD(); + + if (_JSDialogCallback) + { + CWidgetManager::getInstance()->popModalWindow(); + _JSDialogCallback = NULL; + } + } + + // static + void CWebkitHandler::jsDialogContinue(bool success, const std::string &input) + { + if (_JSDialogCallback) + { + _JSDialogCallback->Continue(success, input); + + _JSDialogCallback = NULL; + } + } + +}// namespace + diff --git a/code/ryzom/client/data/gamedev/webkit.lua b/code/ryzom/client/data/gamedev/webkit.lua new file mode 100644 index 000000000..bfd99f591 --- /dev/null +++ b/code/ryzom/client/data/gamedev/webkit.lua @@ -0,0 +1,146 @@ +-- +if not Webkit then + Webkit = { + count = 0 + } +end + +function Webkit:newWindow(url, width, height) + local id = "webkit" .. tostring(self.count); + self.count = self.count + 1; + + local ui = createRootGroupInstance("webkit_browser", id, { + x = 0, + y = 0, + w = width, + h = height, + home = url + }) + ui.active = true; +end + +-- window becomes visible (:active=true) +function Webkit:onActive() + local uiWindow = getUICaller() + -- debugInfo("-- onActive [" .. uiWindow.id .. "]") + + if not uiWindow.opened then + uiWindow.opened = true + end + + if not uiWindow.active then + uiWindow.active = true + end +end + +-- +function Webkit:onJsDeactive() + local uiWindow = getUICaller() + -- debugInfo("-- onJsDeactive [" .. uiWindow.id .. "]") + runAH(getUICaller(), "webkit_jsdialog_cancel", ""); +end + +-- window is minimized +function Webkit:onClickHeaderClose() + -- ui:interface:webkit:header_closed + local uiWindow = getUICaller().parent + -- fixme: save current width/height and minimize window +end + +-- window is restored from minimized state +function Webkit:onClickHeaderOpen() + -- ui:interface:webig:header_opened + local uiWindow = getUICaller().parent + + -- fixme: restore saved width/height +end + +function Webkit:onClickHome() + -- caller is :header_opened:browse_home + local uiWindow = getUICaller().parent.parent + + -- window "home" attribute is used + local html = uiWindow:find("html") + html:browse("home") +end + +function Webkit:onClickRedo() + -- caller is :header_opened:browse_redo + local uiWindow = getUICaller().parent.parent + + local html = uiWindow:find("html") + html:browseRedo(); +end + +function Webkit:onClickUndo() + -- caller is :header_opened:browse_undo + local uiWindow = getUICaller().parent.parent + + local html = uiWindow:find("html") + html:browseUndo(); +end + +function Webkit:onClickRefresh() + -- caller is :header_opened:browse_refresh + local uiWindow = getUICaller().parent.parent + + local html = uiWindow:find("html") + html:refresh() +end + +function Webkit:onGrabKeyboard() + -- caller is :header_opened:browse_refresh + local uiWindow = getUICaller().parent.parent + + local html = uiWindow:find("html") + html.grab_keyboard = not html.grab_keyboard +end + +function Webkit:onMenuHome() + -- :header_closed + -- :header_opened + local uiWindow = getUICaller().parent + + local html = uiWindow:find("html") + html:browse("home") +end + +function Webkit:onMenuZoomIn() + -- :header_closed + -- :header_opened + local uiWindow = getUICaller().parent + + local html = uiWindow:find("html") + html:zoomIn() +end + +function Webkit:onMenuZoomOut() + -- :header_closed + -- :header_opened + local uiWindow = getUICaller().parent + + local html = uiWindow:find("html") + html:zoomOut() +end +function Webkit:onMenuZoomReset() + -- :header_closed + -- :header_opened + local uiWindow = getUICaller().parent + + local html = uiWindow:find("html") + html:zoomReset() +end + +function Webkit:onMenuGrabKeyboard() + local uiWindow = getUICaller().parent + + local html = uiWindow:find("html") + html.grab_keyboard = not html.grab_keyboard; +end + +function Webkit:onMenuQuit() + local uiWindow = getUICaller().parent + deleteUI(uiWindow) +end + + diff --git a/code/ryzom/client/data/gamedev/webkit.xml b/code/ryzom/client/data/gamedev/webkit.xml new file mode 100644 index 000000000..73aaa0f61 --- /dev/null +++ b/code/ryzom/client/data/gamedev/webkit.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/ryzom/client/src/CMakeLists.txt b/code/ryzom/client/src/CMakeLists.txt index 610e434ef..837c8d5fa 100644 --- a/code/ryzom/client/src/CMakeLists.txt +++ b/code/ryzom/client/src/CMakeLists.txt @@ -112,6 +112,11 @@ IF(APPLE) TARGET_LINK_LIBRARIES(ryzom_client ${FOUNDATION_LIBRARY}) ENDIF(APPLE) +IF(WITH_CEF3) + INCLUDE_DIRECTORIES(${CEF3_INCLUDE_DIR}) + #TARGET_LINK_LIBRARIES(ryzom_client ${CEF3_LIBRARIES}) +ENDIF(WITH_CEF3) + ADD_DEFINITIONS(${LIBXML2_DEFINITIONS} ${CURL_DEFINITIONS} ${LUABIND_DEFINITIONS}) NL_DEFAULT_PROPS(ryzom_client "Ryzom, Client: Ryzom Core Client") diff --git a/code/ryzom/client/src/client.cpp b/code/ryzom/client/src/client.cpp index d3bb5254e..9467d63e4 100644 --- a/code/ryzom/client/src/client.cpp +++ b/code/ryzom/client/src/client.cpp @@ -64,6 +64,12 @@ #include "client_cfg.h" #include "far_tp.h" +#ifdef WITH_CEF3 +#include +#include "nel/gui/webkit_app.h" +#include "user_agent.h" +#endif + /////////// // USING // /////////// @@ -360,6 +366,28 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE /* hPrevInstance */, LPSTR cm int main(int argc, char **argv) #endif { + +#ifdef WITH_CEF3 +#ifdef NL_OS_WINDOWS + CefMainArgs main_args(hInstance); +#else + CefMainArgs main_args(argc, argv); +#endif + // >>>>> webhelper.cpp >>>>> + { + // create app instance used in sub-process threads + CefRefPtr app(new CWebkitApp); + + sint result = CefExecuteProcess(main_args, app.get(), NULL); + if (result >= 0) + { + // child process ended + return result; + } + } + // <<<< webhelper.cpp <<<<< +#endif + // init the Nel context CApplicationContext *appContext = new CApplicationContext; @@ -556,6 +584,19 @@ int main(int argc, char **argv) prelogInit(); RYZOM_CATCH("Pre-Login Init") +#ifdef WITH_CEF3 + { + std::string locale = ClientCfg.getHtmlLanguageCode(); + // "Ryzom/ryzomcore/v0.12.0-dev-unix-x64 Chrome/39.0.2171.95" + std::string userAgent = getUserAgent() + " " + CWebkitApp::getChromeVersion(); + if (!webkitInitialize(main_args, locale, userAgent)) + { + nlerror("webkit initialize failed\n"); + return -1; + } + } +#endif + // Log the client and choose from shard RYZOM_TRY("Login") if (!ClientCfg.Local && (ClientCfg.TestBrowser || ClientCfg.FSHost.empty()))