too many changes to list, many regressions were made, master will be UNSTABLE for a few days

master
Eduardo Bart 13 years ago
parent 19eb56997d
commit f548825faf

@ -5,7 +5,7 @@ PROJECT(otclient)
SET(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}") SET(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}")
OPTION(USE_PCH "Use precompiled header" ON) 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(HANDLE_EXCEPTIONS "Generate crash reports" OFF)
OPTION(FORBIDDEN_FUNCTIONS "Enable forbidden lua functions" ON) OPTION(FORBIDDEN_FUNCTIONS "Enable forbidden lua functions" ON)
OPTION(USE_OPENGLES2 "Use OpenGL ES 2.0 (for mobiles devices)" OFF) 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 # setup compiler options
IF(CMAKE_COMPILER_IS_GNUCXX) 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 "-std=gnu++0x -pipe ${CXX_WARNS}")
SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3 -ggdb3 -fno-inline") SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3 -ggdb3 -fno-inline")
SET(CMAKE_CXX_FLAGS_RELEASE "-O2") SET(CMAKE_CXX_FLAGS_RELEASE "-O2")
@ -68,9 +68,9 @@ SET(SOURCES
# otclient # otclient
src/otclient/otclient.cpp src/otclient/otclient.cpp
src/otclient/luafunctions.cpp
# otclient luascript # otclient luascript
src/otclient/luascript/luafunctions.cpp
src/otclient/luascript/luavaluecasts.cpp src/otclient/luascript/luavaluecasts.cpp
# otclient core # otclient core
@ -99,6 +99,10 @@ SET(SOURCES
src/otclient/net/protocolgamesend.cpp src/otclient/net/protocolgamesend.cpp
src/otclient/net/protocolgameparse.cpp src/otclient/net/protocolgameparse.cpp
# framework
src/framework/application.cpp
src/framework/luafunctions.cpp
# framework third party # framework third party
src/framework/thirdparty/apngloader.cpp src/framework/thirdparty/apngloader.cpp
@ -118,9 +122,11 @@ SET(SOURCES
src/framework/core/eventdispatcher.cpp src/framework/core/eventdispatcher.cpp
src/framework/core/modulemanager.cpp src/framework/core/modulemanager.cpp
src/framework/core/module.cpp src/framework/core/module.cpp
src/framework/core/engine.cpp
src/framework/core/clock.cpp src/framework/core/clock.cpp
# framework platform
src/framework/platform/platformwindow.cpp
# framework graphics # framework graphics
src/framework/graphics/font.cpp src/framework/graphics/font.cpp
src/framework/graphics/fontmanager.cpp src/framework/graphics/fontmanager.cpp
@ -144,7 +150,6 @@ SET(SOURCES
src/framework/luascript/luainterface.cpp src/framework/luascript/luainterface.cpp
src/framework/luascript/luaobject.cpp src/framework/luascript/luaobject.cpp
src/framework/luascript/luaexception.cpp src/framework/luascript/luaexception.cpp
src/framework/luascript/luafunctions.cpp
src/framework/luascript/luavaluecasts.cpp src/framework/luascript/luavaluecasts.cpp
# framework ui # framework ui
@ -172,7 +177,7 @@ IF(FORBIDDEN_FUNCTIONS)
ENDIF(FORBIDDEN_FUNCTIONS) ENDIF(FORBIDDEN_FUNCTIONS)
IF(WIN32) IF(WIN32)
SET(SOURCES ${SOURCES} src/framework/platform/win32platform.cpp) SET(SOURCES ${SOURCES} src/framework/platform/win32window.cpp)
SET(ADDITIONAL_LIBRARIES ws2_32 mswsock) SET(ADDITIONAL_LIBRARIES ws2_32 mswsock)
IF(CMAKE_COMPILER_IS_GNUCXX) IF(CMAKE_COMPILER_IS_GNUCXX)
@ -180,7 +185,6 @@ IF(WIN32)
ENDIF(CMAKE_COMPILER_IS_GNUCXX) ENDIF(CMAKE_COMPILER_IS_GNUCXX)
IF(NO_CONSOLE) IF(NO_CONSOLE)
ADD_DEFINITIONS(-DWIN32_NO_CONSOLE)
IF(CMAKE_COMPILER_IS_GNUCXX) IF(CMAKE_COMPILER_IS_GNUCXX)
SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -mwindows") SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -mwindows")
ENDIF(CMAKE_COMPILER_IS_GNUCXX) ENDIF(CMAKE_COMPILER_IS_GNUCXX)
@ -195,7 +199,7 @@ IF(WIN32)
ELSE(WIN32) ELSE(WIN32)
SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -rdynamic") SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -rdynamic")
SET(ADDITIONAL_LIBRARIES pthread) SET(ADDITIONAL_LIBRARIES pthread)
SET(SOURCES ${SOURCES} src/framework/platform/x11platform.cpp) SET(SOURCES ${SOURCES} src/framework/platform/x11window.cpp)
ENDIF(WIN32) ENDIF(WIN32)
# target executable # target executable

16
TODO

@ -13,4 +13,18 @@ grid layout
fix moving windows and tooltips conflicts fix moving windows and tooltips conflicts
todo display otclient icon in window bar todo display otclient icon in window bar
remake otui styles states system remake otui styles states system
padding 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

@ -81,7 +81,7 @@ local function completeCommand()
end end
end end
local function onKeyPress(widget, keyCode, keyChar, keyboardModifiers) local function onKeyPress(widget, keyCode, keyText, keyboardModifiers)
if keyboardModifiers == KeyboardNoModifier then if keyboardModifiers == KeyboardNoModifier then
-- execute current command -- execute current command
if keyCode == KeyReturn or keyCode == keyEnter then if keyCode == KeyReturn or keyCode == keyEnter then

@ -23,4 +23,4 @@ function toboolean(str)
return true return true
end end
return false return false
end end

@ -6,7 +6,7 @@ local loadBox
local characterList local characterList
-- private functions -- private functions
local function onCharactersWindowKeyPress(self, keyCode, keyChar, keyboardModifiers) local function onCharactersWindowKeyPress(self, keyCode, keyText, keyboardModifiers)
if keyboardModifiers == KeyboardNoModifier then if keyboardModifiers == KeyboardNoModifier then
if keyCode == KeyUp or keyCode == KeyTab then if keyCode == KeyUp or keyCode == KeyTab then
characterList:focusPreviousChild(ActiveFocusReason) characterList:focusPreviousChild(ActiveFocusReason)
@ -47,7 +47,7 @@ local function tryLogin(charInfo, tries)
end end
-- save last used character -- save last used character
ConfigManager.set('lastUsedCharacter', charInfo.characterName) g_configs.set('lastUsedCharacter', charInfo.characterName)
end end
-- public functions -- public functions
@ -75,7 +75,7 @@ function CharacterList.create(characters, premDays)
label.worldHost = worldHost label.worldHost = worldHost
label.worldPort = worldIp 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) characterList:focusChild(label, ActiveFocusReason)
end end
end end

@ -11,8 +11,8 @@ local function clearAccountFields()
enterGame:getChildById('accountNameLineEdit'):clearText() enterGame:getChildById('accountNameLineEdit'):clearText()
enterGame:getChildById('accountPasswordLineEdit'):clearText() enterGame:getChildById('accountPasswordLineEdit'):clearText()
enterGame:getChildById('accountNameLineEdit'):focus() enterGame:getChildById('accountNameLineEdit'):focus()
ConfigManager.set('account', nil) g_configs.set('account', nil)
ConfigManager.set('password', nil) g_configs.set('password', nil)
end end
local function onError(protocol, error) local function onError(protocol, error)
@ -30,9 +30,9 @@ end
local function onCharacterList(protocol, characters, premDays) local function onCharacterList(protocol, characters, premDays)
if enterGame:getChildById('rememberPasswordBox'):isChecked() then if enterGame:getChildById('rememberPasswordBox'):isChecked() then
ConfigManager.set('account', EnterGame.account) g_configs.set('account', EnterGame.account)
ConfigManager.set('password', EnterGame.password) g_configs.set('password', EnterGame.password)
ConfigManager.set('autologin', tostring(enterGame:getChildById('autoLoginBox'):isChecked())) g_configs.set('autologin', tostring(enterGame:getChildById('autoLoginBox'):isChecked()))
else else
clearAccountFields() clearAccountFields()
end end
@ -40,9 +40,9 @@ local function onCharacterList(protocol, characters, premDays)
loadBox:destroy() loadBox:destroy()
CharacterList.create(characters, premDays) CharacterList.create(characters, premDays)
local lastMotdNumber = tonumber(ConfigManager.get("motd")) local lastMotdNumber = tonumber(g_configs.get("motd"))
if motdNumber and motdNumber ~= lastMotdNumber then if motdNumber and motdNumber ~= lastMotdNumber then
ConfigManager.set("motd", motdNumber) g_configs.set("motd", motdNumber)
local motdBox = displayInfoBox("Message of the day", motdMessage) local motdBox = displayInfoBox("Message of the day", motdMessage)
motdBox.onOk = CharacterList.show motdBox.onOk = CharacterList.show
CharacterList.hide() CharacterList.hide()
@ -53,11 +53,11 @@ end
function EnterGame.create() function EnterGame.create()
enterGame = UI.display('entergame.otui') enterGame = UI.display('entergame.otui')
local account = ConfigManager.get('account') local account = g_configs.get('account')
local password = ConfigManager.get('password') local password = g_configs.get('password')
local host = ConfigManager.get('host') local host = g_configs.get('host')
local port = tonumber(ConfigManager.get('port')) local port = tonumber(g_configs.get('port'))
local autologin = toboolean(ConfigManager.get('autologin')) local autologin = toboolean(g_configs.get('autologin'))
enterGame:getChildById('accountNameLineEdit'):setText(account) enterGame:getChildById('accountNameLineEdit'):setText(account)
enterGame:getChildById('accountPasswordLineEdit'):setText(password) enterGame:getChildById('accountPasswordLineEdit'):setText(password)
@ -93,8 +93,8 @@ function EnterGame.doLogin()
EnterGame.port = enterGame:getChildById('serverPortLineEdit'):getText() EnterGame.port = enterGame:getChildById('serverPortLineEdit'):getText()
EnterGame.hide() EnterGame.hide()
ConfigManager.set('host', EnterGame.host) g_configs.set('host', EnterGame.host)
ConfigManager.set('port', EnterGame.port) g_configs.set('port', EnterGame.port)
local protocolLogin = ProtocolLogin.create() local protocolLogin = ProtocolLogin.create()
protocolLogin.onError = onError protocolLogin.onError = onError

@ -1,5 +1,5 @@
-- private functions -- private functions
local function onGameKeyPress(self, keyCode, keyChar, keyboardModifiers) local function onGameKeyPress(self, keyCode, keyText, keyboardModifiers)
if keyboardModifiers == KeyboardCtrlModifier then if keyboardModifiers == KeyboardCtrlModifier then
if keyCode == KeyG then if keyCode == KeyG then
CharacterList.show() CharacterList.show()

@ -6,8 +6,8 @@ local fpsEnabled = false
local vsyncEnabled = false local vsyncEnabled = false
function getConfig(name, default) function getConfig(name, default)
if ConfigManager.exists(name) then if g_configs.exists(name) then
local val = string.trim(ConfigManager.get(name)) local val = string.trim(g_configs.get(name))
if val == 'true' or val == 'false' then if val == 'true' or val == 'false' then
return toboolean(val) return toboolean(val)
else else
@ -15,7 +15,7 @@ function getConfig(name, default)
end end
else else
if default ~= nil then if default ~= nil then
ConfigManager.set(name, default) g_configs.set(name, default)
return default return default
else else
return nil return nil
@ -24,13 +24,13 @@ function getConfig(name, default)
end end
function setConfig(name, value) function setConfig(name, value)
ConfigManager.set(name, tostring(value)) g_configs.set(name, tostring(value))
end end
-- private functions -- private functions
function Options.enableVsync(on) function Options.enableVsync(on)
vsyncEnabled = on vsyncEnabled = on
setVerticalSync(on) g_window.setVerticalSync(on)
setConfig('vsync', on) setConfig('vsync', on)
end end

@ -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

@ -3,6 +3,8 @@ Module
description: Load all other otclient modules description: Load all other otclient modules
author: OTClient team author: OTClient team
website: https://github.com/edubart/otclient website: https://github.com/edubart/otclient
autoLoad: true
autoLoadPriority: 10
dependencies: dependencies:
- core - core
- background - background
@ -16,3 +18,10 @@ Module
- chat - chat
- outfit - outfit
- tibiafiles - tibiafiles
onLoad: |
require 'otclient'
return OTClient.init()
onUnload: |
OTClient.terminate()

@ -5,9 +5,9 @@ local currentToolTip
-- private functions -- private functions
local function moveToolTip(tooltip) local function moveToolTip(tooltip)
local pos = getMouseCursorPos() local pos = g_window.getMousePos()
pos.y = pos.y + 1 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 if xdif < 2 then
pos.x = pos.x - tooltip:getWidth() - 3 pos.x = pos.x - tooltip:getWidth() - 3
else else

@ -4,6 +4,7 @@ Module
author: OTClient team author: OTClient team
website: https://github.com/edubart/otclient website: https://github.com/edubart/otclient
autoLoad: true autoLoad: true
autoLoadPriority: 2
dependencies: dependencies:
- core - core

@ -0,0 +1,207 @@
/*
* Copyright (c) 2010-2011 OTClient <https://github.com/edubart/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 <framework/core/clock.h>
#include <framework/core/resourcemanager.h>
#include <framework/core/modulemanager.h>
#include <framework/core/eventdispatcher.h>
#include <framework/core/configmanager.h>
#include <framework/net/connection.h>
#include <framework/platform/platformwindow.h>
#include <framework/ui/uimanager.h>
#include <framework/ui/uiwidget.h>
#include <framework/graphics/graphics.h>
#include <framework/luascript/luainterface.h>
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<std::string>& 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);
}

@ -0,0 +1,64 @@
/*
* Copyright (c) 2010-2011 OTClient <https://github.com/edubart/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 <framework/core/inputevent.h>
class Application
{
enum {
POLL_CYCLE_DELAY = 10
};
public:
virtual void init(const std::string& appName, const std::vector<std::string>& 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<false> m_running;
Boolean<false> m_stopping;
};
extern Application& g_app;
#endif

@ -225,6 +225,16 @@ namespace Fw
OtherFocusReason OtherFocusReason
}; };
enum InputEventType {
NoInputEvent = 0,
KeyPressInputEvent,
KeyReleaseInputEvent,
MousePressInputEvent,
MouseReleaseInputEvent,
MouseMoveInputEvent,
MouseWheelInputEvent
};
enum MouseButton { enum MouseButton {
MouseNoButton = 0, MouseNoButton = 0,
MouseLeftButton, MouseLeftButton,

@ -22,14 +22,18 @@
#include "clock.h" #include "clock.h"
// for usleep
#include <unistd.h>
Clock g_clock; Clock g_clock;
Clock::Clock() Clock::Clock()
{ {
m_startupTime = std::chrono::high_resolution_clock::now(); 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(); auto timeNow = std::chrono::high_resolution_clock::now();
m_currentTicks = std::chrono::duration_cast<std::chrono::milliseconds>(timeNow - m_startupTime).count(); m_currentTicks = std::chrono::duration_cast<std::chrono::milliseconds>(timeNow - m_startupTime).count();

@ -30,15 +30,15 @@ class Clock
public: public:
Clock(); Clock();
int updateTicks(); ticks_t updateTicks();
void sleep(int ms); void sleep(int ms);
int ticks() { return m_currentTicks; } ticks_t ticks() { return m_currentTicks; }
int ticksElapsed(int prevTicks) { return ticks() - prevTicks; } ticks_t ticksElapsed(long prevTicks) { return ticks() - prevTicks; }
int ticksFor(int delay) { return ticks() + delay; } ticks_t ticksFor(int delay) { return ticks() + delay; }
private: private:
int m_currentTicks; ticks_t m_currentTicks;
std::chrono::system_clock::time_point m_startupTime; std::chrono::system_clock::time_point m_startupTime;
}; };

@ -26,9 +26,9 @@
#include "declarations.h" #include "declarations.h"
struct ScheduledEvent { 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; } bool operator<(const ScheduledEvent& other) const { return ticks > other.ticks; }
int ticks; ticks_t ticks;
SimpleCallback callback; SimpleCallback callback;
}; };

@ -20,20 +20,20 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#ifndef PLATFORMLISTENER_H #ifndef INPUTEVENT_H
#define PLATFORMLISTENER_H #define INPUTEVENT_H
#include "platformevent.h" #include "declarations.h"
class PlatformListener struct InputEvent {
{ Fw::InputEventType type;
public: Fw::MouseWheelDirection wheelDirection;
/// Fired when user tries to close the window Fw::MouseButton mouseButton;
virtual void onClose() = 0; int keyboardModifiers;
/// Fired when user resize the window std::string keyText;
virtual void onResize(const Size& size) = 0; Fw::Key keyCode;
/// Fired when user press a key or move the mouse Point mousePos;
virtual void onPlatformEvent(const PlatformEvent& event) = 0; Point mouseMoved;
}; };
#endif #endif

@ -22,7 +22,6 @@
#include "logger.h" #include "logger.h"
#include "eventdispatcher.h" #include "eventdispatcher.h"
#include <framework/platform/platform.h>
Logger g_logger; Logger g_logger;

@ -70,4 +70,4 @@ extern Logger g_logger;
#define logTraceWarning(...) g_logger.logFunc(Fw::LogWarning, Fw::mkstr(__VA_ARGS__), __PRETTY_FUNCTION__) #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__) #define logTraceError(...) g_logger.logFunc(Fw::LogError, Fw::mkstr(__VA_ARGS__), __PRETTY_FUNCTION__)
#endif #endif

@ -26,6 +26,11 @@
#include <framework/otml/otml.h> #include <framework/otml/otml.h>
#include <framework/luascript/luainterface.h> #include <framework/luascript/luainterface.h>
Module::Module(const std::string& name)
{
m_name = name;
}
void Module::discover(const OTMLNodePtr& moduleNode) void Module::discover(const OTMLNodePtr& moduleNode)
{ {
const static std::string none = "none"; const static std::string none = "none";
@ -34,6 +39,7 @@ void Module::discover(const OTMLNodePtr& moduleNode)
m_website = moduleNode->valueAt("website", none); m_website = moduleNode->valueAt("website", none);
m_version = moduleNode->valueAt("version", none); m_version = moduleNode->valueAt("version", none);
m_autoLoad = moduleNode->valueAt<bool>("autoLoad", false); m_autoLoad = moduleNode->valueAt<bool>("autoLoad", false);
m_autoLoadPriority = moduleNode->valueAt<int>("autoLoadPriority", 100);
if(OTMLNodePtr node = moduleNode->get("dependencies")) { if(OTMLNodePtr node = moduleNode->get("dependencies")) {
for(const OTMLNodePtr& tmp : node->children()) for(const OTMLNodePtr& tmp : node->children())
@ -57,6 +63,9 @@ void Module::discover(const OTMLNodePtr& moduleNode)
bool Module::load() bool Module::load()
{ {
if(m_loaded)
return true;
for(const std::string& depName : m_dependencies) { for(const std::string& depName : m_dependencies) {
ModulePtr dep = g_modules.getModule(depName); ModulePtr dep = g_modules.getModule(depName);
if(!dep) { if(!dep) {

@ -30,7 +30,7 @@
class Module class Module
{ {
public: 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); void discover(const OTMLNodePtr& moduleNode);
bool load(); bool load();
@ -43,11 +43,13 @@ public:
std::string getAuthor() { return m_author; } std::string getAuthor() { return m_author; }
std::string getWebsite() { return m_website; } std::string getWebsite() { return m_website; }
std::string getVersion() { return m_version; } std::string getVersion() { return m_version; }
bool autoLoad() { return m_autoLoad; } bool isAutoLoad() { return m_autoLoad; }
int getAutoLoadPriority() { return m_autoLoadPriority; }
private: private:
bool m_loaded; Boolean<false> m_loaded;
bool m_autoLoad; Boolean<false> m_autoLoad;
int m_autoLoadPriority;
std::string m_name; std::string m_name;
std::string m_description; std::string m_description;
std::string m_author; std::string m_author;

@ -29,29 +29,32 @@ ModuleManager g_modules;
void ModuleManager::discoverAndLoadModules() void ModuleManager::discoverAndLoadModules()
{ {
std::multimap<int, ModulePtr> m_autoLoadModules;
auto moduleDirs = g_resources.listDirectoryFiles("/"); auto moduleDirs = g_resources.listDirectoryFiles("/");
for(const std::string& moduleDir : moduleDirs) { for(const std::string& moduleDir : moduleDirs) {
auto moduleFiles = g_resources.listDirectoryFiles("/" + moduleDir); auto moduleFiles = g_resources.listDirectoryFiles("/" + moduleDir);
for(const std::string& file : moduleFiles) { for(const std::string& moduleFile : moduleFiles) {
if(boost::ends_with(file, ".otmod")) if(boost::ends_with(moduleFile, ".otmod")) {
discoverModule("/" + moduleDir + "/" + file); ModulePtr module = discoverModule("/" + moduleDir + "/" + moduleFile);
if(module && module->isAutoLoad())
m_autoLoadModules.insert(make_pair(module->getAutoLoadPriority(), module));
}
} }
} }
// auto load modules for(auto& pair : m_autoLoadModules) {
for(const ModulePtr& module : m_modules) { ModulePtr module = pair.second;
if(!module->isLoaded() && module->autoLoad()) { if(!module->isLoaded() && !module->load())
if(!module->load()) logFatal("A required module has failed to load, cannot continue to run.");
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; ModulePtr module;
try { try {
OTMLDocumentPtr doc = OTMLDocument::parse(file); OTMLDocumentPtr doc = OTMLDocument::parse(moduleFile);
OTMLNodePtr moduleNode = doc->at("Module"); OTMLNodePtr moduleNode = doc->at("Module");
std::string name = moduleNode->valueAt("name"); std::string name = moduleNode->valueAt("name");
@ -62,10 +65,9 @@ bool ModuleManager::discoverModule(const std::string& file)
module->discover(moduleNode); module->discover(moduleNode);
m_modules.push_back(module); m_modules.push_back(module);
} catch(Exception& e) { } catch(Exception& e) {
logError("Unable to discover module from file '", file, "': ", e.what()); logError("Unable to discover module from file '", moduleFile, "': ", e.what());
return false;
} }
return true; return module;
} }
void ModuleManager::unloadModules() void ModuleManager::unloadModules()

@ -29,7 +29,7 @@ class ModuleManager
{ {
public: public:
void discoverAndLoadModules(); void discoverAndLoadModules();
bool discoverModule(const std::string& file); ModulePtr discoverModule(const std::string& moduleFile);
void unloadModules(); void unloadModules();
ModulePtr getModule(const std::string& moduleName); ModulePtr getModule(const std::string& moduleName);

@ -22,24 +22,28 @@
#include "resourcemanager.h" #include "resourcemanager.h"
#include <framework/platform/platform.h> #include <framework/application.h>
#include <framework/luascript/luainterface.h> #include <framework/luascript/luainterface.h>
#include <physfs.h> #include <physfs.h>
ResourceManager g_resources; ResourceManager g_resources;
void ResourceManager::init(const char* argv0, const char *appName) void ResourceManager::init(const char *argv0)
{ {
PHYSFS_init(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 // try to find modules directory, all data lives there
//TODO: move this to Application class
std::string baseDir = PHYSFS_getBaseDir(); std::string baseDir = PHYSFS_getBaseDir();
std::string possibleDirs[] = { "modules", std::string possibleDirs[] = { "modules",
baseDir + "modules", baseDir + "modules",
baseDir + "../modules", baseDir + "../modules",
baseDir + "../share/" + appName + "/otclient/modules", baseDir + "../share/" + g_app.getAppName() + "/otclient/modules",
"" }; "" };
bool found = false; bool found = false;
@ -52,14 +56,7 @@ void ResourceManager::init(const char* argv0, const char *appName)
} }
if(!found) if(!found)
logFatal("Could not find modules directory."); 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");
} }
void ResourceManager::terminate() void ResourceManager::terminate()
@ -67,10 +64,22 @@ void ResourceManager::terminate()
PHYSFS_deinit(); PHYSFS_deinit();
} }
bool ResourceManager::setWriteDir(const std::string& path) bool ResourceManager::setupWriteDir()
{ {
if(!PHYSFS_setWriteDir(path.c_str())) std::string userDir = PHYSFS_getUserDir();
return false; 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; return true;
} }
@ -97,7 +106,7 @@ bool ResourceManager::fileExists(const std::string& fileName)
bool ResourceManager::directoryExists(const std::string& directoryName) 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) void ResourceManager::loadFile(const std::string& fileName, std::iostream& out)

@ -28,11 +28,10 @@
class ResourceManager class ResourceManager
{ {
public: public:
void init(const char* argv0, const char *appName); void init(const char *argv0);
void terminate(); void terminate();
/// Set output files directory bool setupWriteDir();
bool setWriteDir(const std::string& path);
/// Add an package or directory to the search path /// Add an package or directory to the search path
bool addToSearchPath(const std::string& path, bool insertInFront = true); bool addToSearchPath(const std::string& path, bool insertInFront = true);
@ -55,6 +54,7 @@ public:
std::list<std::string> listDirectoryFiles(const std::string& directoryPath = ""); std::list<std::string> listDirectoryFiles(const std::string& directoryPath = "");
std::string resolvePath(const std::string& path); std::string resolvePath(const std::string& path);
std::string getAppUserPath();
}; };
extern ResourceManager g_resources; extern ResourceManager g_resources;

@ -30,7 +30,7 @@
#include <cassert> #include <cassert>
#include <ctime> #include <ctime>
#include <cmath> #include <cmath>
#include <unistd.h> #include <csignal>
// common STL headers // common STL headers
#include <iostream> #include <iostream>

@ -23,11 +23,8 @@
#include "animatedtexture.h" #include "animatedtexture.h"
#include "graphics.h" #include "graphics.h"
#include <framework/platform/platform.h>
#include <framework/core/eventdispatcher.h> #include <framework/core/eventdispatcher.h>
#include <GL/gl.h>
AnimatedTexture::AnimatedTexture(int width, int height, int channels, int numFrames, uchar *framesPixels, int *framesDelay) : AnimatedTexture::AnimatedTexture(int width, int height, int channels, int numFrames, uchar *framesPixels, int *framesDelay) :
Texture(), Texture(),
m_numFrames(numFrames) m_numFrames(numFrames)

@ -41,7 +41,7 @@ private:
std::vector<int> m_framesDelay; std::vector<int> m_framesDelay;
int m_numFrames; int m_numFrames;
int m_currentFrame; int m_currentFrame;
int m_lastAnimCheckTicks; ticks_t m_lastAnimCheckTicks;
}; };
#endif #endif

@ -24,6 +24,7 @@
#define FRAMEWORK_GRAPHICS_DECLARATIONS_H #define FRAMEWORK_GRAPHICS_DECLARATIONS_H
#include <framework/global.h> #include <framework/global.h>
#include "glutil.h"
class Texture; class Texture;
class AnimatedTexture; class AnimatedTexture;

@ -24,17 +24,6 @@
#include "graphics.h" #include "graphics.h"
#include "texture.h" #include "texture.h"
#include <framework/platform/platform.h>
#include <GL/gl.h>
#include <GL/glext.h>
PFNGLGENFRAMEBUFFERSPROC oglGenFramebuffers = 0;
PFNGLBINDFRAMEBUFFERPROC oglBindFramebuffer = 0;
PFNGLFRAMEBUFFERTEXTURE2DPROC oglFramebufferTexture2D = 0;
PFNGLDELETEFRAMEBUFFERSPROC oglDeleteFramebuffers = 0;
PFNGLCHECKFRAMEBUFFERSTATUSPROC oglCheckFramebufferStatus = 0;
FrameBuffer::FrameBuffer(int width, int height) FrameBuffer::FrameBuffer(int width, int height)
{ {
m_fbo = 0; m_fbo = 0;
@ -46,22 +35,15 @@ FrameBuffer::FrameBuffer(int width, int height)
// use FBO ext only if supported // use FBO ext only if supported
if(g_graphics.isExtensionSupported("GL_ARB_framebuffer_object")) { if(g_graphics.isExtensionSupported("GL_ARB_framebuffer_object")) {
m_fallbackOldImp = false; 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 // generate FBO
oglGenFramebuffers(1, &m_fbo); glGenFramebuffers(1, &m_fbo);
oglBindFramebuffer(GL_FRAMEBUFFER_EXT, m_fbo); glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_fbo);
// attach 2D texture to this 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) { switch(status) {
case GL_FRAMEBUFFER_COMPLETE_EXT: case GL_FRAMEBUFFER_COMPLETE_EXT:
//ok //ok
@ -72,7 +54,7 @@ FrameBuffer::FrameBuffer(int width, int height)
} }
// restore back buffer // restore back buffer
oglBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
glDrawBuffer(GL_BACK); glDrawBuffer(GL_BACK);
glReadBuffer(GL_BACK); glReadBuffer(GL_BACK);
} else { } else {
@ -87,14 +69,14 @@ FrameBuffer::FrameBuffer(int width, int height)
FrameBuffer::~FrameBuffer() FrameBuffer::~FrameBuffer()
{ {
if(m_fbo) if(m_fbo)
oglDeleteFramebuffers(1, &m_fbo); glDeleteFramebuffers(1, &m_fbo);
} }
void FrameBuffer::bind() void FrameBuffer::bind()
{ {
if(!m_fallbackOldImp) { if(!m_fallbackOldImp) {
// bind framebuffer // bind framebuffer
oglBindFramebuffer(GL_FRAMEBUFFER_EXT, m_fbo); glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_fbo);
} else { } else {
int screenWidth = g_graphics.getScreenSize().width(); int screenWidth = g_graphics.getScreenSize().width();
int screenHeight = g_graphics.getScreenSize().height(); int screenHeight = g_graphics.getScreenSize().height();
@ -126,7 +108,7 @@ void FrameBuffer::unbind()
{ {
if(!m_fallbackOldImp) { if(!m_fallbackOldImp) {
// bind back buffer again // bind back buffer again
oglBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
glDrawBuffer(GL_BACK); glDrawBuffer(GL_BACK);
glReadBuffer(GL_BACK); glReadBuffer(GL_BACK);

@ -31,13 +31,8 @@ public:
FrameBuffer(int width, int height); FrameBuffer(int width, int height);
virtual ~FrameBuffer(); virtual ~FrameBuffer();
/// Binds the framebuffer, by switching render buffer to itself, everything rendered will be draw on it
void bind(); void bind();
/// Unbinds the framebuffer (switch render buffer to back buffer again)
void unbind(); void unbind();
/// Draws framebuffer texture
void draw(const Rect& screenCoords, const Rect& framebufferCoords = Rect()); void draw(const Rect& screenCoords, const Rect& framebufferCoords = Rect());
TexturePtr getTexture() { return m_texture; } TexturePtr getTexture() { return m_texture; }

@ -0,0 +1,37 @@
/*
* Copyright (c) 2010-2011 OTClient <https://github.com/edubart/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 <GL/gl.h>
#include <GL/glext.h>
#else
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#endif
#endif

@ -25,9 +25,6 @@
#include <framework/graphics/graphics.h> #include <framework/graphics/graphics.h>
#include <framework/graphics/texture.h> #include <framework/graphics/texture.h>
#include <GL/gl.h>
#include <GL/glext.h>
Graphics g_graphics; Graphics g_graphics;
void Graphics::init() void Graphics::init()

@ -23,9 +23,6 @@
#include "texture.h" #include "texture.h"
#include "graphics.h" #include "graphics.h"
#include <GL/gl.h>
#include <GL/glext.h>
Texture::Texture() Texture::Texture()
{ {
m_textureId = 0; 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, " logError("loading texture with size ", width, "x", height, " failed, "
"the maximum size allowed by the graphics card is ", maxTexSize, "x", maxTexSize, ",", "the maximum size allowed by the graphics card is ", maxTexSize, "x", maxTexSize, ",",
"to prevent crashes the texture will be displayed as a blank texture"); "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; return 0;
} }

@ -20,17 +20,18 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "luainterface.h" #include <framework/luascript/luainterface.h>
#include <framework/application.h>
#include <framework/graphics/fontmanager.h> #include <framework/graphics/fontmanager.h>
#include <framework/ui/ui.h> #include <framework/ui/ui.h>
#include <framework/net/protocol.h> #include <framework/net/protocol.h>
#include <framework/core/eventdispatcher.h> #include <framework/core/eventdispatcher.h>
#include <framework/core/configmanager.h> #include <framework/core/configmanager.h>
#include <framework/platform/platform.h>
#include <framework/otml/otml.h> #include <framework/otml/otml.h>
#include <framework/graphics/graphics.h> #include <framework/graphics/graphics.h>
#include <framework/platform/platformwindow.h>
void LuaInterface::registerFunctions() void Application::registerLuaFunctions()
{ {
// UIWidget // UIWidget
g_lua.registerClass<UIWidget>(); g_lua.registerClass<UIWidget>();
@ -152,10 +153,21 @@ void LuaInterface::registerFunctions()
g_lua.registerClass<Protocol>(); g_lua.registerClass<Protocol>();
// ConfigManager // ConfigManager
g_lua.registerClass<ConfigManager>(); g_lua.registerStaticClass("g_configs");
g_lua.bindClassStaticFunction<ConfigManager>("set", std::bind(&ConfigManager::set, &g_configs, _1, _2)); g_lua.bindClassStaticFunction("g_configs", "set", std::bind(&ConfigManager::set, &g_configs, _1, _2));
g_lua.bindClassStaticFunction<ConfigManager>("get", std::bind(&ConfigManager::get, &g_configs, _1)); g_lua.bindClassStaticFunction("g_configs", "get", std::bind(&ConfigManager::get, &g_configs, _1));
g_lua.bindClassStaticFunction<ConfigManager>("exists", std::bind(&ConfigManager::exists, &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 // Logger
g_lua.registerClass<Logger>(); g_lua.registerClass<Logger>();
@ -171,7 +183,4 @@ void LuaInterface::registerFunctions()
g_lua.bindGlobalFunction("getRootWidget", std::bind(&UIManager::getRootWidget, &g_ui)); 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("addEvent", std::bind(&EventDispatcher::addEvent, &g_dispatcher, _1, false));
g_lua.bindGlobalFunction("scheduleEvent", std::bind(&EventDispatcher::scheduleEvent, &g_dispatcher, _1, _2)); 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));
} }

@ -47,9 +47,6 @@ void LuaInterface::init()
// register LuaObject, the base of all other objects // register LuaObject, the base of all other objects
registerClass<LuaObject>(); registerClass<LuaObject>();
bindClassMemberGetField<LuaObject>("use_count", &LuaObject::getUseCount); bindClassMemberGetField<LuaObject>("use_count", &LuaObject::getUseCount);
// register the real script stuff
registerFunctions();
} }
void LuaInterface::terminate() void LuaInterface::terminate()
@ -58,6 +55,14 @@ void LuaInterface::terminate()
closeLuaState(); closeLuaState();
} }
void LuaInterface::registerStaticClass(const std::string& className)
{
newTable();
pushValue();
setGlobal(className);
pop();
}
void LuaInterface::registerClass(const std::string& className, const std::string& baseClass) void LuaInterface::registerClass(const std::string& className, const std::string& baseClass)
{ {
// creates the class table (that it's also the class methods table) // creates the class table (that it's also the class methods table)

@ -42,6 +42,7 @@ public:
void registerFunctions(); void registerFunctions();
// functions that will register all script stuff in lua global environment // 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 registerClass(const std::string& className, const std::string& baseClass = "LuaObject");
void registerClassStaticFunction(const std::string& className, void registerClassStaticFunction(const std::string& className,
@ -86,18 +87,28 @@ public:
// methods for binding functions // methods for binding functions
template<class C, typename F> template<class C, typename F>
void bindClassStaticFunction(const std::string& functionName, const F& function); void bindClassStaticFunction(const std::string& functionName, const F& function);
template<typename F>
void bindClassStaticFunction(const std::string& className, const std::string& functionName, const F& function);
template<class C, typename F> template<class C, typename F>
void bindClassMemberFunction(const std::string& functionName, F C::*function); void bindClassMemberFunction(const std::string& functionName, F C::*function);
template<class C, typename F>
void bindClassMemberFunction(const std::string& className, const std::string& functionName, F C::*function);
template<class C, typename F1, typename F2> template<class C, typename F1, typename F2>
void bindClassMemberField(const std::string& fieldName, F1 C::*getFunction, F2 C::*setFunction); void bindClassMemberField(const std::string& fieldName, F1 C::*getFunction, F2 C::*setFunction);
template<class C, typename F1, typename F2>
void bindClassMemberField(const std::string& className, const std::string& fieldName, F1 C::*getFunction, F2 C::*setFunction);
template<class C, typename F> template<class C, typename F>
void bindClassMemberGetField(const std::string& fieldName, F C::*getFunction); void bindClassMemberGetField(const std::string& fieldName, F C::*getFunction);
template<class C, typename F>
void bindClassMemberGetField(const std::string& className, const std::string& fieldName, F C::*getFunction);
template<class C, typename F> template<class C, typename F>
void bindClassMemberSetField(const std::string& fieldName, F C::*setFunction); void bindClassMemberSetField(const std::string& fieldName, F C::*setFunction);
template<class C, typename F>
void bindClassMemberSetField(const std::string& className, const std::string& fieldName, F C::*setFunction);
template<typename F> template<typename F>
void bindGlobalFunction(const std::string& functionName, const F& function); void bindGlobalFunction(const std::string& functionName, const F& function);
@ -314,31 +325,45 @@ template<class C, typename F>
void LuaInterface::bindClassStaticFunction(const std::string& functionName, const F& function) { void LuaInterface::bindClassStaticFunction(const std::string& functionName, const F& function) {
registerClassStaticFunction<C>(functionName, luabinder::bind_fun(function)); registerClassStaticFunction<C>(functionName, luabinder::bind_fun(function));
} }
template<typename F>
void LuaInterface::bindClassStaticFunction(const std::string& className, const std::string& functionName, const F& function) {
registerClassStaticFunction(className, functionName, luabinder::bind_fun(function));
}
template<class C, typename F> template<class C, typename F>
void LuaInterface::bindClassMemberFunction(const std::string& functionName, F C::*function) { void LuaInterface::bindClassMemberFunction(const std::string& functionName, F C::*function) {
registerClassMemberFunction<C>(functionName, luabinder::bind_mem_fun(function)); registerClassMemberFunction<C>(functionName, luabinder::bind_mem_fun(function));
} }
template<class C, typename F>
void LuaInterface::bindClassMemberFunction(const std::string& className, const std::string& functionName, F C::*function) {
registerClassMemberFunction(className, functionName, luabinder::bind_mem_fun(function));
}
template<class C, typename F1, typename F2> template<class C, typename F1, typename F2>
void LuaInterface::bindClassMemberField(const std::string& fieldName, F1 C::*getFunction, F2 C::*setFunction) { void LuaInterface::bindClassMemberField(const std::string& fieldName, F1 C::*getFunction, F2 C::*setFunction) {
registerClassMemberField<C>(fieldName, registerClassMemberField<C>(fieldName, luabinder::bind_mem_fun(getFunction), luabinder::bind_mem_fun(setFunction));
luabinder::bind_mem_fun(getFunction), }
luabinder::bind_mem_fun(setFunction)); template<class C, typename F1, typename F2>
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<class C, typename F> template<class C, typename F>
void LuaInterface::bindClassMemberGetField(const std::string& fieldName, F C::*getFunction) { void LuaInterface::bindClassMemberGetField(const std::string& fieldName, F C::*getFunction) {
registerClassMemberField<C>(fieldName, registerClassMemberField<C>(fieldName, luabinder::bind_mem_fun(getFunction), LuaCppFunction());
luabinder::bind_mem_fun(getFunction), }
LuaCppFunction()); template<class C, typename F>
void LuaInterface::bindClassMemberGetField(const std::string& className, const std::string& fieldName, F C::*getFunction) {
registerClassMemberField(className, fieldName, luabinder::bind_mem_fun(getFunction), LuaCppFunction());
} }
template<class C, typename F> template<class C, typename F>
void LuaInterface::bindClassMemberSetField(const std::string& fieldName, F C::*setFunction) { void LuaInterface::bindClassMemberSetField(const std::string& fieldName, F C::*setFunction) {
registerClassMemberField<C>(fieldName, registerClassMemberField<C>(fieldName, LuaCppFunction(), luabinder::bind_mem_fun(setFunction));
LuaCppFunction(), }
luabinder::bind_mem_fun(setFunction)); template<class C, typename F>
void LuaInterface::bindClassMemberSetField(const std::string& className, const std::string& fieldName, F C::*setFunction) {
registerClassMemberField(className, fieldName, LuaCppFunction(), luabinder::bind_mem_fun(setFunction));
} }
template<typename F> template<typename F>

@ -0,0 +1,118 @@
/*
* Copyright (c) 2010-2011 OTClient <https://github.com/edubart/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 <csignal>
#include <execinfo.h>
#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

@ -1,86 +0,0 @@
/*
* Copyright (c) 2010-2011 OTClient <https://github.com/edubart/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 <framework/global.h>
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

@ -1,63 +0,0 @@
/*
* Copyright (c) 2010-2011 OTClient <https://github.com/edubart/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 <framework/global.h>
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

@ -20,5 +20,14 @@
* THE SOFTWARE. * 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;

@ -0,0 +1,95 @@
/*
* Copyright (c) 2010-2011 OTClient <https://github.com/edubart/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 <framework/global.h>
#include <framework/core/inputevent.h>
class PlatformWindow
{
typedef std::function<void(const Size&)> OnResizeCallback;
typedef std::function<void(const InputEvent&)> 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<false> m_created;
Boolean<false> m_visible;
Boolean<false> m_focused;
Boolean<false> m_fullscreen;
SimpleCallback m_onClose;
OnResizeCallback m_onResize;
OnInputEventCallback m_onInputEvent;
};
extern PlatformWindow& g_window;
#endif

@ -20,6 +20,10 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
// disabled for a while
#if 0
#include "win32window.h"
#include "platform.h" #include "platform.h"
#include "platformlistener.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); MessageBoxA(NULL, message.c_str(), "Fatal Error", MB_OK | MB_ICONERROR);
} }
#endif

@ -20,14 +20,14 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#ifndef ENGINE_H #ifndef WIN32WINDOW_H
#define ENGINE_H #define WIN32WINDOW_H
#include "declarations.h" #include "platformwindow.h"
class Engine class WIN32Window : public PlatformWindow
{ {
//TODO
}; };
#endif #endif

@ -1,989 +0,0 @@
/*
* Copyright (c) 2010-2011 OTClient <https://github.com/edubart/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 <sys/time.h>
#include <sys/stat.h>
#include <errno.h>
#include <physfs.h>
#include <GL/glx.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <framework/thirdparty/apngloader.h>
#include <framework/core/resourcemanager.h>
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<int, Fw::Key> keyMap;
PlatformListener* listener;
PlatformEvent platformEvent;
} x11;
Platform g_platform;
#ifdef HANDLE_EXCEPTIONS
#include <csignal>
#include <execinfo.h>
#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<unsigned long int> 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<numItems; ++i) {
if(atoms[i] == x11.atomWindowMaximizedVert)
maximized |= 1;
else if(atoms[i] == x11.atomWindowMaximizedHorz)
maximized |= 2;
}
if(maximized == 3)
ret = true;
XFree(propertyValue);
}
return ret;
}
std::string Platform::getAppUserDir()
{
std::stringstream sdir;
sdir << PHYSFS_getUserDir() << "." << x11.appName;
if((mkdir(sdir.str().c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) && (errno != EEXIST))
logError("Couldn't create directory for saving configuration file. (",sdir.str(),")");
return sdir.str();
}
void Platform::displayFatalError(const std::string& message)
{
// nothing to do
}

@ -0,0 +1,887 @@
/*
* Copyright (c) 2010-2011 OTClient <https://github.com/edubart/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 "x11window.h"
#include <framework/core/resourcemanager.h>
#include <framework/thirdparty/apngloader.h>
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<unsigned long int> 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<numItems; ++i) {
if(atoms[i] == wmStateMaximizedVert)
maximizedMask |= 1;
else if(atoms[i] == wmStateMaximizedHorz)
maximizedMask |= 2;
}
if(maximizedMask == 3)
maximizedMask = true;
XFree(propertyValue);
}
return maximized;
}

@ -0,0 +1,106 @@
/*
* Copyright (c) 2010-2011 OTClient <https://github.com/edubart/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 X11WINDOW_H
#define X11WINDOW_H
#include "platformwindow.h"
#include <framework/graphics/glutil.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#ifndef OPENGL_ES2
#include <GL/glx.h>
#else
#include <EGL/egl.h>
#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<int, Fw::Key> m_keyMap;
#ifndef OPENGL_ES2
GLXContext m_glxContext;
#else
EGLConfig m_eglConfig;
EGLContext m_eglContext;
EGLDisplay m_eglDisplay;
EGLSurface m_eglSurface;
#endif
};
#endif

@ -40,13 +40,12 @@ void UIFrameCounter::render()
{ {
UIWidget::render(); UIWidget::render();
int now = g_clock.ticks(); if(g_clock.ticksElapsed(m_lastFrameTicks) >= 1000) {
if(now - m_lastFrameTicks >= 1000) {
m_fpsText = Fw::mkstr("FPS: ", m_frameCount); m_fpsText = Fw::mkstr("FPS: ", m_frameCount);
m_lastFrameTicks = now; m_lastFrameTicks = g_clock.ticks();
m_frameCount = 0; m_frameCount = 0;
} else }
m_frameCount++; m_frameCount++;
m_font->renderText(m_fpsText, m_rect, m_align, Fw::white); m_font->renderText(m_fpsText, m_rect, m_align, Fw::white);
} }

@ -41,7 +41,7 @@ protected:
private: private:
Fw::AlignmentFlag m_align; Fw::AlignmentFlag m_align;
int m_frameCount; int m_frameCount;
int m_lastFrameTicks; ticks_t m_lastFrameTicks;
std::string m_fpsText; std::string m_fpsText;
}; };

@ -23,7 +23,7 @@
#include "uilineedit.h" #include "uilineedit.h"
#include <framework/graphics/font.h> #include <framework/graphics/font.h>
#include <framework/graphics/graphics.h> #include <framework/graphics/graphics.h>
#include <framework/platform/platform.h> #include <framework/platform/platformwindow.h>
#include <framework/core/clock.h> #include <framework/core/clock.h>
#include <framework/otml/otmlnode.h> #include <framework/otml/otmlnode.h>
@ -56,8 +56,7 @@ void UILineEdit::render()
assert(m_cursorPos <= textLength); assert(m_cursorPos <= textLength);
// draw every 333ms // draw every 333ms
const int delay = 333; const int delay = 333;
int ticks = g_clock.ticks(); if(g_clock.ticksElapsed(m_cursorTicks) <= delay) {
if(ticks - m_cursorTicks <= delay) {
Rect cursorRect; Rect cursorRect;
// when cursor is at 0 or is the first visible element // when cursor is at 0 or is the first visible element
if(m_cursorPos == 0 || m_cursorPos == m_startRenderPos) if(m_cursorPos == 0 || m_cursorPos == m_startRenderPos)
@ -65,8 +64,8 @@ void UILineEdit::render()
else else
cursorRect = Rect(m_glyphsCoords[m_cursorPos-1].right(), m_glyphsCoords[m_cursorPos-1].top(), 1, m_font->getGlyphHeight()); cursorRect = Rect(m_glyphsCoords[m_cursorPos-1].right(), m_glyphsCoords[m_cursorPos-1].top(), 1, m_font->getGlyphHeight());
g_graphics.drawFilledRect(cursorRect); g_graphics.drawFilledRect(cursorRect);
} else if(ticks - m_cursorTicks >= 2*delay) { } else if(g_clock.ticksElapsed(m_cursorTicks) >= 2*delay) {
m_cursorTicks = ticks; 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 if(keyCode == Fw::KeyDelete) // erase right character
removeCharacter(true); 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 else if(keyCode == Fw::KeyEnd) // move cursor to last character
setCursorPos(m_text.length()); setCursorPos(m_text.length());
else if(keyCode == Fw::KeyV && keyboardModifiers == Fw::KeyboardCtrlModifier) else if(keyCode == Fw::KeyV && keyboardModifiers == Fw::KeyboardCtrlModifier)
appendText(g_platform.getClipboardText()); appendText(g_window.getClipboardText());
else if(keyCode == Fw::KeyTab) { else if(keyCode == Fw::KeyTab) {
if(!m_alwaysActive) { if(!m_alwaysActive) {
if(UIWidgetPtr parent = getParent()) if(UIWidgetPtr parent = getParent())
parent->focusNextChild(Fw::TabFocusReason); parent->focusNextChild(Fw::TabFocusReason);
} }
} else if(keyChar != 0) } else if(!keyText.empty())
appendCharacter(keyChar); appendText(keyText);
else else
return false; return false;

@ -56,7 +56,7 @@ protected:
virtual void onStyleApply(const OTMLNodePtr& styleNode); virtual void onStyleApply(const OTMLNodePtr& styleNode);
virtual void onGeometryUpdate(const Rect& oldRect, const Rect& newRect); virtual void onGeometryUpdate(const Rect& oldRect, const Rect& newRect);
virtual void onFocusChange(bool focused, Fw::FocusReason reason); 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); virtual bool onMousePress(const Point& mousePos, Fw::MouseButton button);
private: private:
@ -68,7 +68,7 @@ private:
int m_cursorPos; int m_cursorPos;
Point m_startInternalPos; Point m_startInternalPos;
int m_startRenderPos; int m_startRenderPos;
int m_cursorTicks; ticks_t m_cursorTicks;
int m_textHorizontalMargin; int m_textHorizontalMargin;
bool m_textHidden; bool m_textHidden;
bool m_alwaysActive; bool m_alwaysActive;

@ -50,56 +50,32 @@ void UIManager::render()
void UIManager::resize(const Size& size) 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 switch(event.type) {
if(m_rootWidget) { case Fw::KeyPressInputEvent:
if(event.type & EventKeyboardAction) { m_rootWidget->onKeyPress(event.keyCode, event.keyText, event.keyboardModifiers);
int keyboardModifiers = Fw::KeyboardNoModifier; break;
if(event.ctrl) case Fw::KeyReleaseInputEvent:
keyboardModifiers |= Fw::KeyboardCtrlModifier; m_rootWidget->onKeyRelease(event.keyCode, event.keyText, event.keyboardModifiers);
if(event.shift) break;
keyboardModifiers |= Fw::KeyboardShiftModifier; case Fw::MousePressInputEvent:
if(event.alt) m_rootWidget->onMousePress(event.mousePos, event.mouseButton);
keyboardModifiers |= Fw::KeyboardAltModifier; break;
case Fw::MouseReleaseInputEvent:
if(event.type == EventKeyDown) m_rootWidget->onMouseRelease(event.mousePos, event.mouseButton);
m_rootWidget->onKeyPress(event.keycode, event.keychar, keyboardModifiers); break;
else case Fw::MouseMoveInputEvent:
m_rootWidget->onKeyRelease(event.keycode, event.keychar, keyboardModifiers); m_rootWidget->updateState(Fw::HoverState);
} else if(event.type & EventMouseAction) { m_rootWidget->onMouseMove(event.mousePos, event.mouseMoved);
if(event.type == EventMouseMove) { break;
m_rootWidget->updateState(Fw::HoverState); case Fw::MouseWheelInputEvent:
m_rootWidget->onMouseMove(event.mousePos, event.mouseMoved); m_rootWidget->onMouseWheel(event.mousePos, event.wheelDirection);
} break;
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);
}
}
}
} }
bool UIManager::importStyles(const std::string& file) bool UIManager::importStyles(const std::string& file)

@ -24,7 +24,7 @@
#define UIMANAGER_H #define UIMANAGER_H
#include "declarations.h" #include "declarations.h"
#include <framework/platform/platformevent.h> #include <framework/core/inputevent.h>
#include <framework/otml/declarations.h> #include <framework/otml/declarations.h>
class UIManager class UIManager
@ -35,7 +35,7 @@ public:
void render(); void render();
void resize(const Size& size); void resize(const Size& size);
void inputEvent(const PlatformEvent& event); void inputEvent(const InputEvent& event);
bool importStyles(const std::string& file); bool importStyles(const std::string& file);
void importStyleFromOTML(const OTMLNodePtr& styleNode); void importStyleFromOTML(const OTMLNodePtr& styleNode);

@ -32,7 +32,7 @@
#include <framework/graphics/fontmanager.h> #include <framework/graphics/fontmanager.h>
#include <framework/otml/otmlnode.h> #include <framework/otml/otmlnode.h>
#include <framework/graphics/graphics.h> #include <framework/graphics/graphics.h>
#include <framework/platform/platform.h> #include <framework/platform/platformwindow.h>
UIWidget::UIWidget() UIWidget::UIWidget()
{ {
@ -670,7 +670,7 @@ void UIWidget::updateState(Fw::WidgetState state)
} }
else if(state == Fw::HoverState) { else if(state == Fw::HoverState) {
updateChildren = true; updateChildren = true;
Point mousePos = g_platform.getMouseCursorPos(); Point mousePos = g_window.getMousePos();
UIWidgetPtr widget = asUIWidget(); UIWidgetPtr widget = asUIWidget();
UIWidgetPtr parent; UIWidgetPtr parent;
do { do {
@ -971,9 +971,9 @@ void UIWidget::onHoverChange(bool hovered)
g_ui.getRootWidget()->updateState(Fw::HoverState); 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<bool>("onKeyPress", keyCode, keyChar, keyboardModifiers)) if(callLuaField<bool>("onKeyPress", keyCode, keyText, keyboardModifiers))
return true; return true;
// do a backup of children list, because it may change while looping it // 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) { for(const UIWidgetPtr& child : children) {
if(child->onKeyPress(keyCode, keyChar, keyboardModifiers)) if(child->onKeyPress(keyCode, keyText, keyboardModifiers))
return true; return true;
} }
return false; return false;
} }
bool UIWidget::onKeyRelease(uchar keyCode, char keyChar, int keyboardModifiers) bool UIWidget::onKeyRelease(uchar keyCode, std::string keyText, int keyboardModifiers)
{ {
if(callLuaField<bool>("onKeyRelease", keyCode, keyChar, keyboardModifiers)) if(callLuaField<bool>("onKeyRelease", keyCode, keyText, keyboardModifiers))
return true; return true;
// do a backup of children list, because it may change while looping it // 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) { for(const UIWidgetPtr& child : children) {
if(child->onKeyRelease(keyCode, keyChar, keyboardModifiers)) if(child->onKeyRelease(keyCode, keyText, keyboardModifiers))
return true; return true;
} }

@ -166,9 +166,9 @@ protected:
/// Triggered when the mouse enters or leaves widget area /// Triggered when the mouse enters or leaves widget area
virtual void onHoverChange(bool hovered); virtual void onHoverChange(bool hovered);
/// Triggered when user presses key while widget has focus /// 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 /// 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 /// Triggered when a mouse button is pressed down while mouse pointer is inside widget area
virtual bool onMousePress(const Point& mousePos, Fw::MouseButton button); virtual bool onMousePress(const Point& mousePos, Fw::MouseButton button);
/// Triggered when a mouse button is released /// Triggered when a mouse button is released

@ -146,7 +146,7 @@ bool UIWindow::onMouseMove(const Point& mousePos, const Point& mouseMoved)
return UIWidget::onMouseMove(mousePos, 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(keyboardModifiers == Fw::KeyboardNoModifier) {
if(keyCode == Fw::KeyReturn || keyCode == Fw::KeyEnter) { if(keyCode == Fw::KeyReturn || keyCode == Fw::KeyEnter) {
@ -157,5 +157,5 @@ bool UIWindow::onKeyPress(uchar keyCode, char keyChar, int keyboardModifiers)
return true; return true;
} }
} }
return UIWidget::onKeyPress(keyCode, keyChar, keyboardModifiers); return UIWidget::onKeyPress(keyCode, keyText, keyboardModifiers);
} }

@ -46,7 +46,7 @@ protected:
virtual bool onMousePress(const Point& mousePos, Fw::MouseButton button); virtual bool onMousePress(const Point& mousePos, Fw::MouseButton button);
virtual void onMouseRelease(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 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: private:
std::string m_title; std::string m_title;

@ -35,10 +35,13 @@ typedef uint64_t uint64;
typedef uint32_t uint32; typedef uint32_t uint32;
typedef uint16_t uint16; typedef uint16_t uint16;
typedef uint8_t uint8; typedef uint8_t uint8;
typedef int64_t int64;
typedef int32_t int32; typedef int32_t int32;
typedef int16_t int16; typedef int16_t int16;
typedef int8_t int8; typedef int8_t int8;
typedef int64 ticks_t;
typedef std::function<void()> SimpleCallback; typedef std::function<void()> SimpleCallback;
typedef std::function<bool()> BooleanCallback; typedef std::function<bool()> BooleanCallback;

@ -21,39 +21,12 @@
*/ */
#include <otclient/otclient.h> #include <otclient/otclient.h>
#include <framework/core/eventdispatcher.h>
#include <csignal>
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 <windows.h>
int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow)
{
std::vector<std::string> args;
boost::split(args, lpszArgument, boost::is_any_of(std::string(" ")));
#else
int main(int argc, const char* argv[]) int main(int argc, const char* argv[])
{ {
std::vector<std::string> args; std::vector<std::string> args(argv, argv + argc);
for(int i=0; i<argc; ++i) g_otclient.init(args);
args.push_back(argv[i]); g_otclient.run();
#endif g_otclient.terminate();
g_client.init(args);
g_client.run();
g_client.terminate();
return 0; return 0;
} }

@ -28,7 +28,7 @@
namespace Otc namespace Otc
{ {
static const char* AppName = "OTClient"; 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* AppVersion = "0.4.0";
static const char* CipsoftPublicRSA = "1321277432058722840622950990822933849527763264961655079678763618" static const char* CipsoftPublicRSA = "1321277432058722840622950990822933849527763264961655079678763618"

@ -207,7 +207,7 @@ void Creature::walk(const Position& position, bool inverse)
void Creature::updateWalk() void Creature::updateWalk()
{ {
if(m_walking) { 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); int totalPixelsWalked = std::min((int)round(elapsedTicks / m_walkTimePerPixel), 32);
if(m_inverseWalking) { if(m_inverseWalking) {

@ -88,7 +88,7 @@ protected:
FontPtr m_informationFont; FontPtr m_informationFont;
Color m_informationColor; Color m_informationColor;
int m_walkStartTicks; ticks_t m_walkStartTicks;
bool m_walking, m_inverseWalking; bool m_walking, m_inverseWalking;
float m_walkTimePerPixel; float m_walkTimePerPixel;
Position m_walkingFromPosition; Position m_walkingFromPosition;

@ -45,7 +45,7 @@ public:
EffectPtr asEffect() { return std::static_pointer_cast<Effect>(shared_from_this()); } EffectPtr asEffect() { return std::static_pointer_cast<Effect>(shared_from_this()); }
private: private:
int m_animationStartTicks; ticks_t m_animationStartTicks;
}; };
#endif #endif

@ -27,17 +27,6 @@
Game g_game; 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) 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); m_protocolGame = ProtocolGamePtr(new ProtocolGame);
@ -51,12 +40,13 @@ void Game::cancelLogin()
void Game::logout(bool force) void Game::logout(bool force)
{ {
if(m_protocolGame) { if(!m_protocolGame || !m_online)
m_protocolGame->sendLogout(); return;
if(force) m_protocolGame->sendLogout();
processLogout();
} if(force)
processLogout();
} }
void Game::processLoginError(const std::string& error) void Game::processLoginError(const std::string& error)

@ -31,9 +31,6 @@
class Game class Game
{ {
public: public:
void init();
void terminate();
// login/logout related // login/logout related
void loginWorld(const std::string& account, void loginWorld(const std::string& account,
const std::string& password, const std::string& password,

@ -47,7 +47,7 @@ public:
private: private:
int m_data; int m_data;
int m_lastTicks; ticks_t m_lastTicks;
}; };
#endif #endif

@ -46,7 +46,7 @@ public:
MissilePtr asMissile() { return std::static_pointer_cast<Missile>(shared_from_this()); } MissilePtr asMissile() { return std::static_pointer_cast<Missile>(shared_from_this()); }
private: private:
int m_startTicks; ticks_t m_startTicks;
Position m_positionDelta; Position m_positionDelta;
float m_duration; float m_duration;
}; };

@ -1,27 +1,6 @@
/* #include "otclient.h"
* Copyright (c) 2010-2011 OTClient <https://github.com/edubart/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/otclient.h>
#include <framework/luascript/luainterface.h> #include <framework/luascript/luainterface.h>
#include <otclient/luascript/luavaluecasts.h>
#include <otclient/core/game.h> #include <otclient/core/game.h>
#include <otclient/core/tile.h> #include <otclient/core/tile.h>
#include <otclient/core/item.h> #include <otclient/core/item.h>
@ -39,12 +18,11 @@
#include <otclient/ui/uigame.h> #include <otclient/ui/uigame.h>
#include <otclient/core/outfit.h> #include <otclient/core/outfit.h>
#include "luavaluecasts.h"
void OTClient::registerLuaFunctions() void OTClient::registerLuaFunctions()
{ {
g_lua.bindGlobalFunction("exit", std::bind(&OTClient::exit, &g_client)); Application::registerLuaFunctions();
g_lua.bindGlobalFunction("setOnClose", std::bind(&OTClient::setOnClose, &g_client, _1));
g_lua.bindGlobalFunction("exit", std::bind(&Application::exit, &g_app));
g_lua.bindGlobalFunction("importDat", std::bind(&ThingsType::load, &g_thingsType, _1)); 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("importSpr", std::bind(&SpriteManager::load, &g_sprites, _1));
g_lua.bindGlobalFunction("getOufitColor", Outfit::getColor); g_lua.bindGlobalFunction("getOufitColor", Outfit::getColor);

@ -35,5 +35,4 @@ bool luavalue_cast(int index, Outfit& outfit);
void push_luavalue(const Position& pos); void push_luavalue(const Position& pos);
bool luavalue_cast(int index, Position& pos); bool luavalue_cast(int index, Position& pos);
#endif
#endif

@ -251,7 +251,7 @@ void ProtocolGame::parseMessage(InputMessage& msg)
//case Otc::GameServerObjectInfo: //case Otc::GameServerObjectInfo:
//case Otc::GameServerPlayerInventory: //case Otc::GameServerPlayerInventory:
default: default:
Fw::throwException("unknown opt byte ", opt); Fw::throwException("unknown opt byte ", (int)opt);
break; break;
} }
} }

@ -1,304 +1,10 @@
/*
* Copyright (c) 2010-2011 OTClient <https://github.com/edubart/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 "otclient.h"
#include <framework/core/modulemanager.h> OTClient g_otclient;
#include <framework/core/configmanager.h> Application& g_app = g_otclient;
#include <framework/core/resourcemanager.h>
#include <framework/core/eventdispatcher.h>
#include <framework/core/clock.h>
#include <framework/luascript/luainterface.h>
#include <framework/graphics/graphics.h>
#include <framework/graphics/fontmanager.h>
#include <framework/ui/uimanager.h>
#include <framework/ui/uiwidget.h>
#include <framework/net/connection.h>
#include <framework/platform/platform.h>
#include <otclient/net/protocolgame.h>
#include <otclient/core/game.h>
#include <otclient/core/map.h>
OTClient g_client;
void OTClient::init(std::vector<std::string> args) void OTClient::init(const std::vector<std::string>& args)
{ {
m_running = false;
m_stopping = false;
// print client information
logInfo(Otc::AppName, " ", Otc::AppVersion); logInfo(Otc::AppName, " ", Otc::AppVersion);
Application::init(Otc::AppName, args);
// 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);
} }

@ -1,79 +1,16 @@
/*
* Copyright (c) 2010-2011 OTClient <https://github.com/edubart/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 #ifndef OTCLIENT_H
#define OTCLIENT_H #define OTCLIENT_H
#include <framework/ui/declarations.h> #include <framework/application.h>
#include <framework/platform/platformlistener.h> #include <otclient/global.h>
class OTClient : public PlatformListener class OTClient : public Application
{ {
enum {
POLL_CYCLE_DELAY = 10
};
public: public:
/// Where everything begins... void init(const std::vector<std::string>& args);
void init(std::vector<std::string> args); void registerLuaFunctions();
/// 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;
}; };
extern OTClient g_client; extern OTClient g_otclient;
#endif #endif

@ -24,7 +24,7 @@
#include <otclient/core/game.h> #include <otclient/core/game.h>
#include <framework/ui/uilineedit.h> #include <framework/ui/uilineedit.h>
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<UILineEdit>(getParent()->recursiveGetChildById("chatLineEdit")); UILineEditPtr chatLineEdit = std::dynamic_pointer_cast<UILineEdit>(getParent()->recursiveGetChildById("chatLineEdit"));
@ -100,10 +100,10 @@ bool UIGame::onKeyPress(uchar keyCode, char keyChar, int keyboardModifiers)
} }
} }
if(keyChar != 0) { if(!keyText.empty()) {
chatLineEdit->appendText(std::string(1, keyChar)); chatLineEdit->appendText(keyText);
return true; return true;
} }
return UIWidget::onKeyPress(keyCode, keyChar, keyboardModifiers); return UIWidget::onKeyPress(keyCode, keyText, keyboardModifiers);
} }

@ -29,7 +29,7 @@
class UIGame : public UIWidget class UIGame : public UIWidget
{ {
protected: protected:
virtual bool onKeyPress(uchar keyCode, char keyChar, int keyboardModifiers); virtual bool onKeyPress(uchar keyCode, std::string keyText, int keyboardModifiers);
}; };

Loading…
Cancel
Save