From f548825faf744c82a49b30acba31d82bf5ba0519 Mon Sep 17 00:00:00 2001 From: Eduardo Bart Date: Sat, 3 Dec 2011 19:41:37 -0200 Subject: [PATCH] too many changes to list, many regressions were made, master will be UNSTABLE for a few days --- CMakeLists.txt | 20 +- TODO | 16 +- modules/console/console.lua | 2 +- modules/core/ext/color.lua | 1 + modules/core/ext/point.lua | 1 + modules/core/ext/rect.lua | 1 + modules/core/ext/size.lua | 1 + modules/core/ext/string.lua | 2 +- modules/entergame/characterlist.lua | 6 +- modules/entergame/entergame.lua | 28 +- modules/game/game.lua | 2 +- modules/options/options.lua | 10 +- modules/otclient/otclient.lua | 16 + modules/otclient/otclient.otmod | 9 + modules/tooltip/tooltip.lua | 4 +- modules/tooltip/tooltip.otmod | 1 + src/framework/application.cpp | 207 ++++ src/framework/application.h | 64 ++ src/framework/const.h | 10 + src/framework/core/clock.cpp | 6 +- src/framework/core/clock.h | 10 +- src/framework/core/eventdispatcher.h | 4 +- .../platformlistener.h => core/inputevent.h} | 24 +- src/framework/core/logger.cpp | 1 - src/framework/core/logger.h | 2 +- src/framework/core/module.cpp | 9 + src/framework/core/module.h | 10 +- src/framework/core/modulemanager.cpp | 30 +- src/framework/core/modulemanager.h | 2 +- src/framework/core/resourcemanager.cpp | 41 +- src/framework/core/resourcemanager.h | 6 +- src/framework/global.h | 2 +- src/framework/graphics/animatedtexture.cpp | 3 - src/framework/graphics/animatedtexture.h | 2 +- src/framework/graphics/declarations.h | 1 + src/framework/graphics/framebuffer.cpp | 34 +- src/framework/graphics/framebuffer.h | 5 - src/framework/graphics/glutil.h | 37 + src/framework/graphics/graphics.cpp | 3 - src/framework/graphics/texture.cpp | 5 +- .../{luascript => }/luafunctions.cpp | 29 +- src/framework/luascript/luainterface.cpp | 11 +- src/framework/luascript/luainterface.h | 43 +- src/framework/platform/crashhandler.cpp | 118 +++ src/framework/platform/platform.h | 86 -- src/framework/platform/platformevent.h | 63 -- .../platformwindow.cpp} | 11 +- src/framework/platform/platformwindow.h | 95 ++ .../{win32platform.cpp => win32window.cpp} | 5 + .../{core/engine.h => platform/win32window.h} | 10 +- src/framework/platform/x11platform.cpp | 989 ------------------ src/framework/platform/x11window.cpp | 887 ++++++++++++++++ src/framework/platform/x11window.h | 106 ++ src/framework/ui/uiframecounter.cpp | 9 +- src/framework/ui/uiframecounter.h | 2 +- src/framework/ui/uilineedit.cpp | 17 +- src/framework/ui/uilineedit.h | 4 +- src/framework/ui/uimanager.cpp | 70 +- src/framework/ui/uimanager.h | 4 +- src/framework/ui/uiwidget.cpp | 16 +- src/framework/ui/uiwidget.h | 4 +- src/framework/ui/uiwindow.cpp | 4 +- src/framework/ui/uiwindow.h | 2 +- src/framework/util/types.h | 3 + src/main.cpp | 35 +- src/otclient/const.h | 2 +- src/otclient/core/creature.cpp | 2 +- src/otclient/core/creature.h | 2 +- src/otclient/core/effect.h | 2 +- src/otclient/core/game.cpp | 22 +- src/otclient/core/game.h | 3 - src/otclient/core/item.h | 2 +- src/otclient/core/missile.h | 2 +- src/otclient/{luascript => }/luafunctions.cpp | 32 +- src/otclient/luascript/luavaluecasts.h | 3 +- src/otclient/net/protocolgameparse.cpp | 2 +- src/otclient/otclient.cpp | 302 +----- src/otclient/otclient.h | 75 +- src/otclient/ui/uigame.cpp | 8 +- src/otclient/ui/uigame.h | 2 +- 80 files changed, 1880 insertions(+), 1842 deletions(-) create mode 100644 modules/core/ext/color.lua create mode 100644 modules/core/ext/point.lua create mode 100644 modules/core/ext/rect.lua create mode 100644 modules/core/ext/size.lua create mode 100644 modules/otclient/otclient.lua create mode 100644 src/framework/application.cpp create mode 100644 src/framework/application.h rename src/framework/{platform/platformlistener.h => core/inputevent.h} (74%) create mode 100644 src/framework/graphics/glutil.h rename src/framework/{luascript => }/luafunctions.cpp (87%) create mode 100644 src/framework/platform/crashhandler.cpp delete mode 100644 src/framework/platform/platform.h delete mode 100644 src/framework/platform/platformevent.h rename src/framework/{core/engine.cpp => platform/platformwindow.cpp} (86%) create mode 100644 src/framework/platform/platformwindow.h rename src/framework/platform/{win32platform.cpp => win32window.cpp} (99%) rename src/framework/{core/engine.h => platform/win32window.h} (90%) delete mode 100644 src/framework/platform/x11platform.cpp create mode 100644 src/framework/platform/x11window.cpp create mode 100644 src/framework/platform/x11window.h rename src/otclient/{luascript => }/luafunctions.cpp (70%) diff --git a/CMakeLists.txt b/CMakeLists.txt index d990ea49..f8ad02e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ PROJECT(otclient) SET(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}") OPTION(USE_PCH "Use precompiled header" ON) -OPTION(NO_CONSOLE "Disable console window on Windows" OFF) +OPTION(NO_CONSOLE "Disable console window on Windows platform" OFF) OPTION(HANDLE_EXCEPTIONS "Generate crash reports" OFF) OPTION(FORBIDDEN_FUNCTIONS "Enable forbidden lua functions" ON) OPTION(USE_OPENGLES2 "Use OpenGL ES 2.0 (for mobiles devices)" OFF) @@ -39,7 +39,7 @@ MESSAGE(STATUS "BUILD TYPE: " ${CMAKE_BUILD_TYPE}) # setup compiler options IF(CMAKE_COMPILER_IS_GNUCXX) - SET(CXX_WARNS "-Wall -Wextra -Werror -Wno-unused-parameter -Wno-unused-but-set-variable -Wno-unused-variable -Wno-switch") + SET(CXX_WARNS "-Wall -Wextra -Werror -Wno-unused-parameter -Wno-unused-but-set-variable -Wno-unused-variable -Wno-switch -Wno-missing-field-initializers") SET(CMAKE_CXX_FLAGS "-std=gnu++0x -pipe ${CXX_WARNS}") SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3 -ggdb3 -fno-inline") SET(CMAKE_CXX_FLAGS_RELEASE "-O2") @@ -68,9 +68,9 @@ SET(SOURCES # otclient src/otclient/otclient.cpp + src/otclient/luafunctions.cpp # otclient luascript - src/otclient/luascript/luafunctions.cpp src/otclient/luascript/luavaluecasts.cpp # otclient core @@ -99,6 +99,10 @@ SET(SOURCES src/otclient/net/protocolgamesend.cpp src/otclient/net/protocolgameparse.cpp + # framework + src/framework/application.cpp + src/framework/luafunctions.cpp + # framework third party src/framework/thirdparty/apngloader.cpp @@ -118,9 +122,11 @@ SET(SOURCES src/framework/core/eventdispatcher.cpp src/framework/core/modulemanager.cpp src/framework/core/module.cpp - src/framework/core/engine.cpp src/framework/core/clock.cpp + # framework platform + src/framework/platform/platformwindow.cpp + # framework graphics src/framework/graphics/font.cpp src/framework/graphics/fontmanager.cpp @@ -144,7 +150,6 @@ SET(SOURCES src/framework/luascript/luainterface.cpp src/framework/luascript/luaobject.cpp src/framework/luascript/luaexception.cpp - src/framework/luascript/luafunctions.cpp src/framework/luascript/luavaluecasts.cpp # framework ui @@ -172,7 +177,7 @@ IF(FORBIDDEN_FUNCTIONS) ENDIF(FORBIDDEN_FUNCTIONS) IF(WIN32) - SET(SOURCES ${SOURCES} src/framework/platform/win32platform.cpp) + SET(SOURCES ${SOURCES} src/framework/platform/win32window.cpp) SET(ADDITIONAL_LIBRARIES ws2_32 mswsock) IF(CMAKE_COMPILER_IS_GNUCXX) @@ -180,7 +185,6 @@ IF(WIN32) ENDIF(CMAKE_COMPILER_IS_GNUCXX) IF(NO_CONSOLE) - ADD_DEFINITIONS(-DWIN32_NO_CONSOLE) IF(CMAKE_COMPILER_IS_GNUCXX) SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -mwindows") ENDIF(CMAKE_COMPILER_IS_GNUCXX) @@ -195,7 +199,7 @@ IF(WIN32) ELSE(WIN32) SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -rdynamic") SET(ADDITIONAL_LIBRARIES pthread) - SET(SOURCES ${SOURCES} src/framework/platform/x11platform.cpp) + SET(SOURCES ${SOURCES} src/framework/platform/x11window.cpp) ENDIF(WIN32) # target executable diff --git a/TODO b/TODO index 87fdf276..3692be58 100644 --- a/TODO +++ b/TODO @@ -13,4 +13,18 @@ grid layout fix moving windows and tooltips conflicts todo display otclient icon in window bar remake otui styles states system -padding \ No newline at end of file +padding + + +rename Game to g_game, etc +implement Console key binding +fatal error if sprite load fails +impl vertical sync, clipboard +crash handler +modify COnnection::poll() +setOnClose +review and reenable some warnings +make lua/c++ logger more friendly +bind every global lua function in a static class +use metatable for Point,Rect,Color,Size lua classes +lua binder generator diff --git a/modules/console/console.lua b/modules/console/console.lua index 01250d76..57dd9802 100644 --- a/modules/console/console.lua +++ b/modules/console/console.lua @@ -81,7 +81,7 @@ local function completeCommand() end end -local function onKeyPress(widget, keyCode, keyChar, keyboardModifiers) +local function onKeyPress(widget, keyCode, keyText, keyboardModifiers) if keyboardModifiers == KeyboardNoModifier then -- execute current command if keyCode == KeyReturn or keyCode == keyEnter then diff --git a/modules/core/ext/color.lua b/modules/core/ext/color.lua new file mode 100644 index 00000000..de25e286 --- /dev/null +++ b/modules/core/ext/color.lua @@ -0,0 +1 @@ +Color = {} \ No newline at end of file diff --git a/modules/core/ext/point.lua b/modules/core/ext/point.lua new file mode 100644 index 00000000..2cc54826 --- /dev/null +++ b/modules/core/ext/point.lua @@ -0,0 +1 @@ +Point = {} \ No newline at end of file diff --git a/modules/core/ext/rect.lua b/modules/core/ext/rect.lua new file mode 100644 index 00000000..9cd6b13b --- /dev/null +++ b/modules/core/ext/rect.lua @@ -0,0 +1 @@ +Rect = {} \ No newline at end of file diff --git a/modules/core/ext/size.lua b/modules/core/ext/size.lua new file mode 100644 index 00000000..25598c0a --- /dev/null +++ b/modules/core/ext/size.lua @@ -0,0 +1 @@ +Size = {} \ No newline at end of file diff --git a/modules/core/ext/string.lua b/modules/core/ext/string.lua index 18c56379..a619a7a9 100644 --- a/modules/core/ext/string.lua +++ b/modules/core/ext/string.lua @@ -23,4 +23,4 @@ function toboolean(str) return true end return false -end \ No newline at end of file +end diff --git a/modules/entergame/characterlist.lua b/modules/entergame/characterlist.lua index 17f00414..9a9cae5b 100644 --- a/modules/entergame/characterlist.lua +++ b/modules/entergame/characterlist.lua @@ -6,7 +6,7 @@ local loadBox local characterList -- private functions -local function onCharactersWindowKeyPress(self, keyCode, keyChar, keyboardModifiers) +local function onCharactersWindowKeyPress(self, keyCode, keyText, keyboardModifiers) if keyboardModifiers == KeyboardNoModifier then if keyCode == KeyUp or keyCode == KeyTab then characterList:focusPreviousChild(ActiveFocusReason) @@ -47,7 +47,7 @@ local function tryLogin(charInfo, tries) end -- save last used character - ConfigManager.set('lastUsedCharacter', charInfo.characterName) + g_configs.set('lastUsedCharacter', charInfo.characterName) end -- public functions @@ -75,7 +75,7 @@ function CharacterList.create(characters, premDays) label.worldHost = worldHost label.worldPort = worldIp - if i == 1 or ConfigManager.get('lastUsedCharacter') == characterName then + if i == 1 or g_configs.get('lastUsedCharacter') == characterName then characterList:focusChild(label, ActiveFocusReason) end end diff --git a/modules/entergame/entergame.lua b/modules/entergame/entergame.lua index 3d0fcbad..93caf339 100644 --- a/modules/entergame/entergame.lua +++ b/modules/entergame/entergame.lua @@ -11,8 +11,8 @@ local function clearAccountFields() enterGame:getChildById('accountNameLineEdit'):clearText() enterGame:getChildById('accountPasswordLineEdit'):clearText() enterGame:getChildById('accountNameLineEdit'):focus() - ConfigManager.set('account', nil) - ConfigManager.set('password', nil) + g_configs.set('account', nil) + g_configs.set('password', nil) end local function onError(protocol, error) @@ -30,9 +30,9 @@ end local function onCharacterList(protocol, characters, premDays) if enterGame:getChildById('rememberPasswordBox'):isChecked() then - ConfigManager.set('account', EnterGame.account) - ConfigManager.set('password', EnterGame.password) - ConfigManager.set('autologin', tostring(enterGame:getChildById('autoLoginBox'):isChecked())) + g_configs.set('account', EnterGame.account) + g_configs.set('password', EnterGame.password) + g_configs.set('autologin', tostring(enterGame:getChildById('autoLoginBox'):isChecked())) else clearAccountFields() end @@ -40,9 +40,9 @@ local function onCharacterList(protocol, characters, premDays) loadBox:destroy() CharacterList.create(characters, premDays) - local lastMotdNumber = tonumber(ConfigManager.get("motd")) + local lastMotdNumber = tonumber(g_configs.get("motd")) if motdNumber and motdNumber ~= lastMotdNumber then - ConfigManager.set("motd", motdNumber) + g_configs.set("motd", motdNumber) local motdBox = displayInfoBox("Message of the day", motdMessage) motdBox.onOk = CharacterList.show CharacterList.hide() @@ -53,11 +53,11 @@ end function EnterGame.create() enterGame = UI.display('entergame.otui') - local account = ConfigManager.get('account') - local password = ConfigManager.get('password') - local host = ConfigManager.get('host') - local port = tonumber(ConfigManager.get('port')) - local autologin = toboolean(ConfigManager.get('autologin')) + local account = g_configs.get('account') + local password = g_configs.get('password') + local host = g_configs.get('host') + local port = tonumber(g_configs.get('port')) + local autologin = toboolean(g_configs.get('autologin')) enterGame:getChildById('accountNameLineEdit'):setText(account) enterGame:getChildById('accountPasswordLineEdit'):setText(password) @@ -93,8 +93,8 @@ function EnterGame.doLogin() EnterGame.port = enterGame:getChildById('serverPortLineEdit'):getText() EnterGame.hide() - ConfigManager.set('host', EnterGame.host) - ConfigManager.set('port', EnterGame.port) + g_configs.set('host', EnterGame.host) + g_configs.set('port', EnterGame.port) local protocolLogin = ProtocolLogin.create() protocolLogin.onError = onError diff --git a/modules/game/game.lua b/modules/game/game.lua index e4234c38..b389bb1b 100644 --- a/modules/game/game.lua +++ b/modules/game/game.lua @@ -1,5 +1,5 @@ -- private functions -local function onGameKeyPress(self, keyCode, keyChar, keyboardModifiers) +local function onGameKeyPress(self, keyCode, keyText, keyboardModifiers) if keyboardModifiers == KeyboardCtrlModifier then if keyCode == KeyG then CharacterList.show() diff --git a/modules/options/options.lua b/modules/options/options.lua index ec2d1b48..30fa3b79 100644 --- a/modules/options/options.lua +++ b/modules/options/options.lua @@ -6,8 +6,8 @@ local fpsEnabled = false local vsyncEnabled = false function getConfig(name, default) - if ConfigManager.exists(name) then - local val = string.trim(ConfigManager.get(name)) + if g_configs.exists(name) then + local val = string.trim(g_configs.get(name)) if val == 'true' or val == 'false' then return toboolean(val) else @@ -15,7 +15,7 @@ function getConfig(name, default) end else if default ~= nil then - ConfigManager.set(name, default) + g_configs.set(name, default) return default else return nil @@ -24,13 +24,13 @@ function getConfig(name, default) end function setConfig(name, value) - ConfigManager.set(name, tostring(value)) + g_configs.set(name, tostring(value)) end -- private functions function Options.enableVsync(on) vsyncEnabled = on - setVerticalSync(on) + g_window.setVerticalSync(on) setConfig('vsync', on) end diff --git a/modules/otclient/otclient.lua b/modules/otclient/otclient.lua new file mode 100644 index 00000000..55c581bd --- /dev/null +++ b/modules/otclient/otclient.lua @@ -0,0 +1,16 @@ +OTClient = { } + +-- TODO: load and save configurations +function OTClient.init() + g_window.move({ x=220, y=220 }) + g_window.resize({ width=800, height=600 }) + g_window.setTitle('OTClient') + g_window.setIcon('otcicon.png') + + addEvent(g_window.show) + return true +end + +function OTClient.terminate() + g_window.hide() +end diff --git a/modules/otclient/otclient.otmod b/modules/otclient/otclient.otmod index 22a2841a..c041c1c5 100644 --- a/modules/otclient/otclient.otmod +++ b/modules/otclient/otclient.otmod @@ -3,6 +3,8 @@ Module description: Load all other otclient modules author: OTClient team website: https://github.com/edubart/otclient + autoLoad: true + autoLoadPriority: 10 dependencies: - core - background @@ -16,3 +18,10 @@ Module - chat - outfit - tibiafiles + + onLoad: | + require 'otclient' + return OTClient.init() + + onUnload: | + OTClient.terminate() diff --git a/modules/tooltip/tooltip.lua b/modules/tooltip/tooltip.lua index 3c68fb4f..0dcc88ba 100644 --- a/modules/tooltip/tooltip.lua +++ b/modules/tooltip/tooltip.lua @@ -5,9 +5,9 @@ local currentToolTip -- private functions local function moveToolTip(tooltip) - local pos = getMouseCursorPos() + local pos = g_window.getMousePos() pos.y = pos.y + 1 - local xdif = getScreenSize().width - (pos.x + tooltip:getWidth()) + local xdif = g_window.getSize().width - (pos.x + tooltip:getWidth()) if xdif < 2 then pos.x = pos.x - tooltip:getWidth() - 3 else diff --git a/modules/tooltip/tooltip.otmod b/modules/tooltip/tooltip.otmod index cfa5912f..6c3b3efa 100644 --- a/modules/tooltip/tooltip.otmod +++ b/modules/tooltip/tooltip.otmod @@ -4,6 +4,7 @@ Module author: OTClient team website: https://github.com/edubart/otclient autoLoad: true + autoLoadPriority: 2 dependencies: - core diff --git a/src/framework/application.cpp b/src/framework/application.cpp new file mode 100644 index 00000000..cd618829 --- /dev/null +++ b/src/framework/application.cpp @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2010-2011 OTClient + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "application.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void exitSignalHandler(int sig) +{ + static bool signaled = false; + switch(sig) { + case SIGTERM: + case SIGINT: + if(!signaled) { + signaled = true; + g_dispatcher.addEvent(std::bind(&Application::close, &g_app)); + } + break; + } +} + +void Application::init(const std::string& appName, const std::vector& args) +{ + logInfo("Starting application..."); + + m_pollCycleDelay = POLL_CYCLE_DELAY; + m_appName = appName; + + // capture exit signals + signal(SIGTERM, exitSignalHandler); + signal(SIGINT, exitSignalHandler); + + // initialize lua + g_lua.init(); + registerLuaFunctions(); + + // initialize resources + g_resources.init(args[0].c_str()); + + // loads user configuration + if(!g_configs.load("config.otml")) + logInfo("Using default configurations."); + + // initialize the ui + g_ui.init(); + + // setup platform window + g_window.init(); + g_window.setOnResize(std::bind(&Application::resize, this, _1)); + g_window.setOnInputEvent(std::bind(&Application::inputEvent, this, _1)); + g_window.setOnClose(std::bind(&Application::close, this)); + + // initialize graphics + g_graphics.init(); + + // fire first resize + resize(g_window.getSize()); + + // auto load lua modules + g_modules.discoverAndLoadModules(); +} + + +void Application::terminate() +{ + // hide the window because there is no render anymore + g_window.hide(); + + // run modules unload events + g_modules.unloadModules(); + + // release remaining lua object references + g_lua.collectGarbage(); + + // poll remaining events + poll(); + + // terminate ui + g_ui.terminate(); + + // terminate network + Connection::terminate(); + + // flush remaining dispatcher events + g_dispatcher.flush(); + + // terminate graphics + g_graphics.terminate(); + + // save configurations + g_configs.save(); + + // release resources + g_resources.terminate(); + + // terminate script environment + g_lua.terminate(); + + // release platform window + g_window.terminate(); + + logInfo("Application ended successfully."); +} + +void Application::run() +{ + ticks_t lastPollTicks = g_clock.updateTicks(); + m_stopping = false; + m_running = true; + + // run the first poll + poll(); + + if(g_ui.getRootWidget()->getChildCount() == 0) { + logError("There is no root widgets to display, the app will close"); + m_stopping = true; + } + + while(!m_stopping) { + g_clock.updateTicks(); + + // poll events every POLL_CYCLE_DELAY + // this delay exists to avoid massive polling thus increasing framerate + if(g_clock.ticksElapsed(lastPollTicks) >= m_pollCycleDelay) { + poll(); + lastPollTicks = g_clock.ticks(); + } + + if(g_window.isVisible()) { + g_graphics.beginRender(); + render(); + g_graphics.endRender(); + + // update screen pixels + g_window.swapBuffers(); + } else { + // sleeps until next poll to avoid massive cpu usage + g_clock.sleep(POLL_CYCLE_DELAY+1); + } + } + + m_stopping = false; + m_running = false; +} + +void Application::exit() +{ + logInfo("Exiting application.."); + m_stopping = true; +} + +void Application::poll() +{ + // poll input events + g_window.poll(); + + // poll network events + Connection::poll(); + + // poll application genareted events + g_dispatcher.poll(); +} + +void Application::render() +{ + // everything is rendered by UI components + g_ui.render(); +} + +void Application::resize(const Size& size) +{ + g_graphics.resize(size); + g_ui.resize(size); +} + +void Application::inputEvent(const InputEvent& event) +{ + g_ui.inputEvent(event); +} diff --git a/src/framework/application.h b/src/framework/application.h new file mode 100644 index 00000000..c46393ed --- /dev/null +++ b/src/framework/application.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2010-2011 OTClient + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef APPLICATION_H +#define APPLICATION_H + +#include + +class Application +{ + enum { + POLL_CYCLE_DELAY = 10 + }; + +public: + virtual void init(const std::string& appName, const std::vector& args); + virtual void registerLuaFunctions(); + virtual void terminate(); + virtual void run(); + virtual void exit(); + virtual void poll(); + virtual void close() { exit(); } + + void setPollCycleDelay(int delay) { m_pollCycleDelay = delay; } + + bool isRunning() { return m_running; } + bool isStopping() { return m_stopping; } + std::string getAppName() { return m_appName; } + +protected: + virtual void render(); + virtual void resize(const Size& size); + virtual void inputEvent(const InputEvent& event); + +private: + std::string m_appName; + int m_pollCycleDelay; + Boolean m_running; + Boolean m_stopping; +}; + +extern Application& g_app; + +#endif + diff --git a/src/framework/const.h b/src/framework/const.h index d8df86ba..fa148967 100644 --- a/src/framework/const.h +++ b/src/framework/const.h @@ -225,6 +225,16 @@ namespace Fw OtherFocusReason }; + enum InputEventType { + NoInputEvent = 0, + KeyPressInputEvent, + KeyReleaseInputEvent, + MousePressInputEvent, + MouseReleaseInputEvent, + MouseMoveInputEvent, + MouseWheelInputEvent + }; + enum MouseButton { MouseNoButton = 0, MouseLeftButton, diff --git a/src/framework/core/clock.cpp b/src/framework/core/clock.cpp index e8e93e0f..a22555e9 100644 --- a/src/framework/core/clock.cpp +++ b/src/framework/core/clock.cpp @@ -22,14 +22,18 @@ #include "clock.h" +// for usleep +#include + Clock g_clock; Clock::Clock() { m_startupTime = std::chrono::high_resolution_clock::now(); + m_currentTicks = 0; } -int Clock::updateTicks() +ticks_t Clock::updateTicks() { auto timeNow = std::chrono::high_resolution_clock::now(); m_currentTicks = std::chrono::duration_cast(timeNow - m_startupTime).count(); diff --git a/src/framework/core/clock.h b/src/framework/core/clock.h index 5926f5d2..6a749d46 100644 --- a/src/framework/core/clock.h +++ b/src/framework/core/clock.h @@ -30,15 +30,15 @@ class Clock public: Clock(); - int updateTicks(); + ticks_t updateTicks(); void sleep(int ms); - int ticks() { return m_currentTicks; } - int ticksElapsed(int prevTicks) { return ticks() - prevTicks; } - int ticksFor(int delay) { return ticks() + delay; } + ticks_t ticks() { return m_currentTicks; } + ticks_t ticksElapsed(long prevTicks) { return ticks() - prevTicks; } + ticks_t ticksFor(int delay) { return ticks() + delay; } private: - int m_currentTicks; + ticks_t m_currentTicks; std::chrono::system_clock::time_point m_startupTime; }; diff --git a/src/framework/core/eventdispatcher.h b/src/framework/core/eventdispatcher.h index 03de23ad..5fe0ecc1 100644 --- a/src/framework/core/eventdispatcher.h +++ b/src/framework/core/eventdispatcher.h @@ -26,9 +26,9 @@ #include "declarations.h" struct ScheduledEvent { - ScheduledEvent(int ticks, const SimpleCallback& callback) : ticks(ticks), callback(callback) { } + ScheduledEvent(ticks_t ticks, const SimpleCallback& callback) : ticks(ticks), callback(callback) { } bool operator<(const ScheduledEvent& other) const { return ticks > other.ticks; } - int ticks; + ticks_t ticks; SimpleCallback callback; }; diff --git a/src/framework/platform/platformlistener.h b/src/framework/core/inputevent.h similarity index 74% rename from src/framework/platform/platformlistener.h rename to src/framework/core/inputevent.h index df94472f..932672c1 100644 --- a/src/framework/platform/platformlistener.h +++ b/src/framework/core/inputevent.h @@ -20,20 +20,20 @@ * THE SOFTWARE. */ -#ifndef PLATFORMLISTENER_H -#define PLATFORMLISTENER_H +#ifndef INPUTEVENT_H +#define INPUTEVENT_H -#include "platformevent.h" +#include "declarations.h" -class PlatformListener -{ -public: - /// Fired when user tries to close the window - virtual void onClose() = 0; - /// Fired when user resize the window - virtual void onResize(const Size& size) = 0; - /// Fired when user press a key or move the mouse - virtual void onPlatformEvent(const PlatformEvent& event) = 0; +struct InputEvent { + Fw::InputEventType type; + Fw::MouseWheelDirection wheelDirection; + Fw::MouseButton mouseButton; + int keyboardModifiers; + std::string keyText; + Fw::Key keyCode; + Point mousePos; + Point mouseMoved; }; #endif diff --git a/src/framework/core/logger.cpp b/src/framework/core/logger.cpp index d802d357..e061a4f4 100644 --- a/src/framework/core/logger.cpp +++ b/src/framework/core/logger.cpp @@ -22,7 +22,6 @@ #include "logger.h" #include "eventdispatcher.h" -#include Logger g_logger; diff --git a/src/framework/core/logger.h b/src/framework/core/logger.h index 4a5501b7..5f687086 100644 --- a/src/framework/core/logger.h +++ b/src/framework/core/logger.h @@ -70,4 +70,4 @@ extern Logger g_logger; #define logTraceWarning(...) g_logger.logFunc(Fw::LogWarning, Fw::mkstr(__VA_ARGS__), __PRETTY_FUNCTION__) #define logTraceError(...) g_logger.logFunc(Fw::LogError, Fw::mkstr(__VA_ARGS__), __PRETTY_FUNCTION__) -#endif \ No newline at end of file +#endif diff --git a/src/framework/core/module.cpp b/src/framework/core/module.cpp index 3719891d..a6bb2377 100644 --- a/src/framework/core/module.cpp +++ b/src/framework/core/module.cpp @@ -26,6 +26,11 @@ #include #include +Module::Module(const std::string& name) +{ + m_name = name; +} + void Module::discover(const OTMLNodePtr& moduleNode) { const static std::string none = "none"; @@ -34,6 +39,7 @@ void Module::discover(const OTMLNodePtr& moduleNode) m_website = moduleNode->valueAt("website", none); m_version = moduleNode->valueAt("version", none); m_autoLoad = moduleNode->valueAt("autoLoad", false); + m_autoLoadPriority = moduleNode->valueAt("autoLoadPriority", 100); if(OTMLNodePtr node = moduleNode->get("dependencies")) { for(const OTMLNodePtr& tmp : node->children()) @@ -57,6 +63,9 @@ void Module::discover(const OTMLNodePtr& moduleNode) bool Module::load() { + if(m_loaded) + return true; + for(const std::string& depName : m_dependencies) { ModulePtr dep = g_modules.getModule(depName); if(!dep) { diff --git a/src/framework/core/module.h b/src/framework/core/module.h index 6d988ef9..70747a6e 100644 --- a/src/framework/core/module.h +++ b/src/framework/core/module.h @@ -30,7 +30,7 @@ class Module { public: - Module(const std::string& name) : m_loaded(false), m_autoLoad(false), m_name(name) { } + Module(const std::string& name); void discover(const OTMLNodePtr& moduleNode); bool load(); @@ -43,11 +43,13 @@ public: std::string getAuthor() { return m_author; } std::string getWebsite() { return m_website; } std::string getVersion() { return m_version; } - bool autoLoad() { return m_autoLoad; } + bool isAutoLoad() { return m_autoLoad; } + int getAutoLoadPriority() { return m_autoLoadPriority; } private: - bool m_loaded; - bool m_autoLoad; + Boolean m_loaded; + Boolean m_autoLoad; + int m_autoLoadPriority; std::string m_name; std::string m_description; std::string m_author; diff --git a/src/framework/core/modulemanager.cpp b/src/framework/core/modulemanager.cpp index a9154347..d74ebb19 100644 --- a/src/framework/core/modulemanager.cpp +++ b/src/framework/core/modulemanager.cpp @@ -29,29 +29,32 @@ ModuleManager g_modules; void ModuleManager::discoverAndLoadModules() { + std::multimap m_autoLoadModules; + auto moduleDirs = g_resources.listDirectoryFiles("/"); for(const std::string& moduleDir : moduleDirs) { auto moduleFiles = g_resources.listDirectoryFiles("/" + moduleDir); - for(const std::string& file : moduleFiles) { - if(boost::ends_with(file, ".otmod")) - discoverModule("/" + moduleDir + "/" + file); + for(const std::string& moduleFile : moduleFiles) { + if(boost::ends_with(moduleFile, ".otmod")) { + ModulePtr module = discoverModule("/" + moduleDir + "/" + moduleFile); + if(module && module->isAutoLoad()) + m_autoLoadModules.insert(make_pair(module->getAutoLoadPriority(), module)); + } } } - // auto load modules - for(const ModulePtr& module : m_modules) { - if(!module->isLoaded() && module->autoLoad()) { - if(!module->load()) - logFatal("A required module has failed to load, cannot continue to run."); - } + for(auto& pair : m_autoLoadModules) { + ModulePtr module = pair.second; + if(!module->isLoaded() && !module->load()) + logFatal("A required module has failed to load, cannot continue to run."); } } -bool ModuleManager::discoverModule(const std::string& file) +ModulePtr ModuleManager::discoverModule(const std::string& moduleFile) { ModulePtr module; try { - OTMLDocumentPtr doc = OTMLDocument::parse(file); + OTMLDocumentPtr doc = OTMLDocument::parse(moduleFile); OTMLNodePtr moduleNode = doc->at("Module"); std::string name = moduleNode->valueAt("name"); @@ -62,10 +65,9 @@ bool ModuleManager::discoverModule(const std::string& file) module->discover(moduleNode); m_modules.push_back(module); } catch(Exception& e) { - logError("Unable to discover module from file '", file, "': ", e.what()); - return false; + logError("Unable to discover module from file '", moduleFile, "': ", e.what()); } - return true; + return module; } void ModuleManager::unloadModules() diff --git a/src/framework/core/modulemanager.h b/src/framework/core/modulemanager.h index 5984944f..9ce06bcd 100644 --- a/src/framework/core/modulemanager.h +++ b/src/framework/core/modulemanager.h @@ -29,7 +29,7 @@ class ModuleManager { public: void discoverAndLoadModules(); - bool discoverModule(const std::string& file); + ModulePtr discoverModule(const std::string& moduleFile); void unloadModules(); ModulePtr getModule(const std::string& moduleName); diff --git a/src/framework/core/resourcemanager.cpp b/src/framework/core/resourcemanager.cpp index 148cfc23..0367b89d 100644 --- a/src/framework/core/resourcemanager.cpp +++ b/src/framework/core/resourcemanager.cpp @@ -22,24 +22,28 @@ #include "resourcemanager.h" -#include +#include #include #include ResourceManager g_resources; -void ResourceManager::init(const char* argv0, const char *appName) +void ResourceManager::init(const char *argv0) { PHYSFS_init(argv0); + // setup write directory + if(!g_resources.setupWriteDir()) + logError("Could not setup write directory"); + // try to find modules directory, all data lives there + //TODO: move this to Application class std::string baseDir = PHYSFS_getBaseDir(); - std::string possibleDirs[] = { "modules", baseDir + "modules", baseDir + "../modules", - baseDir + "../share/" + appName + "/otclient/modules", + baseDir + "../share/" + g_app.getAppName() + "/otclient/modules", "" }; bool found = false; @@ -52,14 +56,7 @@ void ResourceManager::init(const char* argv0, const char *appName) } if(!found) - logFatal("Could not find modules directory."); - - // setup write directory - std::string dir = g_platform.getAppUserDir(); - if(g_resources.setWriteDir(dir)) - g_resources.addToSearchPath(dir); - else - logError("could not setup write directory"); + logFatal("Could not find modules directory"); } void ResourceManager::terminate() @@ -67,10 +64,22 @@ void ResourceManager::terminate() PHYSFS_deinit(); } -bool ResourceManager::setWriteDir(const std::string& path) +bool ResourceManager::setupWriteDir() { - if(!PHYSFS_setWriteDir(path.c_str())) - return false; + std::string userDir = PHYSFS_getUserDir(); + std::string dirName = Fw::mkstr(".", g_app.getAppName()); + std::string writeDir = userDir + dirName; + if(!PHYSFS_setWriteDir(writeDir.c_str())) { + if(!PHYSFS_setWriteDir(userDir.c_str())) + return false; + if(!PHYSFS_mkdir(dirName.c_str())) { + PHYSFS_setWriteDir(NULL); + return false; + } + if(!PHYSFS_setWriteDir(writeDir.c_str())) + return false; + } + addToSearchPath(writeDir); return true; } @@ -97,7 +106,7 @@ bool ResourceManager::fileExists(const std::string& fileName) bool ResourceManager::directoryExists(const std::string& directoryName) { - return (PHYSFS_exists(resolvePath(directoryName).c_str()) && PHYSFS_isDirectory(resolvePath(directoryName).c_str())); + return (PHYSFS_isDirectory(resolvePath(directoryName).c_str())); } void ResourceManager::loadFile(const std::string& fileName, std::iostream& out) diff --git a/src/framework/core/resourcemanager.h b/src/framework/core/resourcemanager.h index 09d785b5..8adf21b9 100644 --- a/src/framework/core/resourcemanager.h +++ b/src/framework/core/resourcemanager.h @@ -28,11 +28,10 @@ class ResourceManager { public: - void init(const char* argv0, const char *appName); + void init(const char *argv0); void terminate(); - /// Set output files directory - bool setWriteDir(const std::string& path); + bool setupWriteDir(); /// Add an package or directory to the search path bool addToSearchPath(const std::string& path, bool insertInFront = true); @@ -55,6 +54,7 @@ public: std::list listDirectoryFiles(const std::string& directoryPath = ""); std::string resolvePath(const std::string& path); + std::string getAppUserPath(); }; extern ResourceManager g_resources; diff --git a/src/framework/global.h b/src/framework/global.h index 9821b6b1..da93f93d 100644 --- a/src/framework/global.h +++ b/src/framework/global.h @@ -30,7 +30,7 @@ #include #include #include -#include +#include // common STL headers #include diff --git a/src/framework/graphics/animatedtexture.cpp b/src/framework/graphics/animatedtexture.cpp index 8c309adf..d84077b6 100644 --- a/src/framework/graphics/animatedtexture.cpp +++ b/src/framework/graphics/animatedtexture.cpp @@ -23,11 +23,8 @@ #include "animatedtexture.h" #include "graphics.h" -#include #include -#include - AnimatedTexture::AnimatedTexture(int width, int height, int channels, int numFrames, uchar *framesPixels, int *framesDelay) : Texture(), m_numFrames(numFrames) diff --git a/src/framework/graphics/animatedtexture.h b/src/framework/graphics/animatedtexture.h index a5fd7ba1..47db5a19 100644 --- a/src/framework/graphics/animatedtexture.h +++ b/src/framework/graphics/animatedtexture.h @@ -41,7 +41,7 @@ private: std::vector m_framesDelay; int m_numFrames; int m_currentFrame; - int m_lastAnimCheckTicks; + ticks_t m_lastAnimCheckTicks; }; #endif diff --git a/src/framework/graphics/declarations.h b/src/framework/graphics/declarations.h index 9adbd195..54a14cac 100644 --- a/src/framework/graphics/declarations.h +++ b/src/framework/graphics/declarations.h @@ -24,6 +24,7 @@ #define FRAMEWORK_GRAPHICS_DECLARATIONS_H #include +#include "glutil.h" class Texture; class AnimatedTexture; diff --git a/src/framework/graphics/framebuffer.cpp b/src/framework/graphics/framebuffer.cpp index 16ad58b7..3cd16bdb 100644 --- a/src/framework/graphics/framebuffer.cpp +++ b/src/framework/graphics/framebuffer.cpp @@ -24,17 +24,6 @@ #include "graphics.h" #include "texture.h" -#include - -#include -#include - -PFNGLGENFRAMEBUFFERSPROC oglGenFramebuffers = 0; -PFNGLBINDFRAMEBUFFERPROC oglBindFramebuffer = 0; -PFNGLFRAMEBUFFERTEXTURE2DPROC oglFramebufferTexture2D = 0; -PFNGLDELETEFRAMEBUFFERSPROC oglDeleteFramebuffers = 0; -PFNGLCHECKFRAMEBUFFERSTATUSPROC oglCheckFramebufferStatus = 0; - FrameBuffer::FrameBuffer(int width, int height) { m_fbo = 0; @@ -46,22 +35,15 @@ FrameBuffer::FrameBuffer(int width, int height) // use FBO ext only if supported if(g_graphics.isExtensionSupported("GL_ARB_framebuffer_object")) { m_fallbackOldImp = false; - if(!oglGenFramebuffers) { - oglGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)g_platform.getExtensionProcAddress("glGenFramebuffers"); - oglBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)g_platform.getExtensionProcAddress("glBindFramebuffer"); - oglFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)g_platform.getExtensionProcAddress("glFramebufferTexture2D"); - oglDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)g_platform.getExtensionProcAddress("glDeleteFramebuffers"); - oglCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)g_platform.getExtensionProcAddress("glCheckFramebufferStatus"); - } // generate FBO - oglGenFramebuffers(1, &m_fbo); - oglBindFramebuffer(GL_FRAMEBUFFER_EXT, m_fbo); + glGenFramebuffers(1, &m_fbo); + glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_fbo); // attach 2D texture to this FBO - oglFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_texture->getId(), 0); + glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_texture->getId(), 0); - GLenum status = oglCheckFramebufferStatus(GL_FRAMEBUFFER_EXT); + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT); switch(status) { case GL_FRAMEBUFFER_COMPLETE_EXT: //ok @@ -72,7 +54,7 @@ FrameBuffer::FrameBuffer(int width, int height) } // restore back buffer - oglBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); glDrawBuffer(GL_BACK); glReadBuffer(GL_BACK); } else { @@ -87,14 +69,14 @@ FrameBuffer::FrameBuffer(int width, int height) FrameBuffer::~FrameBuffer() { if(m_fbo) - oglDeleteFramebuffers(1, &m_fbo); + glDeleteFramebuffers(1, &m_fbo); } void FrameBuffer::bind() { if(!m_fallbackOldImp) { // bind framebuffer - oglBindFramebuffer(GL_FRAMEBUFFER_EXT, m_fbo); + glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_fbo); } else { int screenWidth = g_graphics.getScreenSize().width(); int screenHeight = g_graphics.getScreenSize().height(); @@ -126,7 +108,7 @@ void FrameBuffer::unbind() { if(!m_fallbackOldImp) { // bind back buffer again - oglBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); glDrawBuffer(GL_BACK); glReadBuffer(GL_BACK); diff --git a/src/framework/graphics/framebuffer.h b/src/framework/graphics/framebuffer.h index 10ffeaaa..182ef1a9 100644 --- a/src/framework/graphics/framebuffer.h +++ b/src/framework/graphics/framebuffer.h @@ -31,13 +31,8 @@ public: FrameBuffer(int width, int height); virtual ~FrameBuffer(); - /// Binds the framebuffer, by switching render buffer to itself, everything rendered will be draw on it void bind(); - - /// Unbinds the framebuffer (switch render buffer to back buffer again) void unbind(); - - /// Draws framebuffer texture void draw(const Rect& screenCoords, const Rect& framebufferCoords = Rect()); TexturePtr getTexture() { return m_texture; } diff --git a/src/framework/graphics/glutil.h b/src/framework/graphics/glutil.h new file mode 100644 index 00000000..344d6ee7 --- /dev/null +++ b/src/framework/graphics/glutil.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2010-2011 OTClient + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef GLUTIL_H +#define GLUTIL_H + +#define GL_GLEXT_PROTOTYPES + +#ifndef OPENGL_ES2 +#include +#include +#else +#include +#include +#endif + +#endif diff --git a/src/framework/graphics/graphics.cpp b/src/framework/graphics/graphics.cpp index baaf7523..d768b568 100644 --- a/src/framework/graphics/graphics.cpp +++ b/src/framework/graphics/graphics.cpp @@ -25,9 +25,6 @@ #include #include -#include -#include - Graphics g_graphics; void Graphics::init() diff --git a/src/framework/graphics/texture.cpp b/src/framework/graphics/texture.cpp index ef2ab065..ef3a8de2 100644 --- a/src/framework/graphics/texture.cpp +++ b/src/framework/graphics/texture.cpp @@ -23,9 +23,6 @@ #include "texture.h" #include "graphics.h" -#include -#include - Texture::Texture() { m_textureId = 0; @@ -61,7 +58,7 @@ uint Texture::internalLoadGLTexture(uchar *pixels, int channels, int width, int logError("loading texture with size ", width, "x", height, " failed, " "the maximum size allowed by the graphics card is ", maxTexSize, "x", maxTexSize, ",", "to prevent crashes the texture will be displayed as a blank texture"); - //TODO: a workground, like bilinear scaling the texture + //TODO: make a workaround, could be bilinear scaling the texture return 0; } diff --git a/src/framework/luascript/luafunctions.cpp b/src/framework/luafunctions.cpp similarity index 87% rename from src/framework/luascript/luafunctions.cpp rename to src/framework/luafunctions.cpp index 44822c7f..8c57cf3b 100644 --- a/src/framework/luascript/luafunctions.cpp +++ b/src/framework/luafunctions.cpp @@ -20,17 +20,18 @@ * THE SOFTWARE. */ -#include "luainterface.h" +#include +#include #include #include #include #include #include -#include #include #include +#include -void LuaInterface::registerFunctions() +void Application::registerLuaFunctions() { // UIWidget g_lua.registerClass(); @@ -152,10 +153,21 @@ void LuaInterface::registerFunctions() g_lua.registerClass(); // ConfigManager - g_lua.registerClass(); - g_lua.bindClassStaticFunction("set", std::bind(&ConfigManager::set, &g_configs, _1, _2)); - g_lua.bindClassStaticFunction("get", std::bind(&ConfigManager::get, &g_configs, _1)); - g_lua.bindClassStaticFunction("exists", std::bind(&ConfigManager::exists, &g_configs, _1)); + g_lua.registerStaticClass("g_configs"); + g_lua.bindClassStaticFunction("g_configs", "set", std::bind(&ConfigManager::set, &g_configs, _1, _2)); + g_lua.bindClassStaticFunction("g_configs", "get", std::bind(&ConfigManager::get, &g_configs, _1)); + g_lua.bindClassStaticFunction("g_configs", "exists", std::bind(&ConfigManager::exists, &g_configs, _1)); + + g_lua.registerStaticClass("g_window"); + g_lua.bindClassStaticFunction("g_window", "show", std::bind(&PlatformWindow::show, &g_window)); + g_lua.bindClassStaticFunction("g_window", "hide", std::bind(&PlatformWindow::hide, &g_window)); + g_lua.bindClassStaticFunction("g_window", "move", std::bind(&PlatformWindow::move, &g_window, _1)); + g_lua.bindClassStaticFunction("g_window", "resize", std::bind(&PlatformWindow::resize, &g_window, _1)); + g_lua.bindClassStaticFunction("g_window", "setVerticalSync", std::bind(&PlatformWindow::setVerticalSync, &g_window, _1)); + g_lua.bindClassStaticFunction("g_window", "setTitle", std::bind(&PlatformWindow::setTitle, &g_window, _1)); + g_lua.bindClassStaticFunction("g_window", "setIcon", std::bind(&PlatformWindow::setIcon, &g_window, _1)); + g_lua.bindClassStaticFunction("g_window", "getMousePos", std::bind(&PlatformWindow::getMousePos, &g_window)); + g_lua.bindClassStaticFunction("g_window", "getSize", std::bind(&PlatformWindow::getSize, &g_window)); // Logger g_lua.registerClass(); @@ -171,7 +183,4 @@ void LuaInterface::registerFunctions() g_lua.bindGlobalFunction("getRootWidget", std::bind(&UIManager::getRootWidget, &g_ui)); g_lua.bindGlobalFunction("addEvent", std::bind(&EventDispatcher::addEvent, &g_dispatcher, _1, false)); g_lua.bindGlobalFunction("scheduleEvent", std::bind(&EventDispatcher::scheduleEvent, &g_dispatcher, _1, _2)); - g_lua.bindGlobalFunction("getMouseCursorPos", std::bind(&Platform::getMouseCursorPos, &g_platform)); - g_lua.bindGlobalFunction("setVerticalSync", std::bind(&Platform::setVerticalSync, &g_platform, _1)); - g_lua.bindGlobalFunction("getScreenSize", std::bind(&Graphics::getScreenSize, &g_graphics)); } diff --git a/src/framework/luascript/luainterface.cpp b/src/framework/luascript/luainterface.cpp index 80f47135..28650441 100644 --- a/src/framework/luascript/luainterface.cpp +++ b/src/framework/luascript/luainterface.cpp @@ -47,9 +47,6 @@ void LuaInterface::init() // register LuaObject, the base of all other objects registerClass(); bindClassMemberGetField("use_count", &LuaObject::getUseCount); - - // register the real script stuff - registerFunctions(); } void LuaInterface::terminate() @@ -58,6 +55,14 @@ void LuaInterface::terminate() closeLuaState(); } +void LuaInterface::registerStaticClass(const std::string& className) +{ + newTable(); + pushValue(); + setGlobal(className); + pop(); +} + void LuaInterface::registerClass(const std::string& className, const std::string& baseClass) { // creates the class table (that it's also the class methods table) diff --git a/src/framework/luascript/luainterface.h b/src/framework/luascript/luainterface.h index d081acfa..b566d8c9 100644 --- a/src/framework/luascript/luainterface.h +++ b/src/framework/luascript/luainterface.h @@ -42,6 +42,7 @@ public: void registerFunctions(); // functions that will register all script stuff in lua global environment + void registerStaticClass(const std::string& className); void registerClass(const std::string& className, const std::string& baseClass = "LuaObject"); void registerClassStaticFunction(const std::string& className, @@ -86,18 +87,28 @@ public: // methods for binding functions template void bindClassStaticFunction(const std::string& functionName, const F& function); + template + void bindClassStaticFunction(const std::string& className, const std::string& functionName, const F& function); template void bindClassMemberFunction(const std::string& functionName, F C::*function); + template + void bindClassMemberFunction(const std::string& className, const std::string& functionName, F C::*function); template void bindClassMemberField(const std::string& fieldName, F1 C::*getFunction, F2 C::*setFunction); + template + void bindClassMemberField(const std::string& className, const std::string& fieldName, F1 C::*getFunction, F2 C::*setFunction); template void bindClassMemberGetField(const std::string& fieldName, F C::*getFunction); + template + void bindClassMemberGetField(const std::string& className, const std::string& fieldName, F C::*getFunction); template void bindClassMemberSetField(const std::string& fieldName, F C::*setFunction); + template + void bindClassMemberSetField(const std::string& className, const std::string& fieldName, F C::*setFunction); template void bindGlobalFunction(const std::string& functionName, const F& function); @@ -314,31 +325,45 @@ template void LuaInterface::bindClassStaticFunction(const std::string& functionName, const F& function) { registerClassStaticFunction(functionName, luabinder::bind_fun(function)); } +template +void LuaInterface::bindClassStaticFunction(const std::string& className, const std::string& functionName, const F& function) { + registerClassStaticFunction(className, functionName, luabinder::bind_fun(function)); +} template void LuaInterface::bindClassMemberFunction(const std::string& functionName, F C::*function) { registerClassMemberFunction(functionName, luabinder::bind_mem_fun(function)); } +template +void LuaInterface::bindClassMemberFunction(const std::string& className, const std::string& functionName, F C::*function) { + registerClassMemberFunction(className, functionName, luabinder::bind_mem_fun(function)); +} template void LuaInterface::bindClassMemberField(const std::string& fieldName, F1 C::*getFunction, F2 C::*setFunction) { - registerClassMemberField(fieldName, - luabinder::bind_mem_fun(getFunction), - luabinder::bind_mem_fun(setFunction)); + registerClassMemberField(fieldName, luabinder::bind_mem_fun(getFunction), luabinder::bind_mem_fun(setFunction)); +} +template +void LuaInterface::bindClassMemberField(const std::string& className, const std::string& fieldName, F1 C::*getFunction, F2 C::*setFunction) { + registerClassMemberField(className, fieldName, luabinder::bind_mem_fun(getFunction), luabinder::bind_mem_fun(setFunction)); } template void LuaInterface::bindClassMemberGetField(const std::string& fieldName, F C::*getFunction) { - registerClassMemberField(fieldName, - luabinder::bind_mem_fun(getFunction), - LuaCppFunction()); + registerClassMemberField(fieldName, luabinder::bind_mem_fun(getFunction), LuaCppFunction()); +} +template +void LuaInterface::bindClassMemberGetField(const std::string& className, const std::string& fieldName, F C::*getFunction) { + registerClassMemberField(className, fieldName, luabinder::bind_mem_fun(getFunction), LuaCppFunction()); } template void LuaInterface::bindClassMemberSetField(const std::string& fieldName, F C::*setFunction) { - registerClassMemberField(fieldName, - LuaCppFunction(), - luabinder::bind_mem_fun(setFunction)); + registerClassMemberField(fieldName, LuaCppFunction(), luabinder::bind_mem_fun(setFunction)); +} +template +void LuaInterface::bindClassMemberSetField(const std::string& className, const std::string& fieldName, F C::*setFunction) { + registerClassMemberField(className, fieldName, LuaCppFunction(), luabinder::bind_mem_fun(setFunction)); } template diff --git a/src/framework/platform/crashhandler.cpp b/src/framework/platform/crashhandler.cpp new file mode 100644 index 00000000..b788a324 --- /dev/null +++ b/src/framework/platform/crashhandler.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2010-2011 OTClient + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifdef HANDLE_EXCEPTIONS + +#include +#include + +#define MAX_BACKTRACE_DEPTH 128 +#define DEMANGLE_BACKTRACE_SYMBOLS + +void crashHandler(int signum, siginfo_t* info, void* secret) +{ + logError("Application crashed"); + + ucontext_t context = *(ucontext_t*)secret; + time_t tnow; + char fileName[128]; + time(&tnow); + tm *ts = localtime(&tnow); + strftime(fileName, 128, (x11.appName + "-crash_-%d-%m-%Y_%H:%M:%S.txt").c_str(), ts); + + std::stringstream ss; + ss.flags(std::ios::hex | std::ios::showbase); +#if __WORDSIZE == 32 + ss << + ss << " at eip = " << context.uc_mcontext.gregs[REG_EIP] << std::endl; + ss << " eax = " << context.uc_mcontext.gregs[REG_EAX] << std::endl; + ss << " ebx = " << context.uc_mcontext.gregs[REG_EBX] << std::endl; + ss << " ecx = " << context.uc_mcontext.gregs[REG_ECX] << std::endl; + ss << " edx = " << context.uc_mcontext.gregs[REG_EDX] << std::endl; + ss << " esi = " << context.uc_mcontext.gregs[REG_ESI] << std::endl; + ss << " edi = " << context.uc_mcontext.gregs[REG_EDI] << std::endl; + ss << " ebp = " << context.uc_mcontext.gregs[REG_EBP] << std::endl; + ss << " esp = " << context.uc_mcontext.gregs[REG_ESP] << std::endl; + ss << " efl = " << context.uc_mcontext.gregs[REG_EFL] << std::endl; +#else // 64-bit + ss << " at rip = " << context.uc_mcontext.gregs[REG_RIP] << std::endl; + ss << " rax = " << context.uc_mcontext.gregs[REG_RAX] << std::endl; + ss << " rbx = " << context.uc_mcontext.gregs[REG_RBX] << std::endl; + ss << " rcx = " << context.uc_mcontext.gregs[REG_RCX] << std::endl; + ss << " rdx = " << context.uc_mcontext.gregs[REG_RDX] << std::endl; + ss << " rsi = " << context.uc_mcontext.gregs[REG_RSI] << std::endl; + ss << " rdi = " << context.uc_mcontext.gregs[REG_RDI] << std::endl; + ss << " rbp = " << context.uc_mcontext.gregs[REG_RBP] << std::endl; + ss << " rsp = " << context.uc_mcontext.gregs[REG_RSP] << std::endl; + ss << " efl = " << context.uc_mcontext.gregs[REG_EFL] << std::endl; +#endif + ss << std::endl; + ss.flags(std::ios::dec); + ss << " backtrace:" << std::endl; + + void* buffer[MAX_BACKTRACE_DEPTH]; + int numLevels = backtrace(buffer, MAX_BACKTRACE_DEPTH); + char **tracebackBuffer = backtrace_symbols(buffer, numLevels); + if(tracebackBuffer) { + for(int i = 2; i < numLevels; i++) { + std::string line = tracebackBuffer[i]; + if(line.find("__libc_start_main") != std::string::npos) + break; +#ifdef DEMANGLE_BACKTRACE_SYMBOLS + std::size_t demanglePos = line.find("(_Z"); + if(demanglePos != std::string::npos) { + demanglePos++; + int len = std::min(line.find_first_of("+", demanglePos), line.find_first_of(")", demanglePos)) - demanglePos; + std::string funcName = line.substr(demanglePos, len); + line.replace(demanglePos, len, Fw::demangleName(funcName.c_str())); + } +#endif + ss << " " << i-1 << ": " << line << std::endl; + } + free(tracebackBuffer); + } + + logInfo(ss.str()); + + std::ofstream out(fileName); + out << ss.str(); + out.close(); + logInfo("Crash report saved to file ", fileName); + + signal(SIGILL, SIG_DFL); + signal(SIGSEGV, SIG_DFL); + signal(SIGFPE, SIG_DFL); +} +#endif + + +#ifdef HANDLE_EXCEPTIONS + struct sigaction sa; + sa.sa_sigaction = &crashHandler; + sigemptyset (&sa.sa_mask); + sa.sa_flags = SA_RESTART | SA_SIGINFO; + + sigaction(SIGILL, &sa, NULL); // illegal instruction + sigaction(SIGSEGV, &sa, NULL); // segmentation fault + sigaction(SIGFPE, &sa, NULL); // floating-point exception +#endif \ No newline at end of file diff --git a/src/framework/platform/platform.h b/src/framework/platform/platform.h deleted file mode 100644 index c6e892b0..00000000 --- a/src/framework/platform/platform.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2010-2011 OTClient - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef PLATFORM_H -#define PLATFORM_H - -#include - -class PlatformListener; - -class Platform -{ -public: - void init(PlatformListener* platformListener, const char* appName); - void terminate(); - - /// Poll platform input/window events - void poll(); - - /// Sleep in current thread - void sleep(ulong ms); - - bool createWindow(int x, int y, int width, int height, int minWidth, int minHeight, bool maximized); - void setWindowIcon(const std::string& pngIcon); - void destroyWindow(); - void showWindow(); - void hideWindow(); - void setWindowTitle(const char* title); - bool isWindowFocused(); - bool isWindowVisible(); - int getWindowX(); - int getWindowY(); - int getWindowWidth(); - int getWindowHeight(); - bool isWindowMaximized(); - - int getDisplayHeight(); - int getDisplayWidth(); - - /// Get GL extension function address - void* getExtensionProcAddress(const char* ext); - /// Check if GLX/WGL extension is supported - bool isExtensionSupported(const char* ext); - - /// Get text from Ctrl+c - const char* getClipboardText(); - /// Set text for Ctrl+v - void setClipboardText(const char* text); - - void hideMouseCursor(); - void showMouseCursor(); - Point getMouseCursorPos(); - - /// Enable or disable vertical synchronization - void setVerticalSync(bool enable); - /// Swap GL buffers - void swapBuffers(); - - /// Get the app user directory, the place to save files configurations files - std::string getAppUserDir(); - - void displayFatalError(const std::string& message); -}; - -extern Platform g_platform; - -#endif diff --git a/src/framework/platform/platformevent.h b/src/framework/platform/platformevent.h deleted file mode 100644 index 697d19e6..00000000 --- a/src/framework/platform/platformevent.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2010-2011 OTClient - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef INPUTEVENT_H -#define INPUTEVENT_H - -#include - -enum PlatformEventType { - EventNone = 0, - EventMouseAction = 1, - EventKeyboardAction = 2, - EventDown = 4, - EventUp = 8, - EventMouseWheel = 16, - EventMouseLeftButton = 32, - EventMouseRightButton = 64, - EventMouseMidButton = 128, - EventKeyDown = EventKeyboardAction | EventDown, - EventKeyUp = EventKeyboardAction | EventUp, - EventMouseMove = EventMouseAction | 512, - EventMouseLeftButtonDown = EventMouseAction | EventMouseLeftButton | EventDown, - EventMouseLeftButtonUp = EventMouseAction | EventMouseLeftButton | EventUp, - EventMouseMiddleButtonDown = EventMouseAction | EventMouseMidButton | EventDown, - EventMouseMiddleButtonUp = EventMouseAction | EventMouseMidButton | EventUp, - EventMouseRightButtonDown = EventMouseAction | EventMouseRightButton | EventDown, - EventMouseRightButtonUp = EventMouseAction | EventMouseRightButton | EventUp, - EventMouseWheelUp = EventMouseAction | EventMouseWheel | EventUp, - EventMouseWheelDown = EventMouseAction | EventMouseWheel | EventDown -}; - -//TODO: rework platform events, make more compatible with UI events -struct PlatformEvent { - int type; - Point mousePos; - Point mouseMoved; - char keychar; - Fw::Key keycode; - bool ctrl; - bool shift; - bool alt; -}; - -#endif diff --git a/src/framework/core/engine.cpp b/src/framework/platform/platformwindow.cpp similarity index 86% rename from src/framework/core/engine.cpp rename to src/framework/platform/platformwindow.cpp index 4023427f..d6eed74e 100644 --- a/src/framework/core/engine.cpp +++ b/src/framework/platform/platformwindow.cpp @@ -20,5 +20,14 @@ * THE SOFTWARE. */ -#include "engine.h" +#include "platformwindow.h" +#ifdef WIN32 +#include "win32window.h" +WIN32Window window; +#else +#include "x11window.h" +X11Window window; +#endif + +PlatformWindow& g_window = window; diff --git a/src/framework/platform/platformwindow.h b/src/framework/platform/platformwindow.h new file mode 100644 index 00000000..bb9fd2d5 --- /dev/null +++ b/src/framework/platform/platformwindow.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2010-2011 OTClient + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef PLATFORMWINDOW_H +#define PLATFORMWINDOW_H + +#include +#include + +class PlatformWindow +{ + typedef std::function OnResizeCallback; + typedef std::function OnInputEventCallback; + +public: + virtual void init() = 0; + virtual void terminate() = 0; + + virtual void move(const Point& pos) = 0; + virtual void resize(const Size& size) = 0; + virtual void show() = 0; + virtual void hide() = 0; + virtual void maximize() = 0; + virtual void poll() = 0; + virtual void swapBuffers() = 0; + virtual void showMouse() = 0; + virtual void hideMouse() = 0; + + virtual void setTitle(const std::string& title) = 0; + virtual void setMinimumSize(const Size& minimumSize) = 0; + virtual void setFullscreen(bool fullscreen) = 0; + virtual void setVerticalSync(bool enable) = 0; + virtual void setIcon(const std::string& iconFile) = 0; + virtual void setClipboardText(const std::string& text) = 0; + + virtual Size getDisplaySize() = 0; + virtual std::string getClipboardText() = 0; + + int getDisplayWidth() { return getDisplaySize().width(); } + int getDisplayHeight() { return getDisplaySize().width(); } + + Size getSize() { return m_size; } + int getWidth() { return m_size.width(); } + int getHeight() { return m_size.height(); } + Point getPos() { return m_pos; } + int getX() { return m_pos.x; } + int getY() { return m_pos.y; } + Point getMousePos() { return m_inputEvent.mousePos; } + int getKeyboardModifiers() { return m_inputEvent.keyboardModifiers; } + + bool isVisible() { return m_visible; } + bool isFullscreen() { return m_fullscreen; } + bool hasFocus() { return m_focused; } + + void setOnClose(const SimpleCallback& onClose) { m_onClose = onClose; } + void setOnResize(const OnResizeCallback& onResize) { m_onResize = onResize; } + void setOnInputEvent(const OnInputEventCallback& onInputEvent) { m_onInputEvent = onInputEvent; } + +protected: + Size m_size; + Point m_pos; + InputEvent m_inputEvent; + + Boolean m_created; + Boolean m_visible; + Boolean m_focused; + Boolean m_fullscreen; + + SimpleCallback m_onClose; + OnResizeCallback m_onResize; + OnInputEventCallback m_onInputEvent; +}; + +extern PlatformWindow& g_window; + +#endif diff --git a/src/framework/platform/win32platform.cpp b/src/framework/platform/win32window.cpp similarity index 99% rename from src/framework/platform/win32platform.cpp rename to src/framework/platform/win32window.cpp index 7ba498dc..9c52470e 100644 --- a/src/framework/platform/win32platform.cpp +++ b/src/framework/platform/win32window.cpp @@ -20,6 +20,10 @@ * THE SOFTWARE. */ +// disabled for a while +#if 0 +#include "win32window.h" + #include "platform.h" #include "platformlistener.h" @@ -789,3 +793,4 @@ void Platform::displayFatalError(const std::string& message) MessageBoxA(NULL, message.c_str(), "Fatal Error", MB_OK | MB_ICONERROR); } +#endif diff --git a/src/framework/core/engine.h b/src/framework/platform/win32window.h similarity index 90% rename from src/framework/core/engine.h rename to src/framework/platform/win32window.h index 64b28a72..366e3512 100644 --- a/src/framework/core/engine.h +++ b/src/framework/platform/win32window.h @@ -20,14 +20,14 @@ * THE SOFTWARE. */ -#ifndef ENGINE_H -#define ENGINE_H +#ifndef WIN32WINDOW_H +#define WIN32WINDOW_H -#include "declarations.h" +#include "platformwindow.h" -class Engine +class WIN32Window : public PlatformWindow { + //TODO }; #endif - diff --git a/src/framework/platform/x11platform.cpp b/src/framework/platform/x11platform.cpp deleted file mode 100644 index 7e0180cb..00000000 --- a/src/framework/platform/x11platform.cpp +++ /dev/null @@ -1,989 +0,0 @@ -/* - * Copyright (c) 2010-2011 OTClient - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "platform.h" -#include "platformlistener.h" - -#include -#include -#include - -#include - -#include - -#include -#include - -#include -#include - -struct X11PlatformPrivate { - Display *display; - XVisualInfo *visual; - GLXContext glxContext; - XIM xim; - XIC xic; - Colormap colormap; - Window window; - Cursor cursor; - Atom atomDeleteWindow; - Atom atomClipboard; - Atom atomTargets; - Atom atomText; - Atom atomCompoundText; - Atom atomUTF8String; - Atom atomWindowState; - Atom atomWindowMaximizedVert; - Atom atomWindowMaximizedHorz; - bool visible; - bool focused; - bool maximizeOnFirstShow; - std::string appName; - int width; - int height; - int x; - int y; - int lastTicks; - std::string clipboardText; - std::map keyMap; - PlatformListener* listener; - PlatformEvent platformEvent; -} x11; - -Platform g_platform; - -#ifdef HANDLE_EXCEPTIONS - -#include -#include - -#define MAX_BACKTRACE_DEPTH 128 -#define DEMANGLE_BACKTRACE_SYMBOLS - -void crashHandler(int signum, siginfo_t* info, void* secret) -{ - logError("Application crashed"); - - ucontext_t context = *(ucontext_t*)secret; - time_t tnow; - char fileName[128]; - time(&tnow); - tm *ts = localtime(&tnow); - strftime(fileName, 128, (x11.appName + "-crash_-%d-%m-%Y_%H:%M:%S.txt").c_str(), ts); - - std::stringstream ss; - ss.flags(std::ios::hex | std::ios::showbase); -#if __WORDSIZE == 32 - ss << - ss << " at eip = " << context.uc_mcontext.gregs[REG_EIP] << std::endl; - ss << " eax = " << context.uc_mcontext.gregs[REG_EAX] << std::endl; - ss << " ebx = " << context.uc_mcontext.gregs[REG_EBX] << std::endl; - ss << " ecx = " << context.uc_mcontext.gregs[REG_ECX] << std::endl; - ss << " edx = " << context.uc_mcontext.gregs[REG_EDX] << std::endl; - ss << " esi = " << context.uc_mcontext.gregs[REG_ESI] << std::endl; - ss << " edi = " << context.uc_mcontext.gregs[REG_EDI] << std::endl; - ss << " ebp = " << context.uc_mcontext.gregs[REG_EBP] << std::endl; - ss << " esp = " << context.uc_mcontext.gregs[REG_ESP] << std::endl; - ss << " efl = " << context.uc_mcontext.gregs[REG_EFL] << std::endl; -#else // 64-bit - ss << " at rip = " << context.uc_mcontext.gregs[REG_RIP] << std::endl; - ss << " rax = " << context.uc_mcontext.gregs[REG_RAX] << std::endl; - ss << " rbx = " << context.uc_mcontext.gregs[REG_RBX] << std::endl; - ss << " rcx = " << context.uc_mcontext.gregs[REG_RCX] << std::endl; - ss << " rdx = " << context.uc_mcontext.gregs[REG_RDX] << std::endl; - ss << " rsi = " << context.uc_mcontext.gregs[REG_RSI] << std::endl; - ss << " rdi = " << context.uc_mcontext.gregs[REG_RDI] << std::endl; - ss << " rbp = " << context.uc_mcontext.gregs[REG_RBP] << std::endl; - ss << " rsp = " << context.uc_mcontext.gregs[REG_RSP] << std::endl; - ss << " efl = " << context.uc_mcontext.gregs[REG_EFL] << std::endl; -#endif - ss << std::endl; - ss.flags(std::ios::dec); - ss << " backtrace:" << std::endl; - - void* buffer[MAX_BACKTRACE_DEPTH]; - int numLevels = backtrace(buffer, MAX_BACKTRACE_DEPTH); - char **tracebackBuffer = backtrace_symbols(buffer, numLevels); - if(tracebackBuffer) { - for(int i = 2; i < numLevels; i++) { - std::string line = tracebackBuffer[i]; - if(line.find("__libc_start_main") != std::string::npos) - break; -#ifdef DEMANGLE_BACKTRACE_SYMBOLS - std::size_t demanglePos = line.find("(_Z"); - if(demanglePos != std::string::npos) { - demanglePos++; - int len = std::min(line.find_first_of("+", demanglePos), line.find_first_of(")", demanglePos)) - demanglePos; - std::string funcName = line.substr(demanglePos, len); - line.replace(demanglePos, len, Fw::demangleName(funcName.c_str())); - } -#endif - ss << " " << i-1 << ": " << line << std::endl; - } - free(tracebackBuffer); - } - - logInfo(ss.str()); - - std::ofstream out(fileName); - out << ss.str(); - out.close(); - logInfo("Crash report saved to file ", fileName); - - signal(SIGILL, SIG_DFL); - signal(SIGSEGV, SIG_DFL); - signal(SIGFPE, SIG_DFL); -} -#endif - -void Platform::init(PlatformListener* platformListener, const char *appName) -{ -#ifdef HANDLE_EXCEPTIONS - struct sigaction sa; - sa.sa_sigaction = &crashHandler; - sigemptyset (&sa.sa_mask); - sa.sa_flags = SA_RESTART | SA_SIGINFO; - - sigaction(SIGILL, &sa, NULL); // illegal instruction - sigaction(SIGSEGV, &sa, NULL); // segmentation fault - sigaction(SIGFPE, &sa, NULL); // floating-point exception -#endif - - // seend random numbers - srand(time(NULL)); - - x11.appName = appName; - x11.display = NULL; - x11.visual = NULL; - x11.glxContext = NULL; - x11.xim = NULL; - x11.xic = NULL; - x11.colormap = None; - x11.window = None; - x11.cursor = None; - x11.visible = false; - x11.focused = false; - x11.width = 0; - x11.height = 0; - x11.maximizeOnFirstShow = false; - x11.listener = platformListener; - - x11.keyMap[XK_Escape] = Fw::KeyEscape; - x11.keyMap[XK_Tab] = Fw::KeyTab; - x11.keyMap[XK_Return] = Fw::KeyReturn; - x11.keyMap[XK_BackSpace] = Fw::KeyBackspace; - - x11.keyMap[XK_Page_Up] = Fw::KeyPageUp; - x11.keyMap[XK_Page_Down] = Fw::KeyPageDown; - x11.keyMap[XK_Home] = Fw::KeyHome; - x11.keyMap[XK_End] = Fw::KeyEnd; - x11.keyMap[XK_Insert] = Fw::KeyInsert; - x11.keyMap[XK_Delete] = Fw::KeyDelete; - - x11.keyMap[XK_Up] = Fw::KeyUp; - x11.keyMap[XK_Down] = Fw::KeyDown; - x11.keyMap[XK_Left] = Fw::KeyLeft; - x11.keyMap[XK_Right] = Fw::KeyRight; - - x11.keyMap[XK_Num_Lock] = Fw::KeyNumLock; - x11.keyMap[XK_Scroll_Lock] = Fw::KeyScrollLock; - x11.keyMap[XK_Caps_Lock] = Fw::KeyCapsLock; - x11.keyMap[XK_Print] = Fw::KeyPrintScreen; - x11.keyMap[XK_Pause] = Fw::KeyPause; - - x11.keyMap[XK_Control_L] = Fw::KeyCtrl; - x11.keyMap[XK_Control_R] = Fw::KeyCtrl; - x11.keyMap[XK_Shift_R] = Fw::KeyShift; - x11.keyMap[XK_Shift_L] = Fw::KeyShift; - x11.keyMap[XK_Alt_R] = Fw::KeyAlt; - x11.keyMap[XK_Alt_L] = Fw::KeyAltGr; - x11.keyMap[XK_Meta_L] = Fw::KeyMeta; - x11.keyMap[XK_Meta_R] = Fw::KeyMeta; - x11.keyMap[XK_Menu] = Fw::KeyMenu; - - // ascii characters - x11.keyMap[XK_space] = Fw::KeySpace; - x11.keyMap[XK_exclam] = Fw::KeyExclamation; - x11.keyMap[XK_quotedbl] = Fw::KeyQuote; - x11.keyMap[XK_numbersign] = Fw::KeyNumberSign; - x11.keyMap[XK_dollar] = Fw::KeyDollar; - x11.keyMap[XK_percent] = Fw::KeyPercent; - x11.keyMap[XK_ampersand] = Fw::KeyAmpersand; - x11.keyMap[XK_apostrophe] = Fw::KeyApostrophe; - x11.keyMap[XK_parenleft] = Fw::KeyLeftParen; - x11.keyMap[XK_parenright] = Fw::KeyRightParen; - x11.keyMap[XK_asterisk] = Fw::KeyAsterisk; - x11.keyMap[XK_plus] = Fw::KeyPlus; - x11.keyMap[XK_comma] = Fw::KeyComma; - x11.keyMap[XK_minus] = Fw::KeyMinus; - x11.keyMap[XK_period] = Fw::KeyPeriod; - x11.keyMap[XK_slash] = Fw::KeySlash; - x11.keyMap[XK_1] = Fw::Key1; - x11.keyMap[XK_2] = Fw::Key2; - x11.keyMap[XK_3] = Fw::Key3; - x11.keyMap[XK_4] = Fw::Key4; - x11.keyMap[XK_5] = Fw::Key5; - x11.keyMap[XK_6] = Fw::Key6; - x11.keyMap[XK_7] = Fw::Key7; - x11.keyMap[XK_8] = Fw::Key8; - x11.keyMap[XK_9] = Fw::Key9; - x11.keyMap[XK_0] = Fw::Key0; - x11.keyMap[XK_colon] = Fw::KeyColon; - x11.keyMap[XK_semicolon] = Fw::KeySemicolon; - x11.keyMap[XK_less] = Fw::KeyLess; - x11.keyMap[XK_equal] = Fw::KeyEqual; - x11.keyMap[XK_greater] = Fw::KeyGreater; - x11.keyMap[XK_question] = Fw::KeyQuestion; - x11.keyMap[XK_at] = Fw::KeyAtSign; - x11.keyMap[XK_a] = Fw::KeyA; - x11.keyMap[XK_b] = Fw::KeyB; - x11.keyMap[XK_c] = Fw::KeyC; - x11.keyMap[XK_d] = Fw::KeyD; - x11.keyMap[XK_e] = Fw::KeyE; - x11.keyMap[XK_f] = Fw::KeyF; - x11.keyMap[XK_g] = Fw::KeyG; - x11.keyMap[XK_h] = Fw::KeyH; - x11.keyMap[XK_i] = Fw::KeyI; - x11.keyMap[XK_j] = Fw::KeyJ; - x11.keyMap[XK_k] = Fw::KeyK; - x11.keyMap[XK_l] = Fw::KeyL; - x11.keyMap[XK_m] = Fw::KeyM; - x11.keyMap[XK_n] = Fw::KeyN; - x11.keyMap[XK_o] = Fw::KeyO; - x11.keyMap[XK_p] = Fw::KeyP; - x11.keyMap[XK_q] = Fw::KeyQ; - x11.keyMap[XK_r] = Fw::KeyR; - x11.keyMap[XK_s] = Fw::KeyS; - x11.keyMap[XK_t] = Fw::KeyT; - x11.keyMap[XK_u] = Fw::KeyU; - x11.keyMap[XK_v] = Fw::KeyV; - x11.keyMap[XK_w] = Fw::KeyW; - x11.keyMap[XK_x] = Fw::KeyX; - x11.keyMap[XK_y] = Fw::KeyY; - x11.keyMap[XK_z] = Fw::KeyZ; - x11.keyMap[XK_bracketleft] = Fw::KeyLeftBracket; - x11.keyMap[XK_backslash] = Fw::KeyBackslash; - x11.keyMap[XK_bracketright] = Fw::KeyRightBracket; - x11.keyMap[XK_asciicircum] = Fw::KeyCaret; - x11.keyMap[XK_underscore] = Fw::KeyUnderscore; - x11.keyMap[XK_grave] = Fw::KeyGrave; - x11.keyMap[XK_braceleft] = Fw::KeyLeftCurly; - x11.keyMap[XK_bar] = Fw::KeyBar; - x11.keyMap[XK_braceright] = Fw::KeyRightCurly; - x11.keyMap[XK_asciitilde] = Fw::KeyTilde; - - // keypad - x11.keyMap[XK_KP_Add] = Fw::KeyPlus; - x11.keyMap[XK_KP_Subtract] = Fw::KeyMinus; - x11.keyMap[XK_KP_Decimal] = Fw::KeyPeriod; - x11.keyMap[XK_KP_Divide] = Fw::KeySlash; - x11.keyMap[XK_KP_Multiply] = Fw::KeyAsterisk; - x11.keyMap[XK_KP_Enter] = Fw::KeyEnter; - - // keypad with numlock off - x11.keyMap[XK_KP_Insert] = Fw::KeyNumpad0; - x11.keyMap[XK_KP_End] = Fw::KeyNumpad1; - x11.keyMap[XK_KP_Down] = Fw::KeyNumpad2; - x11.keyMap[XK_KP_Page_Down] = Fw::KeyNumpad3; - x11.keyMap[XK_KP_Left] = Fw::KeyNumpad4; - x11.keyMap[XK_KP_Begin] = Fw::KeyNumpad5; - x11.keyMap[XK_KP_Right] = Fw::KeyNumpad6; - x11.keyMap[XK_KP_Home] = Fw::KeyNumpad7; - x11.keyMap[XK_KP_Up] = Fw::KeyNumpad8; - x11.keyMap[XK_KP_Page_Up] = Fw::KeyNumpad9; - x11.keyMap[XK_KP_Delete] = Fw::KeyDelete; - - // keypad with numlock on - x11.keyMap[XK_KP_0] = Fw::Key0; - x11.keyMap[XK_KP_1] = Fw::Key1; - x11.keyMap[XK_KP_2] = Fw::Key2; - x11.keyMap[XK_KP_3] = Fw::Key3; - x11.keyMap[XK_KP_4] = Fw::Key4; - x11.keyMap[XK_KP_5] = Fw::Key5; - x11.keyMap[XK_KP_6] = Fw::Key6; - x11.keyMap[XK_KP_7] = Fw::Key7; - x11.keyMap[XK_KP_8] = Fw::Key8; - x11.keyMap[XK_KP_9] = Fw::Key9; - - x11.keyMap[XK_F1] = Fw::KeyF1; - x11.keyMap[XK_F2] = Fw::KeyF2; - x11.keyMap[XK_F3] = Fw::KeyF3; - x11.keyMap[XK_F4] = Fw::KeyF4; - x11.keyMap[XK_F5] = Fw::KeyF5; - x11.keyMap[XK_F6] = Fw::KeyF6; - x11.keyMap[XK_F7] = Fw::KeyF7; - x11.keyMap[XK_F8] = Fw::KeyF8; - x11.keyMap[XK_F9] = Fw::KeyF9; - x11.keyMap[XK_F10] = Fw::KeyF10; - x11.keyMap[XK_F11] = Fw::KeyF11; - x11.keyMap[XK_F12] = Fw::KeyF12; - - // try to set a latin1 locales otherwise fallback to standard C locale - static char locales[][32] = { "en_US.iso88591", "iso88591", "en_US", "C" }; - for(int i=0;i<4;++i) { - if(setlocale(LC_ALL, locales[i])) - break; - } - - // open display - x11.display = XOpenDisplay(0); - if(!x11.display) - logFatal("Failed to open X display"); - - // check if GLX is supported on this display - if(!glXQueryExtension(x11.display, 0, 0)) - logFatal("GLX not supported"); - - // retrieve GLX version - int glxMajor; - int glxMinor; - if(!glXQueryVersion(x11.display, &glxMajor, &glxMinor)) - logFatal("Unable to query GLX version"); - logInfo("GLX version ",glxMajor,".",glxMinor); - - // clipboard related atoms - x11.atomClipboard = XInternAtom(x11.display, "CLIPBOARD", False); - x11.atomTargets = XInternAtom(x11.display, "TARGETS", False); - x11.atomUTF8String = XInternAtom(x11.display, "UTF8_STRING", False); - x11.atomText = XInternAtom(x11.display, "TEXT", False); - x11.atomCompoundText = XInternAtom(x11.display, "COMPOUND_TEXT", False); - x11.atomWindowState = XInternAtom(x11.display, "_NET_WM_STATE", False); - x11.atomWindowMaximizedVert = XInternAtom(x11.display, "_NET_WM_STATE_MAXIMIZED_VERT", False); - x11.atomWindowMaximizedHorz = XInternAtom(x11.display, "_NET_WM_STATE_MAXIMIZED_HORZ", False); -} - -void Platform::terminate() -{ - if(x11.window) { - destroyWindow(); - x11.window = None; - } - - // close display - if(x11.display) { - XCloseDisplay(x11.display); - x11.display = NULL; - } -} - -void Platform::poll() -{ - XEvent event, peekevent; - PlatformEvent& platformEvent = x11.platformEvent; - static int oldWidth = -1; - static int oldHeight = -1; - while(XPending(x11.display) > 0) { - XNextEvent(x11.display, &event); - - // call filter because xim will discard KeyPress events when keys still composing - if(XFilterEvent(&event, x11.window)) - continue; - - // discard events of repeated key releases - if(event.type == KeyRelease && XPending(x11.display)) { - XPeekEvent(x11.display, &peekevent); - if((peekevent.type == KeyPress) && - (peekevent.xkey.keycode == event.xkey.keycode) && - ((peekevent.xkey.time-event.xkey.time) < 2)) - continue; - } - - switch(event.type) { - case Expose: - // needs redraw - break; - case ConfigureNotify: - // window resize - if(oldWidth != event.xconfigure.width || oldHeight != event.xconfigure.height) { - x11.listener->onResize(Size(event.xconfigure.width, event.xconfigure.height)); - oldWidth = event.xconfigure.width; - oldHeight = event.xconfigure.height; - } - - if(!isWindowMaximized()) { - x11.width = event.xconfigure.width; - x11.height = event.xconfigure.height; - - // hack to fix x11 windows move gaps - static int gap_x = -1, gap_y = -1; - if(gap_x == -1 && gap_y == -1) { - gap_x = event.xconfigure.x; - gap_y = event.xconfigure.y; - } - x11.x = event.xconfigure.x - gap_x; - x11.y = event.xconfigure.y - gap_y; - } - break; - - case KeyPress: - case KeyRelease: { - KeySym keysym; - char buf[32]; - int len; - - platformEvent.ctrl = (event.xkey.state & ControlMask); - platformEvent.shift = (event.xkey.state & ShiftMask); - platformEvent.alt = (event.xkey.state & Mod1Mask); - platformEvent.keychar = 0; - - // fire enter text event - if(event.type == KeyPress && !platformEvent.ctrl && !platformEvent.alt) { - if(x11.xic) { // with xim we can get latin1 input correctly - Status status; - len = XmbLookupString(x11.xic, &event.xkey, buf, sizeof(buf), &keysym, &status); - } else { // otherwise use XLookupString, but it doesn't work right with dead keys often - static XComposeStatus compose = {NULL, 0}; - len = XLookupString(&event.xkey, buf, sizeof(buf), &keysym, &compose); - } - - if(len > 0 && - // these keys produces characters that we don't want to capture - keysym != XK_BackSpace && - keysym != XK_Return && - keysym != XK_Delete && - keysym != XK_Escape && - (uchar)(buf[0]) >= 32 - ) { - //logDebug("char: ", buf[0], " code: ", (uint)buf[0]); - platformEvent.keychar = buf[0]; - } - } else { - //event.xkey.state &= ~(ShiftMask | LockMask); - len = XLookupString(&event.xkey, buf, sizeof(buf), &keysym, 0); - - if(len > 0 && (uchar)platformEvent.keychar >= 32) - platformEvent.keychar = (len > 0) ? buf[0] : 0; - } - - if(x11.keyMap.find(keysym) != x11.keyMap.end()) - platformEvent.keycode = x11.keyMap[keysym]; - else - platformEvent.keycode = Fw::KeyUnknown; - - platformEvent.keycode = x11.keyMap[keysym]; - platformEvent.type = (event.type == KeyPress) ? EventKeyDown : EventKeyUp; - - if(platformEvent.keycode != Fw::KeyUnknown || platformEvent.keychar != 0) - x11.listener->onPlatformEvent(platformEvent); - break; - } - case ButtonPress: - case ButtonRelease: - switch(event.xbutton.button) { - case Button1: - platformEvent.type = (event.type == ButtonPress) ? EventMouseLeftButtonDown : EventMouseLeftButtonUp; - break; - case Button3: - platformEvent.type = (event.type == ButtonPress) ? EventMouseRightButtonDown : EventMouseRightButtonUp; - break; - case Button2: - platformEvent.type = (event.type == ButtonPress) ? EventMouseMiddleButtonDown : EventMouseMiddleButtonUp; - break; - case Button4: - platformEvent.type = EventMouseWheelUp; - break; - case Button5: - platformEvent.type = EventMouseWheelDown; - break; - } - x11.listener->onPlatformEvent(platformEvent); - break; - - case MotionNotify: - { - platformEvent.type = EventMouseMove; - Point newMousePos(event.xbutton.x, event.xbutton.y); - platformEvent.mouseMoved = newMousePos - platformEvent.mousePos; - platformEvent.mousePos = newMousePos; - x11.listener->onPlatformEvent(platformEvent); - break; - } - - case MapNotify: - x11.visible = true; - break; - - case UnmapNotify: - x11.visible = false; - break; - - case FocusIn: - x11.focused = true; - break; - - case FocusOut: - x11.focused = false; - break; - - // clipboard data request - case SelectionRequest: - { - XEvent respond; - XSelectionRequestEvent *req = &(event.xselectionrequest); - - if(req->target == x11.atomTargets ) { - Atom typeList[] = {x11.atomText, x11.atomCompoundText, x11.atomUTF8String, XA_STRING}; - - XChangeProperty(x11.display, req->requestor, - req->property, req->target, - 8, PropModeReplace, - (uchar *) &typeList, - sizeof(typeList)); - respond.xselection.property = req->property; - } else { - XChangeProperty(x11.display, - req->requestor, - req->property, req->target, - 8, - PropModeReplace, - (uchar*) x11.clipboardText.c_str(), - x11.clipboardText.size()); - respond.xselection.property = req->property; - } - - respond.xselection.type = SelectionNotify; - respond.xselection.display = req->display; - respond.xselection.requestor = req->requestor; - respond.xselection.selection = req->selection; - respond.xselection.target = req->target; - respond.xselection.time = req->time; - XSendEvent(x11.display, req->requestor, 0, 0, &respond); - XFlush(x11.display); - break; - } - - case ClientMessage: - { - if((Atom)event.xclient.data.l[0] == x11.atomDeleteWindow) - x11.listener->onClose(); - break; - } - } - } -} - -bool Platform::createWindow(int x, int y, int width, int height, int minWidth, int minHeight, bool maximized) -{ - static int attrList[] = { - GLX_USE_GL, - GLX_RGBA, - GLX_DOUBLEBUFFER, - None - }; - - // choose OpenGL, RGBA, double buffered, visual - x11.visual = glXChooseVisual(x11.display, DefaultScreen(x11.display), attrList); - if(!x11.visual) - logFatal("RGBA/Double buffered visual not supported"); - - // create GLX context - x11.glxContext = glXCreateContext(x11.display, x11.visual, NULL, True); - if(!x11.glxContext) - logFatal("Unable to create GLX context"); - - logInfo("Direct rendering: ", glXIsDirect(x11.display, x11.glxContext) ? "Yes" : "No"); - - // color map - x11.colormap = XCreateColormap(x11.display, - RootWindow(x11.display, x11.visual->screen), - x11.visual->visual, - AllocNone); - - // setup window type - XSetWindowAttributes attr; - attr.colormap = x11.colormap; - attr.border_pixel = 0; - attr.event_mask = KeyPressMask | KeyReleaseMask | - ButtonPressMask | ButtonReleaseMask | PointerMotionMask | - ExposureMask | VisibilityChangeMask | - StructureNotifyMask | FocusChangeMask; - unsigned int mask = CWBorderPixel | CWColormap | CWEventMask; - - x11.x = x; - x11.y = y; - - // create the window - x11.window = XCreateWindow(x11.display, - RootWindow(x11.display, x11.visual->screen), - 0, 0, - width, height, - 0, - x11.visual->depth, - InputOutput, - x11.visual->visual, - mask, - &attr); - if(!x11.window) - logFatal("XCreateWindow failed"); - - // create input context (to have better key input handling) - if(XSupportsLocale()) { - XSetLocaleModifiers(""); - x11.xim = XOpenIM(x11.display, NULL, NULL, NULL); - if(x11.xim) { - x11.xic = XCreateIC(x11.xim, - XNInputStyle, - XIMPreeditNothing | XIMStatusNothing, - XNClientWindow, x11.window, NULL); - if(!x11.xic) - logError("Unable to create the input context"); - } else - logError("Failed to open an input method"); - } else - logError("X11 does not support the current locale"); - - if(!x11.xic) - logWarning("Input of special keys maybe messed up because we couldn't create an input context"); - - // set window minimum size - XSizeHints xsizehints; - xsizehints.flags = PMinSize; - xsizehints.min_width = minWidth; - xsizehints.min_height= minHeight; - XSetWMSizeHints(x11.display, x11.window, &xsizehints, XA_WM_NORMAL_HINTS); - - // handle delete window event - x11.atomDeleteWindow = XInternAtom(x11.display, "WM_DELETE_WINDOW", True); - XSetWMProtocols(x11.display, x11.window, &x11.atomDeleteWindow , 1); - - // connect the GLX-context to the window - if(!glXMakeCurrent(x11.display, x11.window, x11.glxContext)) - logFatal("glXMakeCurrent failed"); - - x11.width = width; - x11.height = height; - x11.maximizeOnFirstShow = maximized; - - // call first onResize - x11.listener->onResize(Size(width, height)); - - return true; -} - -void Platform::setWindowIcon(const std::string& pngIcon) -{ - apng_data apng; - std::stringstream fin; - g_resources.loadFile(pngIcon, fin); - if(load_apng(fin, &apng) == 0) { - if(apng.bpp != 4) { - logError("could not set app icon, icon image must have 4 channels"); - free_apng(&apng); - return; - } - - int n = apng.width * apng.height; - std::vector iconData(n + 2); - iconData[0] = apng.width; - iconData[1] = apng.height; - for(int i=0; i < n;++i) { - uint8 *pixel = (uint8*)&iconData[2 + i]; - pixel[2] = *(apng.pdata + (i * 4) + 0); - pixel[1] = *(apng.pdata + (i * 4) + 1); - pixel[0] = *(apng.pdata + (i * 4) + 2); - pixel[3] = *(apng.pdata + (i * 4) + 3); - } - - Atom property = XInternAtom(x11.display, "_NET_WM_ICON", 0); - if(!XChangeProperty(x11.display, x11.window, property, XA_CARDINAL, 32, PropModeReplace, (const unsigned char*)&iconData[0], iconData.size())) - logError("Couldn't set app icon"); - - free_apng(&apng); - } else - logError("Couldn't load app icon"); -} - -void Platform::destroyWindow() -{ - if(x11.glxContext) { - glXMakeCurrent(x11.display, None, NULL); - glXDestroyContext(x11.display, x11.glxContext); - x11.glxContext = NULL; - } - - if(x11.visual) { - XFree(x11.visual); - x11.visual = NULL; - } - - if(x11.colormap != None) { - XFreeColormap(x11.display, x11.colormap); - x11.colormap = 0; - } - - if(x11.window != None) { - XDestroyWindow(x11.display, x11.window); - x11.window = None; - } - - if(x11.xic) { - XDestroyIC(x11.xic); - x11.xic = NULL; - } - - if(x11.xim) { - XCloseIM(x11.xim); - x11.xim = NULL; - } -} - -void Platform::showWindow() -{ - XMapWindow(x11.display, x11.window); - - static bool firstShow = true; - if(firstShow) { - // move window - XMoveWindow(x11.display, x11.window, x11.x, x11.y); - - // set window maximized if needed - if(x11.maximizeOnFirstShow) { - XEvent e; - memset(&e, 0, sizeof(XEvent)); - e.xany.type = ClientMessage; - e.xclient.message_type = x11.atomWindowState; - e.xclient.format = 32; - e.xclient.window = x11.window; - e.xclient.data.l[0] = 1l; - e.xclient.data.l[1] = x11.atomWindowMaximizedVert; - e.xclient.data.l[2] = x11.atomWindowMaximizedHorz; - e.xclient.data.l[3] = 0l; - - XSendEvent(x11.display, RootWindow(x11.display, x11.visual->screen), 0, - SubstructureNotifyMask | SubstructureRedirectMask, &e); - } - firstShow = false; - } -} - -void Platform::hideWindow() -{ - XUnmapWindow(x11.display, x11.window); -} - -void Platform::setWindowTitle(const char *title) -{ - XStoreName(x11.display, x11.window, title); - XSetIconName(x11.display, x11.window, title); -} - -void *Platform::getExtensionProcAddress(const char *ext) -{ - return (void*)glXGetProcAddressARB((const GLubyte*)ext); -} - -bool Platform::isExtensionSupported(const char *ext) -{ - const char *exts = glXQueryExtensionsString(x11.display, DefaultScreen(x11.display)); - if(strstr(exts, ext)) - return true; - return false; -} - -const char *Platform::getClipboardText() -{ - Window ownerWindow = XGetSelectionOwner(x11.display, x11.atomClipboard); - if(ownerWindow == x11.window) - return x11.clipboardText.c_str(); - - std::string clipboard = ""; - if(ownerWindow != None) { - XConvertSelection(x11.display, x11.atomClipboard, XA_STRING, 0, ownerWindow, CurrentTime); - XFlush(x11.display); - - // hack to wait SelectioNotify event, otherwise we will get wrong clipboard pastes - usleep(100 * 1000); - - // check for data - Atom type; - int format; - ulong numItems, bytesLeft, dummy; - uchar *data; - XGetWindowProperty(x11.display, ownerWindow, - XA_STRING, - 0, 0, 0, - AnyPropertyType, - &type, - &format, - &numItems, - &bytesLeft, - &data); - if(bytesLeft > 0) { - // get the data get - int result = XGetWindowProperty(x11.display, ownerWindow, - XA_STRING, - 0, - bytesLeft, - 0, - AnyPropertyType, - &type, - &format, - &numItems, - &dummy, - &data); - if(result == Success) - clipboard = (const char*)data; - XFree(data); - } - } - return clipboard.c_str(); -} - -void Platform::setClipboardText(const char *text) -{ - x11.clipboardText = text; - XSetSelectionOwner(x11.display, x11.atomClipboard, x11.window, CurrentTime); - XFlush(x11.display); -} - -void Platform::hideMouseCursor() -{ - if(x11.cursor == None) { - char bm[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - Pixmap pix = XCreateBitmapFromData(x11.display, x11.window, bm, 8, 8); - XColor black; - memset(&black, 0, sizeof(XColor)); - black.flags = DoRed | DoGreen | DoBlue; - x11.cursor = XCreatePixmapCursor(x11.display, pix, pix, &black, &black, 0, 0); - XFreePixmap(x11.display, pix); - } - XDefineCursor(x11.display, x11.window, x11.cursor); -} - -void Platform::showMouseCursor() -{ - XUndefineCursor(x11.display, x11.window); - if(x11.cursor != None) { - XFreeCursor(x11.display, x11.cursor); - x11.cursor = None; - } -} - -Point Platform::getMouseCursorPos() -{ - return x11.platformEvent.mousePos; -} - -void Platform::setVerticalSync(bool enable) -{ - typedef GLint (*glSwapIntervalProc)(GLint); - glSwapIntervalProc glSwapInterval = NULL; - - if(isExtensionSupported("GLX_MESA_swap_control")) - glSwapInterval = (glSwapIntervalProc)getExtensionProcAddress("glXSwapIntervalMESA"); - else if(isExtensionSupported("GLX_SGI_swap_control")) - glSwapInterval = (glSwapIntervalProc)getExtensionProcAddress("glXSwapIntervalSGI"); - - if(glSwapInterval) - glSwapInterval(enable ? 1 : 0); -} - -void Platform::swapBuffers() -{ - //glFlush(); - //glFinish(); - glXSwapBuffers(x11.display, x11.window); -} - -bool Platform::isWindowFocused() -{ - return x11.focused; -} - -bool Platform::isWindowVisible() -{ - return x11.visible; -} - -int Platform::getWindowX() -{ - return x11.x; -} - -int Platform::getWindowY() -{ - return x11.y; -} - -int Platform::getWindowWidth() -{ - return x11.width; -} - -int Platform::getWindowHeight() -{ - return x11.height; -} - -int Platform::getDisplayWidth() -{ - return XDisplayWidth(x11.display, DefaultScreen(x11.display)); -} - -int Platform::getDisplayHeight() -{ - return XDisplayHeight(x11.display, DefaultScreen(x11.display)); -} - -bool Platform::isWindowMaximized() -{ - bool ret = false; - Atom actualType; - int actualFormat; - ulong i, numItems, bytesAfter; - uchar *propertyValue = NULL; - long maxLength = 1024; - - if(XGetWindowProperty(x11.display, x11.window, x11.atomWindowState, - 0l, maxLength, False, XA_ATOM, &actualType, - &actualFormat, &numItems, &bytesAfter, - &propertyValue) == Success) { - Atom *atoms = (Atom *)propertyValue; - int maximized = 0; - - for(i=0; i + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "x11window.h" +#include +#include + +X11Window::X11Window() +{ + m_cursor = None; + m_visual = 0; + m_colormap = 0; + + m_keyMap[XK_Escape] = Fw::KeyEscape; + m_keyMap[XK_Tab] = Fw::KeyTab; + m_keyMap[XK_Return] = Fw::KeyReturn; + m_keyMap[XK_BackSpace] = Fw::KeyBackspace; + + m_keyMap[XK_Page_Up] = Fw::KeyPageUp; + m_keyMap[XK_Page_Down] = Fw::KeyPageDown; + m_keyMap[XK_Home] = Fw::KeyHome; + m_keyMap[XK_End] = Fw::KeyEnd; + m_keyMap[XK_Insert] = Fw::KeyInsert; + m_keyMap[XK_Delete] = Fw::KeyDelete; + + m_keyMap[XK_Up] = Fw::KeyUp; + m_keyMap[XK_Down] = Fw::KeyDown; + m_keyMap[XK_Left] = Fw::KeyLeft; + m_keyMap[XK_Right] = Fw::KeyRight; + + m_keyMap[XK_Num_Lock] = Fw::KeyNumLock; + m_keyMap[XK_Scroll_Lock] = Fw::KeyScrollLock; + m_keyMap[XK_Caps_Lock] = Fw::KeyCapsLock; + m_keyMap[XK_Print] = Fw::KeyPrintScreen; + m_keyMap[XK_Pause] = Fw::KeyPause; + + m_keyMap[XK_Control_L] = Fw::KeyCtrl; + m_keyMap[XK_Control_R] = Fw::KeyCtrl; + m_keyMap[XK_Shift_R] = Fw::KeyShift; + m_keyMap[XK_Shift_L] = Fw::KeyShift; + m_keyMap[XK_Alt_R] = Fw::KeyAlt; + m_keyMap[XK_Alt_L] = Fw::KeyAltGr; + m_keyMap[XK_Meta_L] = Fw::KeyMeta; + m_keyMap[XK_Meta_R] = Fw::KeyMeta; + m_keyMap[XK_Menu] = Fw::KeyMenu; + + // ascii characters + m_keyMap[XK_space] = Fw::KeySpace; + m_keyMap[XK_exclam] = Fw::KeyExclamation; + m_keyMap[XK_quotedbl] = Fw::KeyQuote; + m_keyMap[XK_numbersign] = Fw::KeyNumberSign; + m_keyMap[XK_dollar] = Fw::KeyDollar; + m_keyMap[XK_percent] = Fw::KeyPercent; + m_keyMap[XK_ampersand] = Fw::KeyAmpersand; + m_keyMap[XK_apostrophe] = Fw::KeyApostrophe; + m_keyMap[XK_parenleft] = Fw::KeyLeftParen; + m_keyMap[XK_parenright] = Fw::KeyRightParen; + m_keyMap[XK_asterisk] = Fw::KeyAsterisk; + m_keyMap[XK_plus] = Fw::KeyPlus; + m_keyMap[XK_comma] = Fw::KeyComma; + m_keyMap[XK_minus] = Fw::KeyMinus; + m_keyMap[XK_period] = Fw::KeyPeriod; + m_keyMap[XK_slash] = Fw::KeySlash; + m_keyMap[XK_1] = Fw::Key1; + m_keyMap[XK_2] = Fw::Key2; + m_keyMap[XK_3] = Fw::Key3; + m_keyMap[XK_4] = Fw::Key4; + m_keyMap[XK_5] = Fw::Key5; + m_keyMap[XK_6] = Fw::Key6; + m_keyMap[XK_7] = Fw::Key7; + m_keyMap[XK_8] = Fw::Key8; + m_keyMap[XK_9] = Fw::Key9; + m_keyMap[XK_0] = Fw::Key0; + m_keyMap[XK_colon] = Fw::KeyColon; + m_keyMap[XK_semicolon] = Fw::KeySemicolon; + m_keyMap[XK_less] = Fw::KeyLess; + m_keyMap[XK_equal] = Fw::KeyEqual; + m_keyMap[XK_greater] = Fw::KeyGreater; + m_keyMap[XK_question] = Fw::KeyQuestion; + m_keyMap[XK_at] = Fw::KeyAtSign; + m_keyMap[XK_a] = Fw::KeyA; + m_keyMap[XK_b] = Fw::KeyB; + m_keyMap[XK_c] = Fw::KeyC; + m_keyMap[XK_d] = Fw::KeyD; + m_keyMap[XK_e] = Fw::KeyE; + m_keyMap[XK_f] = Fw::KeyF; + m_keyMap[XK_g] = Fw::KeyG; + m_keyMap[XK_h] = Fw::KeyH; + m_keyMap[XK_i] = Fw::KeyI; + m_keyMap[XK_j] = Fw::KeyJ; + m_keyMap[XK_k] = Fw::KeyK; + m_keyMap[XK_l] = Fw::KeyL; + m_keyMap[XK_m] = Fw::KeyM; + m_keyMap[XK_n] = Fw::KeyN; + m_keyMap[XK_o] = Fw::KeyO; + m_keyMap[XK_p] = Fw::KeyP; + m_keyMap[XK_q] = Fw::KeyQ; + m_keyMap[XK_r] = Fw::KeyR; + m_keyMap[XK_s] = Fw::KeyS; + m_keyMap[XK_t] = Fw::KeyT; + m_keyMap[XK_u] = Fw::KeyU; + m_keyMap[XK_v] = Fw::KeyV; + m_keyMap[XK_w] = Fw::KeyW; + m_keyMap[XK_x] = Fw::KeyX; + m_keyMap[XK_y] = Fw::KeyY; + m_keyMap[XK_z] = Fw::KeyZ; + m_keyMap[XK_bracketleft] = Fw::KeyLeftBracket; + m_keyMap[XK_backslash] = Fw::KeyBackslash; + m_keyMap[XK_bracketright] = Fw::KeyRightBracket; + m_keyMap[XK_asciicircum] = Fw::KeyCaret; + m_keyMap[XK_underscore] = Fw::KeyUnderscore; + m_keyMap[XK_grave] = Fw::KeyGrave; + m_keyMap[XK_braceleft] = Fw::KeyLeftCurly; + m_keyMap[XK_bar] = Fw::KeyBar; + m_keyMap[XK_braceright] = Fw::KeyRightCurly; + m_keyMap[XK_asciitilde] = Fw::KeyTilde; + + // keypad + m_keyMap[XK_KP_Add] = Fw::KeyPlus; + m_keyMap[XK_KP_Subtract] = Fw::KeyMinus; + m_keyMap[XK_KP_Decimal] = Fw::KeyPeriod; + m_keyMap[XK_KP_Divide] = Fw::KeySlash; + m_keyMap[XK_KP_Multiply] = Fw::KeyAsterisk; + m_keyMap[XK_KP_Enter] = Fw::KeyEnter; + + // keypad with numlock off + m_keyMap[XK_KP_Insert] = Fw::KeyNumpad0; + m_keyMap[XK_KP_End] = Fw::KeyNumpad1; + m_keyMap[XK_KP_Down] = Fw::KeyNumpad2; + m_keyMap[XK_KP_Page_Down] = Fw::KeyNumpad3; + m_keyMap[XK_KP_Left] = Fw::KeyNumpad4; + m_keyMap[XK_KP_Begin] = Fw::KeyNumpad5; + m_keyMap[XK_KP_Right] = Fw::KeyNumpad6; + m_keyMap[XK_KP_Home] = Fw::KeyNumpad7; + m_keyMap[XK_KP_Up] = Fw::KeyNumpad8; + m_keyMap[XK_KP_Page_Up] = Fw::KeyNumpad9; + m_keyMap[XK_KP_Delete] = Fw::KeyDelete; + + // keypad with numlock on + m_keyMap[XK_KP_0] = Fw::Key0; + m_keyMap[XK_KP_1] = Fw::Key1; + m_keyMap[XK_KP_2] = Fw::Key2; + m_keyMap[XK_KP_3] = Fw::Key3; + m_keyMap[XK_KP_4] = Fw::Key4; + m_keyMap[XK_KP_5] = Fw::Key5; + m_keyMap[XK_KP_6] = Fw::Key6; + m_keyMap[XK_KP_7] = Fw::Key7; + m_keyMap[XK_KP_8] = Fw::Key8; + m_keyMap[XK_KP_9] = Fw::Key9; + + m_keyMap[XK_F1] = Fw::KeyF1; + m_keyMap[XK_F2] = Fw::KeyF2; + m_keyMap[XK_F3] = Fw::KeyF3; + m_keyMap[XK_F4] = Fw::KeyF4; + m_keyMap[XK_F5] = Fw::KeyF5; + m_keyMap[XK_F6] = Fw::KeyF6; + m_keyMap[XK_F7] = Fw::KeyF7; + m_keyMap[XK_F8] = Fw::KeyF8; + m_keyMap[XK_F9] = Fw::KeyF9; + m_keyMap[XK_F10] = Fw::KeyF10; + m_keyMap[XK_F11] = Fw::KeyF11; + m_keyMap[XK_F12] = Fw::KeyF12; +} + +void X11Window::init() +{ + m_size = Size(200, 200); + internalOpenDisplay(); + internalCheckGL(); + internalChooseGLVisual(); + internalCreateGLContext(); + internalCreateWindow(); +} + +void X11Window::terminate() +{ + XDestroyWindow(m_display, m_window); + + if(m_colormap) + XFreeColormap(m_display, m_colormap); + + internalDestroyGLContext(); + + if(m_visual) + XFree(m_visual); + + if(m_xic) + XDestroyIC(m_xic); + + if(m_xim) + XCloseIM(m_xim); + + XCloseDisplay(m_display); + + m_visible = false; +} + +void X11Window::internalOpenDisplay() +{ + m_display = XOpenDisplay(NULL); + if(!m_display) + logFatal("Unable to open X11 display"); + m_screen = DefaultScreen(m_display); +} + +void X11Window::internalCreateWindow() +{ + Visual *vis; + int depth; + unsigned int attrsMask = CWEventMask; + XSetWindowAttributes attrs = {0}; + + attrs.event_mask = KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | PointerMotionMask | + ExposureMask | VisibilityChangeMask | + StructureNotifyMask | FocusChangeMask; + + if(m_visual) { + m_colormap = XCreateColormap(m_display, m_rootWindow, m_visual->visual, AllocNone); + attrs.colormap = m_colormap; + attrs.border_pixel = 0; + attrsMask |= CWBorderPixel | CWColormap; + vis = m_visual->visual; + depth = m_visual->depth; + } else { + attrs.override_redirect = False; + attrsMask |= CWOverrideRedirect; + depth = CopyFromParent; + vis = CopyFromParent; + } + + m_window = XCreateWindow(m_display, m_rootWindow, + m_pos.x, m_pos.y, m_size.width(), m_size.height(), + 0, + depth, + InputOutput, + vis, + attrsMask, &attrs); + if(!m_window) + logFatal("Unable to create X11 window!"); + + // ensure window input focus + XWMHints hints; + hints.input = True; + hints.flags = InputHint; + XSetWMHints(m_display, m_window, &hints); + + // ensure window position + XMoveWindow(m_display, m_window, m_pos.x, m_pos.y); + + // handle wm_delete events + m_wmDelete = XInternAtom(m_display, "WM_DELETE_WINDOW", True); + XSetWMProtocols(m_display, m_window, &m_wmDelete , 1); + + if(!internalSetupWindowInput()) + logWarning("Input of special keys may be messed up, because window input initialization failed"); + + internalConnectGLContext(); +} + +bool X11Window::internalSetupWindowInput() +{ + // try to set a latin1 locales, otherwise fallback to standard C locale + static char locales[4][32] = { "en_US.iso88591", "iso88591", "en_US", "C" }; + for(int i=0;i<4;++i) { + if(setlocale(LC_ALL, locales[i])) + break; + } + + // create input context (to have better key input handling) + if(!XSupportsLocale()) { + logError("X11 doesn't support the current locale"); + return false; + } + + XSetLocaleModifiers(""); + m_xim = XOpenIM(m_display, NULL, NULL, NULL); + if(!m_xim) { + logError("XOpenIM failed"); + return false; + } + + m_xic = XCreateIC(m_xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, m_window, NULL); + if(!m_xic) { + logError("Unable to create the input context"); + return false; + } + + return true; +} + +void X11Window::internalCheckGL() +{ +#ifndef OPENGL_ES2 + if(!glXQueryExtension(m_display, NULL, NULL)) + logFatal("GLX not supported"); +#else + m_eglDisplay = eglGetDisplay((EGLNativeDisplayType)m_display); + if(m_eglDisplay == EGL_NO_DISPLAY) + logFatal("EGL not supported"); + + if(!eglInitialize(m_eglDisplay, NULL, NULL)) + logFatal("Unable to initialize EGL"); +#endif +} + +void X11Window::internalChooseGLVisual() +{ +#ifndef OPENGL_ES2 + static int attrList[] = { + GLX_USE_GL, + GLX_RGBA, + GLX_DOUBLEBUFFER, + None + }; + + m_visual = glXChooseVisual(m_display, m_screen, attrList); + if(!m_visual) + logFatal("Couldn't choose RGBA, double buffered visual"); + + m_rootWindow = RootWindow(m_display, m_visual->screen); +#else + static int attrList[] = { + EGL_BUFFER_SIZE, 16, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + EGLint numConfig; + if(!eglChooseConfig(m_eglDisplay, attrList, &m_eglConfig, 1, &numConfig)) + logFatal("Failed to choose EGL config"); + + if(numConfig != 1) + logWarning("Didn't got the exact EGL config"); + + m_rootWindow = DefaultRootWindow(m_display); +#endif +} + +void X11Window::internalCreateGLContext() +{ +#ifndef OPENGL_ES2 + m_glxContext = glXCreateContext(m_display, m_visual, NULL, True); + if(!m_glxContext) + logFatal("Unable to create GLX context"); + + if(!glXIsDirect(m_display, m_glxContext)) + logWarning("GL direct rendering is not possible"); +#else + EGLint attrList[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, attrList); + if(m_eglContext == EGL_NO_CONTEXT ) + logFatal("Unable to create EGL context: ", eglGetError()); +#endif +} + +void X11Window::internalDestroyGLContext() +{ +#ifndef OPENGL_ES2 + glXMakeCurrent(m_display, None, NULL); + glXDestroyContext(m_display, m_glxContext); +#else + eglDestroyContext(m_eglDisplay, m_eglContext); + eglDestroySurface(m_eglDisplay, m_eglSurface); + eglTerminate(m_eglDisplay); +#endif +} + +void X11Window::internalConnectGLContext() +{ +#ifndef OPENGL_ES2 + if(!glXMakeCurrent(m_display, m_window, m_glxContext)) + logFatal("Unable to set GLX context on X11 window"); +#else + m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, m_window, NULL); + if(m_eglSurface == EGL_NO_SURFACE) + logFatal("Unable to create EGL surface: ", eglGetError()); + if(!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext)) + logFatal("Unable to connect EGL context into X11 window"); +#endif +} + +void *X11Window::getExtensionProcAddress(const char *ext) +{ +#ifndef OPENGL_ES2 + return (void *)glXGetProcAddressARB((const GLubyte*)ext); +#else + //TODO + return false; +#endif +} + +bool X11Window::isExtensionSupported(const char *ext) +{ +#ifndef OPENGL_ES2 + const char *exts = glXQueryExtensionsString(m_display, m_screen); + if(strstr(exts, ext)) + return true; +#else + //TODO +#endif + return false; +} + +void X11Window::move(const Point& pos) +{ + XMoveWindow(m_display, m_window, pos.x, pos.y); +} + +void X11Window::resize(const Size& size) +{ + XResizeWindow(m_display, m_window, size.width(), size.height()); +} + +void X11Window::show() +{ + XMapWindow(m_display, m_window); +} + +void X11Window::hide() +{ + XUnmapWindow(m_display, m_window); +} + +void X11Window::maximize() +{ + Atom wmState = XInternAtom(m_display, "_NET_WM_STATE", False); + Atom wmStateMaximizedVert = XInternAtom(m_display, "_NET_WM_STATE_MAXIMIZED_VERT", False); + Atom wmStateMaximizedHorz = XInternAtom(m_display, "_NET_WM_STATE_MAXIMIZED_HORZ", False); + + XEvent e = {0}; + e.xany.type = ClientMessage; + e.xclient.send_event = True; + e.xclient.message_type = wmState; + e.xclient.format = 32; + e.xclient.window = m_window; + e.xclient.data.l[0] = 1; + e.xclient.data.l[1] = wmStateMaximizedVert; + e.xclient.data.l[2] = wmStateMaximizedHorz; + e.xclient.data.l[3] = 0; + + XSendEvent(m_display, m_rootWindow, 0, SubstructureNotifyMask | SubstructureRedirectMask, &e); + XFlush(m_display); +} + +void X11Window::poll() +{ + bool needsResizeUpdate = false; + + XEvent event, peekEvent; + while(XPending(m_display) > 0) { + XNextEvent(m_display, &event); + + // call filter because xim will discard KeyPress events when keys still composing + if(XFilterEvent(&event, m_window)) + continue; + + // discard events of repeated key releases + if(event.type == KeyRelease && XPending(m_display)) { + XPeekEvent(m_display, &peekEvent); + if((peekEvent.type == KeyPress) && + (peekEvent.xkey.keycode == event.xkey.keycode) && + ((peekEvent.xkey.time-event.xkey.time) < 2)) + continue; + } + + // reset inputEvent values, except keyboardModifiers and mousePos + m_inputEvent.type = Fw::NoInputEvent; + m_inputEvent.mouseButton = Fw::MouseNoButton; + m_inputEvent.keyCode = Fw::KeyUnknown; + m_inputEvent.keyText = ""; + m_inputEvent.mouseMoved = Point(); + m_inputEvent.wheelDirection = Fw::MouseNoWheel; + + switch(event.type) { + case ClientMessage: { + // close event + if((Atom)event.xclient.data.l[0] == m_wmDelete && m_onClose) + m_onClose(); + break; + } + case ConfigureNotify: { + Size newSize(event.xconfigure.width, event.xconfigure.height); + Point newPos(event.xconfigure.x, event.xconfigure.y); + + // updates window size + if(m_size != newSize) { + m_size = newSize; + needsResizeUpdate = true; + } + + // updates window pos + if(m_pos != newPos && !isMaximized()) + m_pos = newPos; + break; + } + case SelectionRequest: { + XEvent respond; + XSelectionRequestEvent *req = &(event.xselectionrequest); + + Atom targets = XInternAtom(m_display, "TARGETS", False); + if(req->target == targets) { + Atom typeList[] = { XInternAtom(m_display, "UTF8_STRING", False), + XInternAtom(m_display, "TEXT", False), + XInternAtom(m_display, "COMPOUND_TEXT", False), + XA_STRING }; + + XChangeProperty(m_display, req->requestor, + req->property, req->target, + 8, PropModeReplace, + (uchar *)&typeList, + sizeof(typeList)); + respond.xselection.property = req->property; + } else { + XChangeProperty(m_display, + req->requestor, + req->property, req->target, + 8, + PropModeReplace, + (uchar *)m_clipboardText.c_str(), + m_clipboardText.length()); + respond.xselection.property = req->property; + } + + respond.xselection.type = SelectionNotify; + respond.xselection.display = req->display; + respond.xselection.requestor = req->requestor; + respond.xselection.selection = req->selection; + respond.xselection.target = req->target; + respond.xselection.time = req->time; + XSendEvent(m_display, req->requestor, 0, 0, &respond); + XFlush(m_display); + break; + } + case KeyPress: + case KeyRelease: { + KeySym keysym; + char buf[32]; + int len; + + m_inputEvent.keyboardModifiers = 0; + if(event.xkey.state & ControlMask) + m_inputEvent.keyboardModifiers |= Fw::KeyboardCtrlModifier; + if(event.xkey.state & ShiftMask) + m_inputEvent.keyboardModifiers |= Fw::KeyboardShiftModifier; + if(event.xkey.state & Mod1Mask) + m_inputEvent.keyboardModifiers |= Fw::KeyboardAltModifier; + + // lookup for keyText + if(event.type == KeyPress && !(event.xkey.state & ControlMask) && !(event.xkey.state & Mod1Mask)) { + if(m_xic) { // with xim we can get latin1 input correctly + Status status; + len = XmbLookupString(m_xic, &event.xkey, buf, sizeof(buf), &keysym, &status); + } else { // otherwise use XLookupString, but often it doesn't work right with dead keys + static XComposeStatus compose = {NULL, 0}; + len = XLookupString(&event.xkey, buf, sizeof(buf), &keysym, &compose); + } + + if(len > 0 && + // these keys produces characters that we don't want to capture + keysym != XK_BackSpace && + keysym != XK_Return && + keysym != XK_Delete && + keysym != XK_Escape && + (uchar)(buf[0]) >= 32 + ) { + //logDebug("char: ", buf[0], " code: ", (uint)buf[0]); + m_inputEvent.keyText = buf; + } + } + + if(m_keyMap.find(keysym) != m_keyMap.end()) + m_inputEvent.keyCode = m_keyMap[keysym]; + + m_inputEvent.type = (event.type == KeyPress) ? Fw::KeyPressInputEvent : Fw::KeyReleaseInputEvent; + if(m_inputEvent.keyCode != Fw::KeyUnknown || !m_inputEvent.keyText.empty()) + m_onInputEvent(m_inputEvent); + break; + } + case ButtonPress: + case ButtonRelease: { + m_inputEvent.type = (event.type == ButtonPress) ? Fw::MousePressInputEvent : Fw::MouseReleaseInputEvent; + switch(event.xbutton.button) { + case Button1: + m_inputEvent.mouseButton = Fw::MouseLeftButton; + break; + case Button3: + m_inputEvent.mouseButton = Fw::MouseRightButton; + break; + case Button2: + m_inputEvent.mouseButton = Fw::MouseMidButton; + break; + case Button4: + m_inputEvent.type = Fw::MouseWheelInputEvent; + m_inputEvent.mouseButton = Fw::MouseMidButton; + m_inputEvent.wheelDirection = Fw::MouseWheelUp; + break; + case Button5: + m_inputEvent.type = Fw::MouseWheelInputEvent; + m_inputEvent.mouseButton = Fw::MouseMidButton; + m_inputEvent.wheelDirection = Fw::MouseWheelDown; + break; + default: + m_inputEvent.type = Fw::NoInputEvent; + break; + } + if(m_inputEvent.type != Fw::NoInputEvent && m_onInputEvent) + m_onInputEvent(m_inputEvent); + break; + } + + case MotionNotify: { + m_inputEvent.type = Fw::MouseMoveInputEvent; + Point newMousePos(event.xbutton.x, event.xbutton.y); + m_inputEvent.mouseMoved = newMousePos - m_inputEvent.mousePos; + m_inputEvent.mousePos = newMousePos; + if(m_onInputEvent) + m_onInputEvent(m_inputEvent); + break; + } + case MapNotify: + m_visible = true; + break; + case UnmapNotify: + m_visible = false; + break; + case FocusIn: + m_focused = true; + break; + case FocusOut: + m_focused = false; + break; + case Expose: + // window needs redraw + break; + } + } + + if(needsResizeUpdate && m_onResize) + m_onResize(m_size); +} + +void X11Window::swapBuffers() +{ +#ifndef OPENGL_ES2 + glXSwapBuffers(m_display, m_window); +#else + eglSwapBuffers(m_eglDisplay, m_eglSurface); +#endif +} + +void X11Window::showMouse() +{ + XUndefineCursor(m_display, m_window); + if(m_cursor != None) { + XFreeCursor(m_display, m_cursor); + m_cursor = None; + } +} + +void X11Window::hideMouse() +{ + if(m_cursor == None) { + char bm[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + Pixmap pix = XCreateBitmapFromData(m_display, m_window, bm, 8, 8); + XColor black = {0}; + black.flags = DoRed | DoGreen | DoBlue; + m_cursor = XCreatePixmapCursor(m_display, pix, pix, &black, &black, 0, 0); + XFreePixmap(m_display, pix); + } + XDefineCursor(m_display, m_window, m_cursor); +} + +void X11Window::setTitle(const std::string& title) +{ + XStoreName(m_display, m_window, title.c_str()); + XSetIconName(m_display, m_window, title.c_str()); +} + +void X11Window::setMinimumSize(const Size& minimumSize) +{ + XSizeHints sizeHints = {0}; + sizeHints.flags = PMinSize; + sizeHints.min_width = minimumSize.width(); + sizeHints.min_height= minimumSize.height(); + XSetWMSizeHints(m_display, m_window, &sizeHints, XA_WM_NORMAL_HINTS); +} + +void X11Window::setFullscreen(bool fullscreen) +{ + if(m_fullscreen != fullscreen) { + Atom wmState = XInternAtom(m_display, "_NET_WM_STATE", False); + Atom wmStateFullscreen = XInternAtom(m_display, "_NET_WM_STATE_FULLSCREEN", False); + + XEvent xev; + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = m_window; + xev.xclient.message_type = wmState; + xev.xclient.format = 32; + xev.xclient.data.l[0] = fullscreen ? 1 : 0; + xev.xclient.data.l[1] = wmStateFullscreen; + xev.xclient.data.l[2] = 0; + + XSendEvent(m_display, m_rootWindow, False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + XFlush(m_display); + + m_fullscreen = fullscreen; + } +} + +void X11Window::setVerticalSync(bool enable) +{ +#ifndef OPENGL_ES2 + typedef GLint (*glSwapIntervalProc)(GLint); + glSwapIntervalProc glSwapInterval = NULL; + + if(isExtensionSupported("GLX_MESA_swap_control")) + glSwapInterval = (glSwapIntervalProc)getExtensionProcAddress("glXSwapIntervalMESA"); + else if(isExtensionSupported("GLX_SGI_swap_control")) + glSwapInterval = (glSwapIntervalProc)getExtensionProcAddress("glXSwapIntervalSGI"); + + if(glSwapInterval) + glSwapInterval(enable ? 1 : 0); +#else + //TODO +#endif +} + +void X11Window::setIcon(const std::string& iconFile) +{ + std::stringstream fin; + g_resources.loadFile(iconFile, fin); + + apng_data apng; + if(load_apng(fin, &apng) != 0) { + logError("Unable to load window icon"); + return; + } + + if(apng.bpp != 4) { + logError("Could not set window icon, icon image must have 4 channels"); + free_apng(&apng); + return; + } + + int n = apng.width * apng.height; + std::vector iconData(n + 2); + iconData[0] = apng.width; + iconData[1] = apng.height; + for(int i=0; i < n;++i) { + uint8 *pixel = (uint8*)&iconData[2 + i]; + pixel[2] = *(apng.pdata + (i * 4) + 0); + pixel[1] = *(apng.pdata + (i * 4) + 1); + pixel[0] = *(apng.pdata + (i * 4) + 2); + pixel[3] = *(apng.pdata + (i * 4) + 3); + } + + Atom property = XInternAtom(m_display, "_NET_WM_ICON", 0); + if(!XChangeProperty(m_display, m_window, property, XA_CARDINAL, 32, PropModeReplace, (const unsigned char*)&iconData[0], iconData.size())) + logError("Couldn't set app icon"); + + free_apng(&apng); +} + +void X11Window::setClipboardText(const std::string& text) +{ + m_clipboardText = text; + Atom clipboard = XInternAtom(m_display, "CLIPBOARD", False); + XSetSelectionOwner(m_display, clipboard, m_window, CurrentTime); + XFlush(m_display); +} + +Size X11Window::getDisplaySize() +{ + return Size(XDisplayWidth(m_display, m_screen), XDisplayHeight(m_display, m_screen)); +} + +std::string X11Window::getClipboardText() +{ + Atom clipboard = XInternAtom(m_display, "CLIPBOARD", False); + Window ownerWindow = XGetSelectionOwner(m_display, clipboard); + if(ownerWindow == m_window) + return m_clipboardText; + + std::string clipboardText; + if(ownerWindow != None) { + XConvertSelection(m_display, clipboard, XA_STRING, 0, ownerWindow, CurrentTime); + XFlush(m_display); + + // hack to wait SelectioNotify event, otherwise we will get wrong clipboard pastes + // TODO: fix this in a correct way + usleep(100 * 1000); + + // check for data + Atom type; + int format; + ulong numItems, bytesLeft, dummy; + uchar *data; + XGetWindowProperty(m_display, ownerWindow, + XA_STRING, + 0, 0, 0, + AnyPropertyType, + &type, + &format, + &numItems, + &bytesLeft, + &data); + if(bytesLeft > 0) { + // get the data get + int result = XGetWindowProperty(m_display, ownerWindow, + XA_STRING, + 0, + bytesLeft, + 0, + AnyPropertyType, + &type, + &format, + &numItems, + &dummy, + &data); + if(result == Success) + clipboardText = (const char*)data; + XFree(data); + } + } + + return clipboardText; +} + +bool X11Window::isMaximized() +{ + Atom wmState = XInternAtom(m_display, "_NET_WM_STATE", False); + Atom wmStateMaximizedVert = XInternAtom(m_display, "_NET_WM_STATE_MAXIMIZED_VERT", False); + Atom wmStateMaximizedHorz = XInternAtom(m_display, "_NET_WM_STATE_MAXIMIZED_HORZ", False); + Atom actualType; + ulong i, numItems, bytesAfter; + uchar *propertyValue = NULL; + int actualFormat; + bool maximized = false; + + if(XGetWindowProperty(m_display, m_window, wmState, + 0, 1024, False, XA_ATOM, &actualType, + &actualFormat, &numItems, &bytesAfter, + &propertyValue) == Success) { + Atom *atoms = (Atom*)propertyValue; + int maximizedMask = 0; + + for(i=0; i + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef X11WINDOW_H +#define X11WINDOW_H + +#include "platformwindow.h" +#include + +#include +#include + +#ifndef OPENGL_ES2 +#include +#else +#include +#endif + +class X11Window : public PlatformWindow +{ + void internalOpenDisplay(); + void internalCreateWindow(); + bool internalSetupWindowInput(); + + void internalCheckGL(); + void internalChooseGLVisual(); + void internalCreateGLContext(); + void internalDestroyGLContext(); + void internalConnectGLContext(); + + void *getExtensionProcAddress(const char *ext); + bool isExtensionSupported(const char *ext); + +public: + X11Window(); + + void init(); + void terminate(); + + void move(const Point& pos); + void resize(const Size& size); + void show(); + void hide(); + void maximize(); + void poll(); + void swapBuffers(); + void showMouse(); + void hideMouse(); + + void setTitle(const std::string& title); + void setMinimumSize(const Size& minimumSize); + void setFullscreen(bool fullscreen); + void setVerticalSync(bool enable); + void setIcon(const std::string& iconFile); + void setClipboardText(const std::string& text); + + Size getDisplaySize(); + std::string getClipboardText(); + + bool isMaximized(); + +private: + Display *m_display; + XVisualInfo *m_visual; + Window m_window; + Window m_rootWindow; + Colormap m_colormap; + Cursor m_cursor; + XIM m_xim; + XIC m_xic; + int m_screen; + Atom m_wmDelete; + std::string m_clipboardText; + std::map m_keyMap; + +#ifndef OPENGL_ES2 + GLXContext m_glxContext; +#else + EGLConfig m_eglConfig; + EGLContext m_eglContext; + EGLDisplay m_eglDisplay; + EGLSurface m_eglSurface; +#endif +}; + +#endif + diff --git a/src/framework/ui/uiframecounter.cpp b/src/framework/ui/uiframecounter.cpp index 33bc404c..e7a674d6 100644 --- a/src/framework/ui/uiframecounter.cpp +++ b/src/framework/ui/uiframecounter.cpp @@ -40,13 +40,12 @@ void UIFrameCounter::render() { UIWidget::render(); - int now = g_clock.ticks(); - if(now - m_lastFrameTicks >= 1000) { + if(g_clock.ticksElapsed(m_lastFrameTicks) >= 1000) { m_fpsText = Fw::mkstr("FPS: ", m_frameCount); - m_lastFrameTicks = now; + m_lastFrameTicks = g_clock.ticks(); m_frameCount = 0; - } else - m_frameCount++; + } + m_frameCount++; m_font->renderText(m_fpsText, m_rect, m_align, Fw::white); } diff --git a/src/framework/ui/uiframecounter.h b/src/framework/ui/uiframecounter.h index daa908b2..5e13bee9 100644 --- a/src/framework/ui/uiframecounter.h +++ b/src/framework/ui/uiframecounter.h @@ -41,7 +41,7 @@ protected: private: Fw::AlignmentFlag m_align; int m_frameCount; - int m_lastFrameTicks; + ticks_t m_lastFrameTicks; std::string m_fpsText; }; diff --git a/src/framework/ui/uilineedit.cpp b/src/framework/ui/uilineedit.cpp index 7e8cf097..91df18e9 100644 --- a/src/framework/ui/uilineedit.cpp +++ b/src/framework/ui/uilineedit.cpp @@ -23,7 +23,7 @@ #include "uilineedit.h" #include #include -#include +#include #include #include @@ -56,8 +56,7 @@ void UILineEdit::render() assert(m_cursorPos <= textLength); // draw every 333ms const int delay = 333; - int ticks = g_clock.ticks(); - if(ticks - m_cursorTicks <= delay) { + if(g_clock.ticksElapsed(m_cursorTicks) <= delay) { Rect cursorRect; // when cursor is at 0 or is the first visible element if(m_cursorPos == 0 || m_cursorPos == m_startRenderPos) @@ -65,8 +64,8 @@ void UILineEdit::render() else cursorRect = Rect(m_glyphsCoords[m_cursorPos-1].right(), m_glyphsCoords[m_cursorPos-1].top(), 1, m_font->getGlyphHeight()); g_graphics.drawFilledRect(cursorRect); - } else if(ticks - m_cursorTicks >= 2*delay) { - m_cursorTicks = ticks; + } else if(g_clock.ticksElapsed(m_cursorTicks) >= 2*delay) { + m_cursorTicks = g_clock.ticks(); } } } @@ -407,7 +406,7 @@ void UILineEdit::onFocusChange(bool focused, Fw::FocusReason reason) } } -bool UILineEdit::onKeyPress(uchar keyCode, char keyChar, int keyboardModifiers) +bool UILineEdit::onKeyPress(uchar keyCode, std::string keyText, int keyboardModifiers) { if(keyCode == Fw::KeyDelete) // erase right character removeCharacter(true); @@ -422,14 +421,14 @@ bool UILineEdit::onKeyPress(uchar keyCode, char keyChar, int keyboardModifiers) else if(keyCode == Fw::KeyEnd) // move cursor to last character setCursorPos(m_text.length()); else if(keyCode == Fw::KeyV && keyboardModifiers == Fw::KeyboardCtrlModifier) - appendText(g_platform.getClipboardText()); + appendText(g_window.getClipboardText()); else if(keyCode == Fw::KeyTab) { if(!m_alwaysActive) { if(UIWidgetPtr parent = getParent()) parent->focusNextChild(Fw::TabFocusReason); } - } else if(keyChar != 0) - appendCharacter(keyChar); + } else if(!keyText.empty()) + appendText(keyText); else return false; diff --git a/src/framework/ui/uilineedit.h b/src/framework/ui/uilineedit.h index ef8a401c..7ee30ec7 100644 --- a/src/framework/ui/uilineedit.h +++ b/src/framework/ui/uilineedit.h @@ -56,7 +56,7 @@ protected: virtual void onStyleApply(const OTMLNodePtr& styleNode); virtual void onGeometryUpdate(const Rect& oldRect, const Rect& newRect); virtual void onFocusChange(bool focused, Fw::FocusReason reason); - virtual bool onKeyPress(uchar keyCode, char keyChar, int keyboardModifiers); + virtual bool onKeyPress(uchar keyCode, std::string keyText, int keyboardModifiers); virtual bool onMousePress(const Point& mousePos, Fw::MouseButton button); private: @@ -68,7 +68,7 @@ private: int m_cursorPos; Point m_startInternalPos; int m_startRenderPos; - int m_cursorTicks; + ticks_t m_cursorTicks; int m_textHorizontalMargin; bool m_textHidden; bool m_alwaysActive; diff --git a/src/framework/ui/uimanager.cpp b/src/framework/ui/uimanager.cpp index 2725a8fa..6a8b3ada 100644 --- a/src/framework/ui/uimanager.cpp +++ b/src/framework/ui/uimanager.cpp @@ -50,56 +50,32 @@ void UIManager::render() void UIManager::resize(const Size& size) { - if(m_rootWidget) - m_rootWidget->resize(g_graphics.getScreenSize()); + m_rootWidget->resize(g_graphics.getScreenSize()); } -void UIManager::inputEvent(const PlatformEvent& event) +void UIManager::inputEvent(const InputEvent& event) { - // translate input event to ui events - if(m_rootWidget) { - if(event.type & EventKeyboardAction) { - int keyboardModifiers = Fw::KeyboardNoModifier; - if(event.ctrl) - keyboardModifiers |= Fw::KeyboardCtrlModifier; - if(event.shift) - keyboardModifiers |= Fw::KeyboardShiftModifier; - if(event.alt) - keyboardModifiers |= Fw::KeyboardAltModifier; - - if(event.type == EventKeyDown) - m_rootWidget->onKeyPress(event.keycode, event.keychar, keyboardModifiers); - else - m_rootWidget->onKeyRelease(event.keycode, event.keychar, keyboardModifiers); - } else if(event.type & EventMouseAction) { - if(event.type == EventMouseMove) { - m_rootWidget->updateState(Fw::HoverState); - m_rootWidget->onMouseMove(event.mousePos, event.mouseMoved); - } - else if(event.type & EventMouseWheel) { - Fw::MouseWheelDirection dir = Fw::MouseNoWheel; - if(event.type & EventDown) - dir = Fw::MouseWheelDown; - else if(event.type & EventUp) - dir = Fw::MouseWheelUp; - - m_rootWidget->onMouseWheel(event.mousePos, dir); - } else { - Fw::MouseButton button = Fw::MouseNoButton; - if(event.type & EventMouseLeftButton) - button = Fw::MouseLeftButton; - else if(event.type & EventMouseMidButton) - button = Fw::MouseMidButton; - else if(event.type & EventMouseRightButton) - button = Fw::MouseRightButton; - - if(event.type & EventDown) - m_rootWidget->onMousePress(event.mousePos, button); - else if(event.type & EventUp) - m_rootWidget->onMouseRelease(event.mousePos, button); - } - } - } + switch(event.type) { + case Fw::KeyPressInputEvent: + m_rootWidget->onKeyPress(event.keyCode, event.keyText, event.keyboardModifiers); + break; + case Fw::KeyReleaseInputEvent: + m_rootWidget->onKeyRelease(event.keyCode, event.keyText, event.keyboardModifiers); + break; + case Fw::MousePressInputEvent: + m_rootWidget->onMousePress(event.mousePos, event.mouseButton); + break; + case Fw::MouseReleaseInputEvent: + m_rootWidget->onMouseRelease(event.mousePos, event.mouseButton); + break; + case Fw::MouseMoveInputEvent: + m_rootWidget->updateState(Fw::HoverState); + m_rootWidget->onMouseMove(event.mousePos, event.mouseMoved); + break; + case Fw::MouseWheelInputEvent: + m_rootWidget->onMouseWheel(event.mousePos, event.wheelDirection); + break; + }; } bool UIManager::importStyles(const std::string& file) diff --git a/src/framework/ui/uimanager.h b/src/framework/ui/uimanager.h index c9992594..16bde639 100644 --- a/src/framework/ui/uimanager.h +++ b/src/framework/ui/uimanager.h @@ -24,7 +24,7 @@ #define UIMANAGER_H #include "declarations.h" -#include +#include #include class UIManager @@ -35,7 +35,7 @@ public: void render(); void resize(const Size& size); - void inputEvent(const PlatformEvent& event); + void inputEvent(const InputEvent& event); bool importStyles(const std::string& file); void importStyleFromOTML(const OTMLNodePtr& styleNode); diff --git a/src/framework/ui/uiwidget.cpp b/src/framework/ui/uiwidget.cpp index 2ed4f8c1..53dfe14d 100644 --- a/src/framework/ui/uiwidget.cpp +++ b/src/framework/ui/uiwidget.cpp @@ -32,7 +32,7 @@ #include #include #include -#include +#include UIWidget::UIWidget() { @@ -670,7 +670,7 @@ void UIWidget::updateState(Fw::WidgetState state) } else if(state == Fw::HoverState) { updateChildren = true; - Point mousePos = g_platform.getMouseCursorPos(); + Point mousePos = g_window.getMousePos(); UIWidgetPtr widget = asUIWidget(); UIWidgetPtr parent; do { @@ -971,9 +971,9 @@ void UIWidget::onHoverChange(bool hovered) g_ui.getRootWidget()->updateState(Fw::HoverState); } -bool UIWidget::onKeyPress(uchar keyCode, char keyChar, int keyboardModifiers) +bool UIWidget::onKeyPress(uchar keyCode, std::string keyText, int keyboardModifiers) { - if(callLuaField("onKeyPress", keyCode, keyChar, keyboardModifiers)) + if(callLuaField("onKeyPress", keyCode, keyText, keyboardModifiers)) return true; // do a backup of children list, because it may change while looping it @@ -989,16 +989,16 @@ bool UIWidget::onKeyPress(uchar keyCode, char keyChar, int keyboardModifiers) } for(const UIWidgetPtr& child : children) { - if(child->onKeyPress(keyCode, keyChar, keyboardModifiers)) + if(child->onKeyPress(keyCode, keyText, keyboardModifiers)) return true; } return false; } -bool UIWidget::onKeyRelease(uchar keyCode, char keyChar, int keyboardModifiers) +bool UIWidget::onKeyRelease(uchar keyCode, std::string keyText, int keyboardModifiers) { - if(callLuaField("onKeyRelease", keyCode, keyChar, keyboardModifiers)) + if(callLuaField("onKeyRelease", keyCode, keyText, keyboardModifiers)) return true; // do a backup of children list, because it may change while looping it @@ -1014,7 +1014,7 @@ bool UIWidget::onKeyRelease(uchar keyCode, char keyChar, int keyboardModifiers) } for(const UIWidgetPtr& child : children) { - if(child->onKeyRelease(keyCode, keyChar, keyboardModifiers)) + if(child->onKeyRelease(keyCode, keyText, keyboardModifiers)) return true; } diff --git a/src/framework/ui/uiwidget.h b/src/framework/ui/uiwidget.h index 96a6673f..c7e860bf 100644 --- a/src/framework/ui/uiwidget.h +++ b/src/framework/ui/uiwidget.h @@ -166,9 +166,9 @@ protected: /// Triggered when the mouse enters or leaves widget area virtual void onHoverChange(bool hovered); /// Triggered when user presses key while widget has focus - virtual bool onKeyPress(uchar keyCode, char keyChar, int keyboardModifiers); + virtual bool onKeyPress(uchar keyCode, std::string keyText, int keyboardModifiers); /// Triggered when user releases key while widget has focus - virtual bool onKeyRelease(uchar keyCode, char keyChar, int keyboardModifiers); + virtual bool onKeyRelease(uchar keyCode, std::string keyText, int keyboardModifiers); /// Triggered when a mouse button is pressed down while mouse pointer is inside widget area virtual bool onMousePress(const Point& mousePos, Fw::MouseButton button); /// Triggered when a mouse button is released diff --git a/src/framework/ui/uiwindow.cpp b/src/framework/ui/uiwindow.cpp index f1adb97d..e0a1ae57 100644 --- a/src/framework/ui/uiwindow.cpp +++ b/src/framework/ui/uiwindow.cpp @@ -146,7 +146,7 @@ bool UIWindow::onMouseMove(const Point& mousePos, const Point& mouseMoved) return UIWidget::onMouseMove(mousePos, mouseMoved); } -bool UIWindow::onKeyPress(uchar keyCode, char keyChar, int keyboardModifiers) +bool UIWindow::onKeyPress(uchar keyCode, std::string keyText, int keyboardModifiers) { if(keyboardModifiers == Fw::KeyboardNoModifier) { if(keyCode == Fw::KeyReturn || keyCode == Fw::KeyEnter) { @@ -157,5 +157,5 @@ bool UIWindow::onKeyPress(uchar keyCode, char keyChar, int keyboardModifiers) return true; } } - return UIWidget::onKeyPress(keyCode, keyChar, keyboardModifiers); + return UIWidget::onKeyPress(keyCode, keyText, keyboardModifiers); } diff --git a/src/framework/ui/uiwindow.h b/src/framework/ui/uiwindow.h index a6f48651..81d1259f 100644 --- a/src/framework/ui/uiwindow.h +++ b/src/framework/ui/uiwindow.h @@ -46,7 +46,7 @@ protected: virtual bool onMousePress(const Point& mousePos, Fw::MouseButton button); virtual void onMouseRelease(const Point& mousePos, Fw::MouseButton button); virtual bool onMouseMove(const Point& mousePos, const Point& mouseMoved); - virtual bool onKeyPress(uchar keyCode, char keyChar, int keyboardModifiers); + virtual bool onKeyPress(uchar keyCode, std::string keyText, int keyboardModifiers); private: std::string m_title; diff --git a/src/framework/util/types.h b/src/framework/util/types.h index b4a91c13..4dcc392c 100644 --- a/src/framework/util/types.h +++ b/src/framework/util/types.h @@ -35,10 +35,13 @@ typedef uint64_t uint64; typedef uint32_t uint32; typedef uint16_t uint16; typedef uint8_t uint8; +typedef int64_t int64; typedef int32_t int32; typedef int16_t int16; typedef int8_t int8; +typedef int64 ticks_t; + typedef std::function SimpleCallback; typedef std::function BooleanCallback; diff --git a/src/main.cpp b/src/main.cpp index dbb538a5..6fa287e2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,39 +21,12 @@ */ #include -#include -#include - -void signal_handler(int sig) -{ - static bool signaled = false; - switch(sig) { - case SIGTERM: - case SIGINT: - if(!signaled) { - signaled = true; - g_dispatcher.addEvent(std::bind(&OTClient::onClose, &g_client)); - } - break; - } -} - -#ifdef WIN32_NO_CONSOLE -#include -int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow) -{ - std::vector args; - boost::split(args, lpszArgument, boost::is_any_of(std::string(" "))); -#else int main(int argc, const char* argv[]) { - std::vector args; - for(int i=0; i args(argv, argv + argc); + g_otclient.init(args); + g_otclient.run(); + g_otclient.terminate(); return 0; } diff --git a/src/otclient/const.h b/src/otclient/const.h index 3649f558..23b7feb2 100644 --- a/src/otclient/const.h +++ b/src/otclient/const.h @@ -28,7 +28,7 @@ namespace Otc { static const char* AppName = "OTClient"; - static const char* AppPathName = "otclient"; + static const char* AppIdentifierName = "otclient"; static const char* AppVersion = "0.4.0"; static const char* CipsoftPublicRSA = "1321277432058722840622950990822933849527763264961655079678763618" diff --git a/src/otclient/core/creature.cpp b/src/otclient/core/creature.cpp index 18a95062..35f11bd2 100644 --- a/src/otclient/core/creature.cpp +++ b/src/otclient/core/creature.cpp @@ -207,7 +207,7 @@ void Creature::walk(const Position& position, bool inverse) void Creature::updateWalk() { if(m_walking) { - int elapsedTicks = g_clock.ticks() - m_walkStartTicks; + int elapsedTicks = g_clock.ticksElapsed(m_walkStartTicks); int totalPixelsWalked = std::min((int)round(elapsedTicks / m_walkTimePerPixel), 32); if(m_inverseWalking) { diff --git a/src/otclient/core/creature.h b/src/otclient/core/creature.h index d3a7afdb..96899250 100644 --- a/src/otclient/core/creature.h +++ b/src/otclient/core/creature.h @@ -88,7 +88,7 @@ protected: FontPtr m_informationFont; Color m_informationColor; - int m_walkStartTicks; + ticks_t m_walkStartTicks; bool m_walking, m_inverseWalking; float m_walkTimePerPixel; Position m_walkingFromPosition; diff --git a/src/otclient/core/effect.h b/src/otclient/core/effect.h index 4c8b3176..b385790c 100644 --- a/src/otclient/core/effect.h +++ b/src/otclient/core/effect.h @@ -45,7 +45,7 @@ public: EffectPtr asEffect() { return std::static_pointer_cast(shared_from_this()); } private: - int m_animationStartTicks; + ticks_t m_animationStartTicks; }; #endif diff --git a/src/otclient/core/game.cpp b/src/otclient/core/game.cpp index be9cc9b9..c9286a40 100644 --- a/src/otclient/core/game.cpp +++ b/src/otclient/core/game.cpp @@ -27,17 +27,6 @@ Game g_game; -void Game::init() -{ - m_online = false; -} - -void Game::terminate() -{ - if(m_online) - logout(true); -} - void Game::loginWorld(const std::string& account, const std::string& password, const std::string& worldHost, int worldPort, const std::string& characterName) { m_protocolGame = ProtocolGamePtr(new ProtocolGame); @@ -51,12 +40,13 @@ void Game::cancelLogin() void Game::logout(bool force) { - if(m_protocolGame) { - m_protocolGame->sendLogout(); + if(!m_protocolGame || !m_online) + return; - if(force) - processLogout(); - } + m_protocolGame->sendLogout(); + + if(force) + processLogout(); } void Game::processLoginError(const std::string& error) diff --git a/src/otclient/core/game.h b/src/otclient/core/game.h index 740d72d2..73de4ab7 100644 --- a/src/otclient/core/game.h +++ b/src/otclient/core/game.h @@ -31,9 +31,6 @@ class Game { public: - void init(); - void terminate(); - // login/logout related void loginWorld(const std::string& account, const std::string& password, diff --git a/src/otclient/core/item.h b/src/otclient/core/item.h index eb690a6f..05f97634 100644 --- a/src/otclient/core/item.h +++ b/src/otclient/core/item.h @@ -47,7 +47,7 @@ public: private: int m_data; - int m_lastTicks; + ticks_t m_lastTicks; }; #endif diff --git a/src/otclient/core/missile.h b/src/otclient/core/missile.h index 680ce4cd..07cadf68 100644 --- a/src/otclient/core/missile.h +++ b/src/otclient/core/missile.h @@ -46,7 +46,7 @@ public: MissilePtr asMissile() { return std::static_pointer_cast(shared_from_this()); } private: - int m_startTicks; + ticks_t m_startTicks; Position m_positionDelta; float m_duration; }; diff --git a/src/otclient/luascript/luafunctions.cpp b/src/otclient/luafunctions.cpp similarity index 70% rename from src/otclient/luascript/luafunctions.cpp rename to src/otclient/luafunctions.cpp index 8b3b1472..75caacfd 100644 --- a/src/otclient/luascript/luafunctions.cpp +++ b/src/otclient/luafunctions.cpp @@ -1,27 +1,6 @@ -/* - * Copyright (c) 2010-2011 OTClient - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include +#include "otclient.h" #include +#include #include #include #include @@ -39,12 +18,11 @@ #include #include -#include "luavaluecasts.h" - void OTClient::registerLuaFunctions() { - g_lua.bindGlobalFunction("exit", std::bind(&OTClient::exit, &g_client)); - g_lua.bindGlobalFunction("setOnClose", std::bind(&OTClient::setOnClose, &g_client, _1)); + Application::registerLuaFunctions(); + + g_lua.bindGlobalFunction("exit", std::bind(&Application::exit, &g_app)); g_lua.bindGlobalFunction("importDat", std::bind(&ThingsType::load, &g_thingsType, _1)); g_lua.bindGlobalFunction("importSpr", std::bind(&SpriteManager::load, &g_sprites, _1)); g_lua.bindGlobalFunction("getOufitColor", Outfit::getColor); diff --git a/src/otclient/luascript/luavaluecasts.h b/src/otclient/luascript/luavaluecasts.h index 71eb4064..b4bd63fe 100644 --- a/src/otclient/luascript/luavaluecasts.h +++ b/src/otclient/luascript/luavaluecasts.h @@ -35,5 +35,4 @@ bool luavalue_cast(int index, Outfit& outfit); void push_luavalue(const Position& pos); bool luavalue_cast(int index, Position& pos); - -#endif \ No newline at end of file +#endif diff --git a/src/otclient/net/protocolgameparse.cpp b/src/otclient/net/protocolgameparse.cpp index f1a3ed83..f98f321c 100644 --- a/src/otclient/net/protocolgameparse.cpp +++ b/src/otclient/net/protocolgameparse.cpp @@ -251,7 +251,7 @@ void ProtocolGame::parseMessage(InputMessage& msg) //case Otc::GameServerObjectInfo: //case Otc::GameServerPlayerInventory: default: - Fw::throwException("unknown opt byte ", opt); + Fw::throwException("unknown opt byte ", (int)opt); break; } } diff --git a/src/otclient/otclient.cpp b/src/otclient/otclient.cpp index 5a392e18..e0734bae 100644 --- a/src/otclient/otclient.cpp +++ b/src/otclient/otclient.cpp @@ -1,304 +1,10 @@ -/* - * Copyright (c) 2010-2011 OTClient - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - #include "otclient.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -OTClient g_client; +OTClient g_otclient; +Application& g_app = g_otclient; -void OTClient::init(std::vector args) +void OTClient::init(const std::vector& args) { - m_running = false; - m_stopping = false; - - // print client information logInfo(Otc::AppName, " ", Otc::AppVersion); - - // initialize platform related stuff - g_platform.init(this, Otc::AppName); - - // initialize script environment - g_lua.init(); - - // register otclient lua functions - registerLuaFunctions(); - - // initialize resources - g_resources.init(args[0].c_str(), Otc::AppPathName); - - // load configurations - loadConfigurations(); - - // create the client window - //TODO: bind these functions and move to otclient module - int minWidth = 550; - int minHeight = 450; - int windowX = Fw::fromstring(g_configs.get("window x"), 0); - int windowY = Fw::fromstring(g_configs.get("window y"), 0); - int windowWidth = Fw::fromstring(g_configs.get("window width"), minWidth); - int windowHeight = Fw::fromstring(g_configs.get("window height"), minHeight); - bool maximized = Fw::fromstring(g_configs.get("window maximized"), false); - g_platform.createWindow(windowX, windowY, windowWidth, windowHeight, minWidth, minHeight, maximized); - g_platform.setWindowTitle(Otc::AppName); - g_platform.setWindowIcon("otclient/otcicon.png"); - - // initialize graphics - g_graphics.init(); - - // initialize the ui - g_ui.init(); - - g_game.init(); - - // discover and load modules - g_modules.discoverAndLoadModules(); - g_modules.getModule("otclient")->load(); - ModulePtr tibiaFilesModule = g_modules.getModule("tibiafiles"); - if(!tibiaFilesModule || !tibiaFilesModule->isLoaded()) { - logFatal("Could not find or load 'tibiafiles' module, this module contains SPR and DAT files.\n" - "Please download it and place in modules directory, then try running again."); - } - - // now that everything is initialized, setup configurations - setupConfigurations(); - - // now that everything is loaded, show the window - g_platform.showWindow(); -} - -void OTClient::run() -{ - int frameTicks = g_clock.ticks(); - int lastPollTicks = frameTicks; - int frameCount = 0; - - m_stopping = false; - m_running = true; - - if(g_ui.getRootWidget()->getChildCount() == 0) { - logError("There is no root widgets to display, the app will close"); - m_stopping = true; - } - - // run the first poll - poll(); - - while(!m_stopping) { - //g_platform.sleep(150); - frameTicks = g_clock.updateTicks(); - - // poll events every POLL_CYCLE_DELAY - // this delay exists to avoid massive polling thus increasing framerate - if(frameTicks - lastPollTicks >= POLL_CYCLE_DELAY) { - poll(); - lastPollTicks = frameTicks; - } - - // only render when the windows is visible - if(g_platform.isWindowVisible()) { - // render begin - g_graphics.beginRender(); - - // render everything - render(); - - // render end - g_graphics.endRender(); - - // swap the old buffer with the new rendered - g_platform.swapBuffers(); - } else { - // sleeps until next poll to avoid massive cpu usage - g_clock.sleep(POLL_CYCLE_DELAY+1); - } - } - - m_stopping = false; - m_running = false; -} - -void OTClient::terminate() -{ - // hide the window because there is no render anymore - g_platform.hideWindow(); - - g_game.terminate(); - - // run modules unload event - g_modules.unloadModules(); - - // terminate ui - g_ui.terminate(); - - // release remaining lua object references - g_lua.collectGarbage(); - - // poll remaining events - poll(); - - // terminate network - Connection::terminate(); - - // flush remaining dispatcher events - g_dispatcher.flush(); - - // terminate graphics - g_graphics.terminate(); - - // save configurations - saveConfigurations(); - - // release resources - g_resources.terminate(); - - // terminate script environment - g_lua.terminate(); - - // end platform related stuff - g_platform.terminate(); - -} - -void OTClient::exit() -{ - // stops the main loop - m_stopping = true; -} - -void OTClient::poll() -{ - // poll platform events - g_platform.poll(); - - // poll network events - Connection::poll(); - - // poll dispatcher events - g_dispatcher.poll(); -} - -void OTClient::render() -{ - // everything is rendered by UI components - g_ui.render(); -} - -void OTClient::loadConfigurations() -{ - // default window size - int defWidth = 550; - int defHeight = 450; - - // sets default window configuration - g_configs.set("window x", Fw::tostring((g_platform.getDisplayWidth() - defWidth)/2)); - g_configs.set("window y", Fw::tostring((g_platform.getDisplayHeight() - defHeight)/2)); - g_configs.set("window width", Fw::tostring(defWidth)); - g_configs.set("window height", Fw::tostring(defHeight)); - g_configs.set("window maximized", Fw::tostring(false)); - g_configs.set("vsync", Fw::tostring(true)); - - // loads user configuration - if(!g_configs.load("config.otml")) - logInfo("Using default configurations."); -} - -void OTClient::setupConfigurations() -{ - // activate vertical synchronization? - bool vsync = Fw::fromstring(g_configs.get("vsync"), true); - g_platform.setVerticalSync(vsync); -} - -void OTClient::saveConfigurations() -{ - g_configs.set("window x", Fw::tostring(g_platform.getWindowX())); - g_configs.set("window y", Fw::tostring(g_platform.getWindowY())); - g_configs.set("window width", Fw::tostring(g_platform.getWindowWidth())); - g_configs.set("window height", Fw::tostring(g_platform.getWindowHeight())); - g_configs.set("window maximized", Fw::tostring(g_platform.isWindowMaximized())); - - // saves user configuration - if(!g_configs.save()) - logError("Configurations are lost because it couldn't be saved"); -} - -void OTClient::onClose() -{ - //TODO: make and use g_lua.callGlobalField - if(m_onCloseCallback) - m_onCloseCallback(); - else - exit(); -} - -void OTClient::onResize(const Size& size) -{ - static bool eventScheduled = false; - static Size newSize; - newSize = size; - - // avoid massive resize updates - if(!eventScheduled) { - g_dispatcher.addEvent([&] { - g_graphics.resize(newSize); - g_ui.resize(newSize); - eventScheduled = false; - }); - eventScheduled = true; - } -} - -void OTClient::onPlatformEvent(const PlatformEvent& event) -{ - bool fireUi = true; - - if(event.type == EventKeyDown && event.ctrl && !event.alt && !event.shift && event.keycode == Fw::KeyF8) { - // TODO: move this events to lua - UIWidgetPtr console = g_ui.getRootWidget()->getChildById("consolePanel"); - if(!console->isExplicitlyVisible()) { - console->lock(); - console->show(); - } else { - console->unlock(); - console->hide(); - } - fireUi = false; - } - - if(fireUi) - g_ui.inputEvent(event); + Application::init(Otc::AppName, args); } diff --git a/src/otclient/otclient.h b/src/otclient/otclient.h index 85726aeb..c40321a3 100644 --- a/src/otclient/otclient.h +++ b/src/otclient/otclient.h @@ -1,79 +1,16 @@ -/* - * Copyright (c) 2010-2011 OTClient - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - #ifndef OTCLIENT_H #define OTCLIENT_H -#include -#include +#include +#include -class OTClient : public PlatformListener +class OTClient : public Application { - enum { - POLL_CYCLE_DELAY = 10 - }; - public: - /// Where everything begins... - void init(std::vector args); - /// Main loop - void run(); - /// Called when the client is terminating - void terminate(); - - /// Stop running - void exit(); - /// Poll platform, dispatcher and network events - void poll(); - /// Render each frame - void render(); - - /// Fired when user tryes to close the window - void onClose(); - /// Fired when user resize the window - void onResize(const Size& size); - /// Fired on an user input event - void onPlatformEvent(const PlatformEvent& event); - - bool isRunning() const { return m_running; } - - void setOnClose(const SimpleCallback& onCloseCallback) { m_onCloseCallback = onCloseCallback; } - SimpleCallback getOnClose() const { return m_onCloseCallback; } - - static void registerLuaFunctions(); - -private: - /// Set default configurations and load the user configurations - void loadConfigurations(); - /// Use the loaded configurations to setup other classes - void setupConfigurations(); - void saveConfigurations(); - - bool m_running; - bool m_stopping; - - SimpleCallback m_onCloseCallback; + void init(const std::vector& args); + void registerLuaFunctions(); }; -extern OTClient g_client; +extern OTClient g_otclient; #endif diff --git a/src/otclient/ui/uigame.cpp b/src/otclient/ui/uigame.cpp index 5e9a3c78..21a781ba 100644 --- a/src/otclient/ui/uigame.cpp +++ b/src/otclient/ui/uigame.cpp @@ -24,7 +24,7 @@ #include #include -bool UIGame::onKeyPress(uchar keyCode, char keyChar, int keyboardModifiers) +bool UIGame::onKeyPress(uchar keyCode, std::string keyText, int keyboardModifiers) { UILineEditPtr chatLineEdit = std::dynamic_pointer_cast(getParent()->recursiveGetChildById("chatLineEdit")); @@ -100,10 +100,10 @@ bool UIGame::onKeyPress(uchar keyCode, char keyChar, int keyboardModifiers) } } - if(keyChar != 0) { - chatLineEdit->appendText(std::string(1, keyChar)); + if(!keyText.empty()) { + chatLineEdit->appendText(keyText); return true; } - return UIWidget::onKeyPress(keyCode, keyChar, keyboardModifiers); + return UIWidget::onKeyPress(keyCode, keyText, keyboardModifiers); } diff --git a/src/otclient/ui/uigame.h b/src/otclient/ui/uigame.h index e4a6115a..5b0c7b04 100644 --- a/src/otclient/ui/uigame.h +++ b/src/otclient/ui/uigame.h @@ -29,7 +29,7 @@ class UIGame : public UIWidget { protected: - virtual bool onKeyPress(uchar keyCode, char keyChar, int keyboardModifiers); + virtual bool onKeyPress(uchar keyCode, std::string keyText, int keyboardModifiers); };