mirror of
https://github.com/edubart/otclient.git
synced 2025-10-19 05:53:26 +02:00
merge total remake
This commit is contained in:
35
src/framework/core/configmanager.cpp
Normal file
35
src/framework/core/configmanager.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "configmanager.h"
|
||||
#include "resourcemanager.h"
|
||||
|
||||
#include <otml/otml.h>
|
||||
|
||||
ConfigManager g_configs;
|
||||
|
||||
bool ConfigManager::load(const std::string& fileName)
|
||||
{
|
||||
m_fileName = fileName;
|
||||
|
||||
if(!g_resources.fileExists(fileName))
|
||||
return false;
|
||||
|
||||
try {
|
||||
OTMLDocumentPtr doc = OTMLDocument::parse(fileName);
|
||||
for(const OTMLNodePtr& child : doc->childNodes())
|
||||
m_confsMap[child->tag()] = child->value();
|
||||
} catch(std::exception& e) {
|
||||
logError("ERROR: could not load configurations: ", e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConfigManager::save()
|
||||
{
|
||||
if(!m_fileName.empty()) {
|
||||
OTMLDocumentPtr doc = OTMLDocument::create();
|
||||
doc->write(m_confsMap);
|
||||
return doc->save(m_fileName);
|
||||
}
|
||||
return false;
|
||||
}
|
@@ -1,25 +1,25 @@
|
||||
#ifndef CONFIGS_H
|
||||
#define CONFIGS_H
|
||||
#ifndef CONFIGMANAGER_H
|
||||
#define CONFIGMANAGER_H
|
||||
|
||||
#include <global.h>
|
||||
|
||||
struct ConfigValueProxy {
|
||||
ConfigValueProxy(const std::string& v) : value(v) { }
|
||||
operator std::string() const { return convert<std::string>(value); }
|
||||
operator float() const { return convert<float>(value); }
|
||||
operator int() const { return convert<int>(value); }
|
||||
operator bool() const { return convert<bool>(value); }
|
||||
operator std::string() const { return aux::unsafe_cast<std::string>(value); }
|
||||
operator float() const { return aux::unsafe_cast<float>(value); }
|
||||
operator int() const { return aux::unsafe_cast<int>(value); }
|
||||
operator bool() const { return aux::unsafe_cast<bool>(value); }
|
||||
std::string value;
|
||||
};
|
||||
|
||||
class Configs
|
||||
class ConfigManager
|
||||
{
|
||||
public:
|
||||
bool load(const std::string& fileName);
|
||||
void save();
|
||||
bool save();
|
||||
|
||||
template<class T>
|
||||
void set(const std::string& key, const T& value) { m_confsMap[key] = convert<std::string>(value); }
|
||||
void set(const std::string& key, const T& value) { m_confsMap[key] = aux::unsafe_cast<std::string>(value); }
|
||||
|
||||
ConfigValueProxy get(const std::string& key) { return ConfigValueProxy(m_confsMap[key]); }
|
||||
|
||||
@@ -28,6 +28,6 @@ private:
|
||||
std::map<std::string, std::string> m_confsMap;
|
||||
};
|
||||
|
||||
extern Configs g_configs;
|
||||
extern ConfigManager g_configs;
|
||||
|
||||
#endif // CONFIGS_H
|
||||
#endif
|
@@ -1,37 +0,0 @@
|
||||
#include "configs.h"
|
||||
#include "resources.h"
|
||||
|
||||
#include <otml/otml.h>
|
||||
|
||||
Configs g_configs;
|
||||
|
||||
bool Configs::load(const std::string& fileName)
|
||||
{
|
||||
m_fileName = fileName;
|
||||
|
||||
if(!g_resources.fileExists(fileName))
|
||||
return false;
|
||||
|
||||
std::stringstream fin;
|
||||
if(!g_resources.loadFile(fileName, fin))
|
||||
return false;
|
||||
|
||||
try {
|
||||
OTMLParser parser(fin, fileName);
|
||||
parser.getDocument()->read(&m_confsMap);
|
||||
} catch(OTMLException e) {
|
||||
logError("ERROR: Malformed config file: ", e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Configs::save()
|
||||
{
|
||||
if(!m_fileName.empty()) {
|
||||
OTMLEmitter emitter;
|
||||
emitter.createDocument()->write(m_confsMap);
|
||||
g_resources.saveFile(m_fileName, emitter.emitDocument());
|
||||
}
|
||||
}
|
@@ -1,43 +0,0 @@
|
||||
#include "dispatcher.h"
|
||||
#include "engine.h"
|
||||
|
||||
Dispatcher g_dispatcher;
|
||||
|
||||
void Dispatcher::cleanup()
|
||||
{
|
||||
while(!m_scheduledTaskList.empty()) {
|
||||
ScheduledTask *task = m_scheduledTaskList.top();
|
||||
m_scheduledTaskList.pop();
|
||||
delete task;
|
||||
}
|
||||
}
|
||||
|
||||
void Dispatcher::poll()
|
||||
{
|
||||
while(!m_taskList.empty()) {
|
||||
m_taskList.front()();
|
||||
m_taskList.pop_front();
|
||||
}
|
||||
|
||||
while(!m_scheduledTaskList.empty()) {
|
||||
ScheduledTask *task = m_scheduledTaskList.top();
|
||||
if(g_engine.getCurrentFrameTicks() < task->ticks)
|
||||
break;
|
||||
m_scheduledTaskList.pop();
|
||||
task->callback();
|
||||
delete task;
|
||||
}
|
||||
}
|
||||
|
||||
void Dispatcher::scheduleTask(const std::function<void()>& callback, int delay)
|
||||
{
|
||||
m_scheduledTaskList.push(new ScheduledTask(g_engine.getCurrentFrameTicks() + delay, callback));
|
||||
}
|
||||
|
||||
void Dispatcher::addTask(const std::function<void()>& callback, bool pushFront)
|
||||
{
|
||||
if(pushFront)
|
||||
m_taskList.push_front(callback);
|
||||
else
|
||||
m_taskList.push_back(callback);
|
||||
}
|
@@ -1,43 +0,0 @@
|
||||
#ifndef DISPATCHER_H
|
||||
#define DISPATCHER_H
|
||||
|
||||
#include <global.h>
|
||||
#include <queue>
|
||||
|
||||
struct ScheduledTask {
|
||||
ScheduledTask(const std::function<void()>& _callback) : ticks(0), callback(_callback) { }
|
||||
ScheduledTask(int _ticks, const std::function<void()>& _callback) : ticks(_ticks), callback(_callback) { }
|
||||
bool operator<(const ScheduledTask& other) const { return ticks > other.ticks; }
|
||||
int ticks;
|
||||
std::function<void()> callback;
|
||||
};
|
||||
|
||||
struct lessScheduledTask : public std::binary_function<ScheduledTask*&, ScheduledTask*&, bool> {
|
||||
bool operator()(ScheduledTask*& t1,ScheduledTask*& t2) { return (*t1) < (*t2); }
|
||||
};
|
||||
|
||||
class Dispatcher
|
||||
{
|
||||
public:
|
||||
Dispatcher() { }
|
||||
|
||||
/// Cleanup scheduled events
|
||||
void cleanup();
|
||||
|
||||
/// Execute scheduled events
|
||||
void poll();
|
||||
|
||||
/// Add an event
|
||||
void addTask(const std::function<void()>& callback, bool pushFront = false);
|
||||
|
||||
/// Schedula an event
|
||||
void scheduleTask(const std::function<void()>& callback, int delay);
|
||||
|
||||
private:
|
||||
std::list<std::function<void()>> m_taskList;
|
||||
std::priority_queue<ScheduledTask*, std::vector<ScheduledTask*>, lessScheduledTask> m_scheduledTaskList;
|
||||
};
|
||||
|
||||
extern Dispatcher g_dispatcher;
|
||||
|
||||
#endif // DISPATCHER_H
|
@@ -1,155 +0,0 @@
|
||||
#include "engine.h"
|
||||
#include "platform.h"
|
||||
#include "dispatcher.h"
|
||||
#include <graphics/graphics.h>
|
||||
#include <graphics/fonts.h>
|
||||
#include <graphics/textures.h>
|
||||
#include <ui/uicontainer.h>
|
||||
#include <ui/uiskins.h>
|
||||
#include <script/luainterface.h>
|
||||
#include <net/connection.h>
|
||||
#include <../game.h>
|
||||
#include <../item.h>
|
||||
|
||||
Engine g_engine;
|
||||
|
||||
void Engine::init()
|
||||
{
|
||||
// initialize stuff
|
||||
g_graphics.init();
|
||||
g_fonts.init();
|
||||
g_lua.init();
|
||||
}
|
||||
|
||||
void Engine::terminate()
|
||||
{
|
||||
// destroy root ui
|
||||
UIContainer::getRoot()->destroy();
|
||||
|
||||
// cleanup script stuff
|
||||
g_lua.terminate();
|
||||
|
||||
// poll remaning events
|
||||
g_engine.poll();
|
||||
|
||||
// terminate stuff
|
||||
g_fonts.terminate();
|
||||
g_graphics.terminate();
|
||||
g_dispatcher.cleanup();
|
||||
}
|
||||
|
||||
void Engine::poll()
|
||||
{
|
||||
// poll platform events
|
||||
Platform::poll();
|
||||
|
||||
// poll diaptcher tasks
|
||||
g_dispatcher.poll();
|
||||
|
||||
// poll network events
|
||||
Connection::poll();
|
||||
}
|
||||
|
||||
void Engine::run()
|
||||
{
|
||||
// check if root container has elements
|
||||
const UIContainerPtr& rootContainer = UIContainer::getRoot();
|
||||
if(rootContainer->getChildCount() == 0)
|
||||
logFatal("FATAL ERROR: no ui loaded at all, no reason to continue running");
|
||||
|
||||
std::string fpsText;
|
||||
Size fpsTextSize;
|
||||
FontPtr defaultFont = g_uiSkins.getDefaultFont();
|
||||
|
||||
m_lastFrameTicks = Platform::getTicks();
|
||||
int lastFpsTicks = m_lastFrameTicks;
|
||||
int frameCount = 0;
|
||||
int fps = 0;
|
||||
m_running = true;
|
||||
|
||||
while(!m_stopping) {
|
||||
m_lastFrameTicks = Platform::getTicks();
|
||||
|
||||
poll();
|
||||
|
||||
// render only when visible
|
||||
if(Platform::isWindowVisible()) {
|
||||
// calculate fps
|
||||
if(m_calculateFps) {
|
||||
frameCount++;
|
||||
if(m_lastFrameTicks - lastFpsTicks >= 1000) {
|
||||
lastFpsTicks = m_lastFrameTicks;
|
||||
fps = frameCount;
|
||||
frameCount = 0;
|
||||
|
||||
// update fps text
|
||||
fpsText = make_string("FPS: ", fps);
|
||||
fpsTextSize = defaultFont->calculateTextRectSize(fpsText);
|
||||
}
|
||||
}
|
||||
|
||||
// render
|
||||
g_graphics.beginRender();
|
||||
|
||||
rootContainer->render();
|
||||
|
||||
// todo remove. render map
|
||||
g_game.getMap()->draw(0, 0);
|
||||
|
||||
// todo remove. view items
|
||||
static Item *item = NULL;
|
||||
if(!item) {
|
||||
item = new Item();
|
||||
item->setId(8377);
|
||||
}
|
||||
//item->draw(1, 1, 7);
|
||||
|
||||
// render fps
|
||||
if(m_calculateFps)
|
||||
defaultFont->renderText(fpsText, Point(g_graphics.getScreenSize().width() - fpsTextSize.width() - 10, 10));
|
||||
|
||||
g_graphics.endRender();
|
||||
|
||||
// swap buffers
|
||||
Platform::swapBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
m_stopping = false;
|
||||
m_running = false;
|
||||
}
|
||||
|
||||
void Engine::stop()
|
||||
{
|
||||
m_stopping = true;
|
||||
}
|
||||
|
||||
void Engine::onClose()
|
||||
{
|
||||
g_lua.getGlobal("onClose")->call("onClose");
|
||||
}
|
||||
|
||||
void Engine::onResize(const Size& size)
|
||||
{
|
||||
g_graphics.resize(size);
|
||||
UIContainer::getRoot()->setSize(size);
|
||||
}
|
||||
|
||||
void Engine::onInputEvent(const InputEvent& event)
|
||||
{
|
||||
UIContainer::getRoot()->onInputEvent(event);
|
||||
|
||||
ProtocolGame *protocol = g_game.getProtocol();
|
||||
if(protocol) {
|
||||
if(event.type == EV_KEY_DOWN) {
|
||||
if(event.keycode == KC_UP)
|
||||
protocol->sendWalkNorth();
|
||||
if(event.keycode == KC_RIGHT)
|
||||
protocol->sendWalkEast();
|
||||
if(event.keycode == KC_DOWN)
|
||||
protocol->sendWalkSouth();
|
||||
if(event.keycode == KC_LEFT)
|
||||
protocol->sendWalkWest();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,53 +0,0 @@
|
||||
#ifndef ENGINE_H
|
||||
#define ENGINE_H
|
||||
|
||||
#include <global.h>
|
||||
#include "input.h"
|
||||
|
||||
class Engine
|
||||
{
|
||||
public:
|
||||
Engine() : m_stopping(false),
|
||||
m_running(false),
|
||||
m_calculateFps(false) { }
|
||||
|
||||
void init();
|
||||
void terminate();
|
||||
|
||||
/// Poll events
|
||||
void poll();
|
||||
/// Main loop
|
||||
void run();
|
||||
|
||||
/// Stops main loop
|
||||
void stop();
|
||||
|
||||
/// Change current game state
|
||||
bool isRunning() const { return m_running; }
|
||||
bool isStopping() const { return m_stopping; }
|
||||
|
||||
/// Fired by platform on window close
|
||||
void onClose();
|
||||
/// Fired by platform on window resize
|
||||
void onResize(const Size& size);
|
||||
/// Fired by platform on mouse/keyboard input
|
||||
void onInputEvent(const InputEvent& event);
|
||||
|
||||
/// Enable FPS counter on screen
|
||||
void enableFpsCounter(bool enable = true) { m_calculateFps = enable; };
|
||||
|
||||
/// Return the current ticks on this frame
|
||||
int getCurrentFrameTicks() const { return m_lastFrameTicks; }
|
||||
|
||||
private:
|
||||
bool m_stopping;
|
||||
bool m_running;
|
||||
bool m_calculateFps;
|
||||
|
||||
int m_lastFrameTicks;
|
||||
};
|
||||
|
||||
extern Engine g_engine;
|
||||
|
||||
#endif // ENGINE_H
|
||||
|
45
src/framework/core/eventdispatcher.cpp
Normal file
45
src/framework/core/eventdispatcher.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "eventdispatcher.h"
|
||||
#include <core/platform.h>
|
||||
|
||||
EventDispatcher g_dispatcher;
|
||||
|
||||
void EventDispatcher::init()
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
void EventDispatcher::terminate()
|
||||
{
|
||||
// clean scheduled events
|
||||
while(!m_scheduledEventList.empty())
|
||||
m_scheduledEventList.pop();
|
||||
}
|
||||
|
||||
void EventDispatcher::poll()
|
||||
{
|
||||
while(!m_eventList.empty()) {
|
||||
m_eventList.front()();
|
||||
m_eventList.pop_front();
|
||||
}
|
||||
|
||||
while(!m_scheduledEventList.empty()) {
|
||||
if(g_platform.getTicks() < m_scheduledEventList.top().ticks)
|
||||
break;
|
||||
SimpleCallback callback = std::move(m_scheduledEventList.top().callback);
|
||||
m_scheduledEventList.pop();
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::scheduleEvent(const SimpleCallback& callback, int delay)
|
||||
{
|
||||
m_scheduledEventList.push(ScheduledEvent(g_platform.getTicks() + delay, callback));
|
||||
}
|
||||
|
||||
void EventDispatcher::addEvent(const SimpleCallback& callback, bool pushFront)
|
||||
{
|
||||
if(pushFront)
|
||||
m_eventList.push_front(callback);
|
||||
else
|
||||
m_eventList.push_back(callback);
|
||||
}
|
38
src/framework/core/eventdispatcher.h
Normal file
38
src/framework/core/eventdispatcher.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef EVENTDISPATCHER_H
|
||||
#define EVENTDISPATCHER_H
|
||||
|
||||
#include <global.h>
|
||||
|
||||
struct ScheduledEvent {
|
||||
ScheduledEvent(int ticks, const SimpleCallback& callback) : ticks(ticks), callback(callback) { }
|
||||
bool operator<(const ScheduledEvent& other) const { return ticks > other.ticks; }
|
||||
int ticks;
|
||||
SimpleCallback callback;
|
||||
};
|
||||
|
||||
class EventDispatcher
|
||||
{
|
||||
public:
|
||||
/// Initialize dispatcher
|
||||
void init();
|
||||
|
||||
/// Cleanup scheduled events
|
||||
void terminate();
|
||||
|
||||
/// Execute scheduled events
|
||||
void poll();
|
||||
|
||||
/// Add an event
|
||||
void addEvent(const SimpleCallback& callback, bool pushFront = false);
|
||||
|
||||
/// Schedule an event
|
||||
void scheduleEvent(const SimpleCallback& callback, int delay);
|
||||
|
||||
private:
|
||||
std::list<SimpleCallback> m_eventList;
|
||||
std::priority_queue<ScheduledEvent> m_scheduledEventList;
|
||||
};
|
||||
|
||||
extern EventDispatcher g_dispatcher;
|
||||
|
||||
#endif
|
@@ -1,10 +1,9 @@
|
||||
#ifndef INPUT_H
|
||||
#define INPUT_H
|
||||
#ifndef INPUTEVENT_H
|
||||
#define INPUTEVENT_H
|
||||
|
||||
#include <util/types.h>
|
||||
#include <util/point.h>
|
||||
#include <global.h>
|
||||
|
||||
enum EKeyCode {
|
||||
enum InputKeyCode {
|
||||
KC_UNKNOWN = 0x00,
|
||||
KC_ESCAPE = 0x01,
|
||||
KC_1 = 0x02,
|
||||
@@ -152,28 +151,28 @@ enum EKeyCode {
|
||||
KC_MEDIASELECT = 0xED // Media Select
|
||||
};
|
||||
|
||||
enum EEvent {
|
||||
EV_MOUSE = 1,
|
||||
EV_KEYBOARD = 2,
|
||||
EV_DOWN = 4,
|
||||
EV_UP = 8,
|
||||
EV_MOUSE_WHEEL = 16,
|
||||
EV_MOUSE_LEFT = 32,
|
||||
EV_MOUSE_RIGHT = 64,
|
||||
EV_MOUSE_MIDDLE = 128,
|
||||
|
||||
EV_TEXT_ENTER = EV_KEYBOARD | 256,
|
||||
EV_KEY_DOWN = EV_KEYBOARD | EV_DOWN,
|
||||
EV_KEY_UP = EV_KEYBOARD | EV_UP,
|
||||
EV_MOUSE_MOVE = EV_MOUSE | 512,
|
||||
EV_MOUSE_LDOWN = EV_MOUSE | EV_MOUSE_LEFT | EV_DOWN,
|
||||
EV_MOUSE_LUP = EV_MOUSE | EV_MOUSE_LEFT | EV_UP,
|
||||
EV_MOUSE_MDOWN = EV_MOUSE | EV_MOUSE_MIDDLE | EV_DOWN,
|
||||
EV_MOUSE_MUP = EV_MOUSE | EV_MOUSE_MIDDLE | EV_UP,
|
||||
EV_MOUSE_RDOWN = EV_MOUSE | EV_MOUSE_RIGHT | EV_DOWN,
|
||||
EV_MOUSE_RUP = EV_MOUSE | EV_MOUSE_RIGHT | EV_UP,
|
||||
EV_MOUSE_WHEEL_UP = EV_MOUSE | EV_MOUSE_WHEEL | EV_UP,
|
||||
EV_MOUSE_WHEEL_DOWN = EV_MOUSE | EV_MOUSE_WHEEL | EV_DOWN
|
||||
enum InputEventType {
|
||||
EventNone = 0,
|
||||
EventMouseAction = 1,
|
||||
EventKeyboardAction = 2,
|
||||
EventDown = 4,
|
||||
EventUp = 8,
|
||||
EventMouseWheel = 16,
|
||||
EventMouseLeftButton = 32,
|
||||
EventMouseRightButton = 64,
|
||||
EventMouseMidButton = 128,
|
||||
EventTextEnter = 256,
|
||||
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
|
||||
};
|
||||
|
||||
struct InputEvent {
|
||||
@@ -187,4 +186,4 @@ struct InputEvent {
|
||||
bool alt;
|
||||
};
|
||||
|
||||
#endif // INPUT_H
|
||||
#endif
|
73
src/framework/core/module.cpp
Normal file
73
src/framework/core/module.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#include "module.h"
|
||||
|
||||
#include <otml/otml.h>
|
||||
#include <luascript/luainterface.h>
|
||||
#include "modulemanager.h"
|
||||
|
||||
void Module::discover(const OTMLNodePtr& moduleNode)
|
||||
{
|
||||
m_description = moduleNode->readAt<std::string>("description");
|
||||
m_author = moduleNode->readAt<std::string>("author");
|
||||
m_website = moduleNode->readAt<std::string>("website");
|
||||
m_version = moduleNode->readAt<std::string>("version");
|
||||
|
||||
if(OTMLNodePtr node = moduleNode->get("dependencies")) {
|
||||
for(const OTMLNodePtr& tmp : node->childNodes())
|
||||
m_dependencies.push_back(tmp->value());
|
||||
}
|
||||
|
||||
// set onLoad callback
|
||||
if(OTMLNodePtr node = moduleNode->get("onLoad")) {
|
||||
g_lua.loadFunction(node->read<std::string>(), "@" + node->source() + "[" + node->tag() + "]");
|
||||
g_lua.useValue();
|
||||
m_loadCallback = g_lua.polymorphicPop<BooleanCallback>();
|
||||
}
|
||||
|
||||
// set onUnload callback
|
||||
if(OTMLNodePtr node = moduleNode->get("onUnload")) {
|
||||
g_lua.loadFunction(node->read<std::string>(), "@" + node->source() + "[" + node->tag() + "]");
|
||||
g_lua.useValue();
|
||||
m_unloadCallback = g_lua.polymorphicPop<SimpleCallback>();
|
||||
}
|
||||
|
||||
// load if autoLoad is set
|
||||
m_autoLoad = moduleNode->readAt<bool>("autoLoad", false);
|
||||
}
|
||||
|
||||
bool Module::load()
|
||||
{
|
||||
for(const std::string& depName : m_dependencies) {
|
||||
ModulePtr dep = g_modules.getModule(depName);
|
||||
if(!dep) {
|
||||
logError("ERROR: failed to load module '",m_name,"': could not find module dependency '",depName,"'");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!dep->isLoaded()) {
|
||||
if(!dep->load()) {
|
||||
logError("ERROR: failed to load module '",m_name,"': a dependency has failed to load");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(m_loadCallback) {
|
||||
m_loaded = m_loadCallback();
|
||||
if(!m_loaded) {
|
||||
logError("ERROR: failed to load module '",m_name, "': onLoad returned false");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
logInfo("Loaded module '", m_name, "'");
|
||||
return true;
|
||||
}
|
||||
|
||||
void Module::unload()
|
||||
{
|
||||
if(m_loaded) {
|
||||
if(m_unloadCallback)
|
||||
m_unloadCallback();
|
||||
m_loaded = false;
|
||||
}
|
||||
}
|
40
src/framework/core/module.h
Normal file
40
src/framework/core/module.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef MODULE_H
|
||||
#define MODULE_H
|
||||
|
||||
#include <otml/otmldeclarations.h>
|
||||
|
||||
class Module;
|
||||
typedef std::shared_ptr<Module> ModulePtr;
|
||||
|
||||
class Module
|
||||
{
|
||||
public:
|
||||
Module(const std::string& name) : m_loaded(false), m_autoLoad(false), m_name(name) { }
|
||||
void discover(const OTMLNodePtr& moduleNode);
|
||||
|
||||
bool load();
|
||||
void unload();
|
||||
|
||||
bool isLoaded() const { return m_loaded; }
|
||||
|
||||
std::string getDescription() const { return m_description; }
|
||||
std::string getName() const { return m_name; }
|
||||
std::string getAuthor() const { return m_author; }
|
||||
std::string getWebsite() const { return m_website; }
|
||||
std::string getVersion() const { return m_version; }
|
||||
bool autoLoad() const { return m_autoLoad; }
|
||||
|
||||
private:
|
||||
bool m_loaded;
|
||||
bool m_autoLoad;
|
||||
std::string m_name;
|
||||
std::string m_description;
|
||||
std::string m_author;
|
||||
std::string m_website;
|
||||
std::string m_version;
|
||||
BooleanCallback m_loadCallback;
|
||||
SimpleCallback m_unloadCallback;
|
||||
std::list<std::string> m_dependencies;
|
||||
};
|
||||
|
||||
#endif
|
58
src/framework/core/modulemanager.cpp
Normal file
58
src/framework/core/modulemanager.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "modulemanager.h"
|
||||
#include "resourcemanager.h"
|
||||
#include <otml/otml.h>
|
||||
|
||||
ModuleManager g_modules;
|
||||
|
||||
void ModuleManager::discoverAndLoadModules()
|
||||
{
|
||||
auto moduleDirs = g_resources.listDirectoryFiles("/");
|
||||
for(const std::string& moduleDir : moduleDirs) {
|
||||
auto moduleFiles = g_resources.listDirectoryFiles("/" + moduleDir);
|
||||
for(const std::string& file : moduleFiles) {
|
||||
if(boost::ends_with(file, ".otmod"))
|
||||
discoverModule("/" + moduleDir + "/" + file);
|
||||
}
|
||||
}
|
||||
|
||||
// auto load modules
|
||||
for(const ModulePtr& module : m_modules) {
|
||||
if(!module->isLoaded() && module->autoLoad())
|
||||
module->load();
|
||||
}
|
||||
}
|
||||
|
||||
bool ModuleManager::discoverModule(const std::string& file)
|
||||
{
|
||||
ModulePtr module;
|
||||
try {
|
||||
OTMLDocumentPtr doc = OTMLDocument::parse(file);
|
||||
OTMLNodePtr moduleNode = doc->at("Module");
|
||||
|
||||
std::string name = moduleNode->readAt<std::string>("name");
|
||||
if(getModule(name))
|
||||
throw OTMLException(moduleNode, "a module with the same name is already discovered, did you duplicate module names?");
|
||||
|
||||
module = ModulePtr(new Module(name));
|
||||
module->discover(moduleNode);
|
||||
m_modules.push_back(module);
|
||||
} catch(std::exception& e) {
|
||||
logError("ERROR: failed to load module from '", file, "':\n", e.what());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ModuleManager::unloadModules()
|
||||
{
|
||||
for(const ModulePtr& module : m_modules)
|
||||
module->unload();
|
||||
}
|
||||
|
||||
ModulePtr ModuleManager::getModule(const std::string& moduleName)
|
||||
{
|
||||
for(const ModulePtr& module : m_modules)
|
||||
if(module->getName() == moduleName)
|
||||
return module;
|
||||
return nullptr;
|
||||
}
|
21
src/framework/core/modulemanager.h
Normal file
21
src/framework/core/modulemanager.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef MODULEMANAGER_H
|
||||
#define MODULEMANAGER_H
|
||||
|
||||
#include "module.h"
|
||||
|
||||
class ModuleManager
|
||||
{
|
||||
public:
|
||||
void discoverAndLoadModules();
|
||||
bool discoverModule(const std::string& file);
|
||||
void unloadModules();
|
||||
|
||||
ModulePtr getModule(const std::string& moduleName);
|
||||
|
||||
private:
|
||||
std::vector<ModulePtr> m_modules;
|
||||
};
|
||||
|
||||
extern ModuleManager g_modules;
|
||||
|
||||
#endif
|
@@ -1,29 +0,0 @@
|
||||
#include "packages.h"
|
||||
|
||||
#include <core/resources.h>
|
||||
#include <script/luainterface.h>
|
||||
|
||||
Packages g_packages;
|
||||
|
||||
void Packages::loadPackages()
|
||||
{
|
||||
std::list<std::string> packages = g_resources.listDirectoryFiles("modules");
|
||||
foreach(const std::string& package, packages) {
|
||||
std::string dir = make_string("modules/", package);
|
||||
g_resources.pushCurrentPath(dir);
|
||||
|
||||
std::list<std::string> packagesFiles = g_resources.listDirectoryFiles();
|
||||
foreach(const std::string& packageFile, packagesFiles) {
|
||||
if(boost::ends_with(packageFile, ".lua")) {
|
||||
g_lua.runScript(packageFile);
|
||||
}
|
||||
}
|
||||
|
||||
g_resources.popCurrentPath();
|
||||
}
|
||||
}
|
||||
|
||||
void Packages::terminate()
|
||||
{
|
||||
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
#ifndef PACKAGES_H
|
||||
#define PACKAGES_H
|
||||
|
||||
#include <global.h>
|
||||
|
||||
class Packages
|
||||
{
|
||||
public:
|
||||
void loadPackages();
|
||||
void terminate();
|
||||
};
|
||||
|
||||
extern Packages g_packages;
|
||||
|
||||
#endif // MODULES_H
|
@@ -3,53 +3,63 @@
|
||||
|
||||
#include <global.h>
|
||||
|
||||
class PlatformListener;
|
||||
|
||||
class Platform
|
||||
{
|
||||
public:
|
||||
static void init(const char *appName);
|
||||
static void terminate();
|
||||
void init(PlatformListener* platformListener, const char* appName);
|
||||
void terminate();
|
||||
|
||||
/// Poll platform input/window events
|
||||
static void poll();
|
||||
void poll();
|
||||
|
||||
/// Get current time in milliseconds since init
|
||||
static int getTicks();
|
||||
int getTicks();
|
||||
/// Sleep in current thread
|
||||
static void sleep(ulong miliseconds);
|
||||
void sleep(ulong ms);
|
||||
|
||||
static bool createWindow(int x, int y, int width, int height, int minWidth, int minHeight, bool maximized);
|
||||
static void destroyWindow();
|
||||
static void showWindow();
|
||||
static void setWindowTitle(const char *title);
|
||||
static bool isWindowFocused();
|
||||
static bool isWindowVisible();
|
||||
static int getWindowX();
|
||||
static int getWindowY();
|
||||
static int getWindowWidth();
|
||||
static int getWindowHeight();
|
||||
static bool isWindowMaximized();
|
||||
bool createWindow(int x, int y, int width, int height, int minWidth, int minHeight, bool maximized);
|
||||
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();
|
||||
|
||||
static int getDisplayHeight();
|
||||
static int getDisplayWidth();
|
||||
int getDisplayHeight();
|
||||
int getDisplayWidth();
|
||||
|
||||
/// Get GL extension function address
|
||||
static void *getExtensionProcAddress(const char *ext);
|
||||
/// Check if GL extension is supported
|
||||
static bool isExtensionSupported(const char *ext);
|
||||
void* getExtensionProcAddress(const char* ext);
|
||||
/// Check if GLX/WGL extension is supported
|
||||
bool isExtensionSupported(const char* ext);
|
||||
|
||||
static const char *getClipboardText();
|
||||
static void setClipboardText(const char *text);
|
||||
/// Get text from Ctrl+c
|
||||
const char* getClipboardText();
|
||||
/// Set text for Ctrl+v
|
||||
void setClipboardText(const char* text);
|
||||
|
||||
static void hideMouseCursor();
|
||||
static void showMouseCursor();
|
||||
void hideMouseCursor();
|
||||
void showMouseCursor();
|
||||
|
||||
/// Enable/disable vertical synchronization
|
||||
static void setVsync(bool enable = true);
|
||||
/// Enable or disable vertical synchronization
|
||||
void setVerticalSync(bool enable);
|
||||
/// Swap GL buffers
|
||||
static void swapBuffers();
|
||||
void swapBuffers();
|
||||
|
||||
/// Get the app user directory, the place to save files configurations files
|
||||
static std::string getAppUserDir();
|
||||
std::string getAppUserDir();
|
||||
|
||||
private:
|
||||
PlatformListener* m_listener;
|
||||
};
|
||||
|
||||
#endif // PLATFORM_H
|
||||
extern Platform g_platform;
|
||||
|
||||
#endif
|
||||
|
17
src/framework/core/platformlistener.h
Normal file
17
src/framework/core/platformlistener.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef PLATFORMLISTENER_H
|
||||
#define PLATFORMLISTENER_H
|
||||
|
||||
#include "inputevent.h"
|
||||
|
||||
class PlatformListener
|
||||
{
|
||||
public:
|
||||
/// Fired when user tries to close the window
|
||||
virtual void onClose() = 0;
|
||||
/// Fired when user resize the window
|
||||
virtual void onResize(const Size& size) = 0;
|
||||
/// Fired when user press a key or move the mouse
|
||||
virtual void onInputEvent(const InputEvent& event) = 0;
|
||||
};
|
||||
|
||||
#endif
|
170
src/framework/core/resourcemanager.cpp
Normal file
170
src/framework/core/resourcemanager.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
#include "resourcemanager.h"
|
||||
|
||||
#include <core/platform.h>
|
||||
#include <luascript/luainterface.h>
|
||||
|
||||
#include <physfs.h>
|
||||
|
||||
ResourceManager g_resources;
|
||||
|
||||
void ResourceManager::init(const char* argv0)
|
||||
{
|
||||
PHYSFS_init(argv0);
|
||||
|
||||
// try to find modules directory, all data lives there
|
||||
std::string baseDir = PHYSFS_getBaseDir();
|
||||
|
||||
std::string possibleDirs[] = { "modules",
|
||||
baseDir + "modules",
|
||||
baseDir + "../modules",
|
||||
baseDir + "../share/otclient/modules",
|
||||
"" };
|
||||
|
||||
bool found = false;
|
||||
for(const std::string& dir : possibleDirs) {
|
||||
if(g_resources.addToSearchPath(dir)) {
|
||||
logInfo("Using modules directory '", dir.c_str(), "'");
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!found)
|
||||
throw std::runtime_error("could not find modules directory");
|
||||
|
||||
// setup write directory
|
||||
std::string dir = g_platform.getAppUserDir();
|
||||
if(g_resources.setWriteDir(dir))
|
||||
g_resources.addToSearchPath(dir);
|
||||
else
|
||||
throw std::runtime_error("could not setup write directory");
|
||||
}
|
||||
|
||||
void ResourceManager::terminate()
|
||||
{
|
||||
PHYSFS_deinit();
|
||||
}
|
||||
|
||||
bool ResourceManager::setWriteDir(const std::string& path)
|
||||
{
|
||||
if(!PHYSFS_setWriteDir(path.c_str()))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourceManager::addToSearchPath(const std::string& path, bool insertInFront)
|
||||
{
|
||||
if(!PHYSFS_addToSearchPath(path.c_str(), insertInFront ? 0 : 1))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ResourceManager::searchAndAddPackages(const std::string& packagesDir, const std::string& packageExt, bool append)
|
||||
{
|
||||
auto files = listDirectoryFiles(resolvePath(packagesDir));
|
||||
for(const std::string& file : files) {
|
||||
if(boost::ends_with(file, packageExt))
|
||||
addToSearchPath(packagesDir + "/" + file, !append);
|
||||
}
|
||||
}
|
||||
|
||||
bool ResourceManager::fileExists(const std::string& fileName)
|
||||
{
|
||||
return (PHYSFS_exists(resolvePath(fileName).c_str()) && !PHYSFS_isDirectory(resolvePath(fileName).c_str()));
|
||||
}
|
||||
|
||||
bool ResourceManager::directoryExists(const std::string& directoryName)
|
||||
{
|
||||
return (PHYSFS_exists(resolvePath(directoryName).c_str()) && PHYSFS_isDirectory(resolvePath(directoryName).c_str()));
|
||||
}
|
||||
|
||||
void ResourceManager::loadFile(const std::string& fileName, std::iostream& out)
|
||||
{
|
||||
std::string fullPath = resolvePath(fileName);
|
||||
out.clear(std::ios::goodbit);
|
||||
PHYSFS_file* file = PHYSFS_openRead(fullPath.c_str());
|
||||
if(!file) {
|
||||
out.clear(std::ios::failbit);
|
||||
throw std::runtime_error(aux::make_string("failed to load file '", fullPath.c_str(), "': ", PHYSFS_getLastError()));
|
||||
} else {
|
||||
int fileSize = PHYSFS_fileLength(file);
|
||||
if(fileSize > 0) {
|
||||
char* buffer = new char[fileSize];
|
||||
PHYSFS_read(file, (void*)buffer, 1, fileSize);
|
||||
out.write(buffer, fileSize);
|
||||
delete[] buffer;
|
||||
} else
|
||||
out.clear(std::ios::eofbit);
|
||||
PHYSFS_close(file);
|
||||
out.seekg(0, std::ios::beg);
|
||||
}
|
||||
}
|
||||
|
||||
std::string ResourceManager::loadFile(const std::string& fileName)
|
||||
{
|
||||
std::stringstream fin;
|
||||
loadFile(fileName, fin);
|
||||
return fin.str();
|
||||
}
|
||||
|
||||
bool ResourceManager::saveFile(const std::string& fileName, const uchar* data, uint size)
|
||||
{
|
||||
PHYSFS_file* file = PHYSFS_openWrite(resolvePath(fileName).c_str());
|
||||
if(!file)
|
||||
return false;
|
||||
|
||||
PHYSFS_write(file, (void*)data, size, 1);
|
||||
PHYSFS_close(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourceManager::saveFile(const std::string& fileName, std::istream& in)
|
||||
{
|
||||
std::streampos oldPos = in.tellg();
|
||||
in.seekg(0, std::ios::end);
|
||||
std::streampos size = in.tellg();
|
||||
in.seekg(0, std::ios::beg);
|
||||
char* buffer = new char[size];
|
||||
in.read(buffer, size);
|
||||
bool ret = saveFile(fileName, (const uchar*)buffer, size);
|
||||
delete[] buffer;
|
||||
in.seekg(oldPos, std::ios::beg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ResourceManager::saveFile(const std::string& fileName, const std::string& data)
|
||||
{
|
||||
return saveFile(fileName, (const uchar*)data.c_str(), data.size());
|
||||
}
|
||||
|
||||
bool ResourceManager::deleteFile(const std::string& fileName)
|
||||
{
|
||||
return PHYSFS_delete(resolvePath(fileName).c_str()) != 0;
|
||||
}
|
||||
|
||||
std::list<std::string> ResourceManager::listDirectoryFiles(const std::string& directoryPath)
|
||||
{
|
||||
std::list<std::string> files;
|
||||
char** rc = PHYSFS_enumerateFiles(resolvePath(directoryPath).c_str());
|
||||
|
||||
for(char** i = rc; *i != NULL; i++)
|
||||
files.push_back(*i);
|
||||
|
||||
PHYSFS_freeList(rc);
|
||||
return files;
|
||||
}
|
||||
|
||||
std::string ResourceManager::resolvePath(const std::string& path)
|
||||
{
|
||||
std::string fullPath;
|
||||
if(boost::starts_with(path, "/"))
|
||||
fullPath = path.substr(1);
|
||||
else {
|
||||
std::string scriptPath = g_lua.currentSourcePath();
|
||||
if(!scriptPath.empty()) {
|
||||
fullPath += scriptPath + "/";
|
||||
}
|
||||
fullPath += path;
|
||||
}
|
||||
return fullPath;
|
||||
}
|
40
src/framework/core/resourcemanager.h
Normal file
40
src/framework/core/resourcemanager.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef RESOURCES_H
|
||||
#define RESOURCES_H
|
||||
|
||||
#include <global.h>
|
||||
|
||||
class ResourceManager
|
||||
{
|
||||
public:
|
||||
void init(const char* argv0);
|
||||
void terminate();
|
||||
|
||||
/// Set output files directory
|
||||
bool setWriteDir(const std::string& path);
|
||||
|
||||
/// Add an package or directory to the search path
|
||||
bool addToSearchPath(const std::string& path, bool insertInFront = true);
|
||||
|
||||
/// Search and packages from a directory to the search path
|
||||
void searchAndAddPackages(const std::string& packagesDir, const std::string& packagesExt, bool append);
|
||||
|
||||
bool fileExists(const std::string& fileName);
|
||||
bool directoryExists(const std::string& directoryName);
|
||||
|
||||
void loadFile(const std::string& fileName, std::iostream& out);
|
||||
std::string loadFile(const std::string& fileName);
|
||||
|
||||
bool saveFile(const std::string& fileName, const uchar* data, uint size);
|
||||
bool saveFile(const std::string& fileName, const std::string& data);
|
||||
bool saveFile(const std::string& fileName, std::istream& in);
|
||||
|
||||
bool deleteFile(const std::string& fileName);
|
||||
|
||||
std::list<std::string> listDirectoryFiles(const std::string& directoryPath = "");
|
||||
|
||||
std::string resolvePath(const std::string& path);
|
||||
};
|
||||
|
||||
extern ResourceManager g_resources;
|
||||
|
||||
#endif
|
@@ -1,176 +0,0 @@
|
||||
#include <global.h>
|
||||
#include "resources.h"
|
||||
#include "platform.h"
|
||||
|
||||
#include <physfs.h>
|
||||
|
||||
Resources g_resources;
|
||||
|
||||
void Resources::init(const char *argv0)
|
||||
{
|
||||
PHYSFS_init(argv0);
|
||||
|
||||
// try to find data directory
|
||||
std::string dir;
|
||||
std::string baseDir = PHYSFS_getBaseDir();
|
||||
|
||||
std::string possibleDirs[] = { "data",
|
||||
baseDir + "data",
|
||||
baseDir + "../data",
|
||||
baseDir + "../share/otclient/data",
|
||||
"" };
|
||||
|
||||
bool found = false;
|
||||
foreach(dir, possibleDirs) {
|
||||
if(g_resources.addToSearchPath(dir)) {
|
||||
logInfo("Using data directory: ", dir.c_str());
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found)
|
||||
logFatal("ERROR: could not find data directory");
|
||||
|
||||
// setup write directory
|
||||
dir = Platform::getAppUserDir();
|
||||
if(g_resources.setWriteDir(dir))
|
||||
g_resources.addToSearchPath(dir);
|
||||
else
|
||||
logError("ERROR: could not setup write directory");
|
||||
}
|
||||
|
||||
void Resources::terminate()
|
||||
{
|
||||
PHYSFS_deinit();
|
||||
}
|
||||
|
||||
bool Resources::setWriteDir(const std::string& path)
|
||||
{
|
||||
if(!PHYSFS_setWriteDir(path.c_str()))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Resources::addToSearchPath(const std::string& path, bool insertInFront /*= true*/)
|
||||
{
|
||||
if(!PHYSFS_addToSearchPath(path.c_str(), insertInFront ? 0 : 1))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Resources::addPackagesToSearchPath(const std::string &packagesDirectory, const std::string &packageExtension, bool append)
|
||||
{
|
||||
auto files = listDirectoryFiles(resolvePath(packagesDirectory));
|
||||
foreach(const std::string& file, files) {
|
||||
if(boost::ends_with(file, packageExtension))
|
||||
addToSearchPath(packagesDirectory + "/" + file, !append);
|
||||
}
|
||||
}
|
||||
|
||||
bool Resources::fileExists(const std::string& fileName)
|
||||
{
|
||||
return (PHYSFS_exists(resolvePath(fileName).c_str()) && !PHYSFS_isDirectory(resolvePath(fileName).c_str()));
|
||||
}
|
||||
|
||||
bool Resources::directoryExists(const std::string& directoryName)
|
||||
{
|
||||
return (PHYSFS_exists(resolvePath(directoryName).c_str()) && PHYSFS_isDirectory(resolvePath(directoryName).c_str()));
|
||||
}
|
||||
|
||||
bool Resources::loadFile(const std::string& fileName, std::iostream& out)
|
||||
{
|
||||
std::string fullPath = resolvePath(fileName);
|
||||
out.clear(std::ios::goodbit);
|
||||
PHYSFS_file *file = PHYSFS_openRead(fullPath.c_str());
|
||||
if(!file) {
|
||||
logError("ERROR: Failed to load file '", fullPath.c_str(), "': ", PHYSFS_getLastError());
|
||||
out.clear(std::ios::failbit);
|
||||
return false;
|
||||
} else {
|
||||
int fileSize = PHYSFS_fileLength(file);
|
||||
if(fileSize > 0) {
|
||||
char *buffer = new char[fileSize];
|
||||
PHYSFS_read(file, (void*)buffer, 1, fileSize);
|
||||
out.write(buffer, fileSize);
|
||||
delete[] buffer;
|
||||
} else
|
||||
out.clear(std::ios::eofbit);
|
||||
PHYSFS_close(file);
|
||||
out.seekg(0, std::ios::beg);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Resources::saveFile(const std::string &fileName, const uchar *data, uint size)
|
||||
{
|
||||
PHYSFS_file *file = PHYSFS_openWrite(resolvePath(fileName).c_str());
|
||||
if(!file) {
|
||||
logError("ERROR: Failed to save file '",fileName,"': ",PHYSFS_getLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
PHYSFS_write(file, (void*)data, size, 1);
|
||||
PHYSFS_close(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Resources::saveFile(const std::string &fileName, std::istream& in)
|
||||
{
|
||||
std::streampos oldPos = in.tellg();
|
||||
in.seekg(0, std::ios::end);
|
||||
std::streampos size = in.tellg();
|
||||
in.seekg(0, std::ios::beg);
|
||||
char *buffer = new char[size];
|
||||
in.read(buffer, size);
|
||||
bool ret = saveFile(fileName, (const uchar*)buffer, size);
|
||||
delete[] buffer;
|
||||
in.seekg(oldPos, std::ios::beg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Resources::deleteFile(const std::string& fileName)
|
||||
{
|
||||
return PHYSFS_delete(resolvePath(fileName).c_str()) != 0;
|
||||
}
|
||||
|
||||
std::list<std::string> Resources::listDirectoryFiles(const std::string& directoryPath)
|
||||
{
|
||||
std::list<std::string> files;
|
||||
char **rc = PHYSFS_enumerateFiles(resolvePath(directoryPath).c_str());
|
||||
|
||||
for(char **i = rc; *i != NULL; i++)
|
||||
files.push_back(*i);
|
||||
|
||||
PHYSFS_freeList(rc);
|
||||
return files;
|
||||
}
|
||||
|
||||
void Resources::pushCurrentPath(const std::string ¤tPath)
|
||||
{
|
||||
//logTraceDebug(currentPath);
|
||||
m_currentPaths.push(currentPath);
|
||||
}
|
||||
|
||||
void Resources::popCurrentPath()
|
||||
{
|
||||
m_currentPaths.pop();
|
||||
//if(!m_currentPaths.empty())
|
||||
// logTraceDebug(m_currentPaths.top());
|
||||
}
|
||||
|
||||
std::string Resources::resolvePath(const std::string& path)
|
||||
{
|
||||
std::string fullPath;
|
||||
if(boost::starts_with(path, "/"))
|
||||
fullPath = path.substr(1);
|
||||
else {
|
||||
if(m_currentPaths.size() > 0) {
|
||||
std::string currentPath = m_currentPaths.top();
|
||||
if(currentPath.length() > 0)
|
||||
fullPath += currentPath + "/";
|
||||
}
|
||||
fullPath += path;
|
||||
}
|
||||
return fullPath;
|
||||
}
|
||||
|
@@ -1,46 +0,0 @@
|
||||
#ifndef RESOURCES_H
|
||||
#define RESOURCES_H
|
||||
|
||||
#include <global.h>
|
||||
#include <stack>
|
||||
|
||||
class Resources
|
||||
{
|
||||
public:
|
||||
Resources() { }
|
||||
|
||||
void init(const char *argv0);
|
||||
void terminate();
|
||||
|
||||
/// Set output files dir
|
||||
bool setWriteDir(const std::string &path);
|
||||
|
||||
/// Add an package or directory to the search path
|
||||
bool addToSearchPath(const std::string& path, bool insertInFront = true);
|
||||
/// Add all packages from a directory
|
||||
void addPackagesToSearchPath(const std::string &packagesDirectory, const std::string &packageExtension, bool append);
|
||||
|
||||
bool fileExists(const std::string& fileName);
|
||||
bool directoryExists(const std::string& directoryName);
|
||||
|
||||
bool loadFile(const std::string& fileName, std::iostream& out);
|
||||
|
||||
bool saveFile(const std::string& fileName, const uchar *data, uint size);
|
||||
bool saveFile(const std::string& fileName, const std::string& data) { return saveFile(fileName, (const uchar*)data.c_str(), data.size()); }
|
||||
bool saveFile(const std::string& fileName, std::istream& in);
|
||||
|
||||
bool deleteFile(const std::string& fileName);
|
||||
|
||||
std::list<std::string> listDirectoryFiles(const std::string& directoryPath = "");
|
||||
|
||||
void pushCurrentPath(const std::string ¤tPath);
|
||||
void popCurrentPath();
|
||||
std::string resolvePath(const std::string& path);
|
||||
|
||||
private:
|
||||
std::stack<std::string> m_currentPaths;
|
||||
};
|
||||
|
||||
extern Resources g_resources;
|
||||
|
||||
#endif // RESOURCES_H
|
0
src/framework/core/win32platform.cpp
Normal file
0
src/framework/core/win32platform.cpp
Normal file
827
src/framework/core/x11platform.cpp
Normal file
827
src/framework/core/x11platform.cpp
Normal file
@@ -0,0 +1,827 @@
|
||||
#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>
|
||||
|
||||
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;
|
||||
std::string clipboardText;
|
||||
std::map<int, uchar> keyMap;
|
||||
PlatformListener* listener;
|
||||
} x11;
|
||||
|
||||
Platform g_platform;
|
||||
|
||||
void Platform::init(PlatformListener* platformListener, const char *appName)
|
||||
{
|
||||
// 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;
|
||||
m_listener = platformListener;
|
||||
|
||||
// setup keymap
|
||||
x11.keyMap[XK_1] = KC_1;
|
||||
x11.keyMap[XK_2] = KC_2;
|
||||
x11.keyMap[XK_3] = KC_3;
|
||||
x11.keyMap[XK_4] = KC_4;
|
||||
x11.keyMap[XK_5] = KC_5;
|
||||
x11.keyMap[XK_6] = KC_6;
|
||||
x11.keyMap[XK_7] = KC_7;
|
||||
x11.keyMap[XK_8] = KC_8;
|
||||
x11.keyMap[XK_9] = KC_9;
|
||||
x11.keyMap[XK_0] = KC_0;
|
||||
|
||||
x11.keyMap[XK_BackSpace] = KC_BACK;
|
||||
|
||||
x11.keyMap[XK_minus] = KC_MINUS;
|
||||
x11.keyMap[XK_equal] = KC_EQUALS;
|
||||
x11.keyMap[XK_space] = KC_SPACE;
|
||||
x11.keyMap[XK_comma] = KC_COMMA;
|
||||
x11.keyMap[XK_period] = KC_PERIOD;
|
||||
|
||||
x11.keyMap[XK_backslash] = KC_BACKSLASH;
|
||||
x11.keyMap[XK_slash] = KC_SLASH;
|
||||
x11.keyMap[XK_bracketleft] = KC_LBRACKET;
|
||||
x11.keyMap[XK_bracketright] = KC_RBRACKET;
|
||||
|
||||
x11.keyMap[XK_Escape] = KC_ESCAPE;
|
||||
x11.keyMap[XK_Caps_Lock] = KC_CAPITAL;
|
||||
|
||||
x11.keyMap[XK_Tab] = KC_TAB;
|
||||
x11.keyMap[XK_Return] = KC_RETURN;
|
||||
x11.keyMap[XK_Control_L] = KC_LCONTROL;
|
||||
x11.keyMap[XK_Control_R] = KC_RCONTROL;
|
||||
|
||||
x11.keyMap[XK_colon] = KC_COLON;
|
||||
x11.keyMap[XK_semicolon] = KC_SEMICOLON;
|
||||
x11.keyMap[XK_apostrophe] = KC_APOSTROPHE;
|
||||
x11.keyMap[XK_grave] = KC_GRAVE;
|
||||
|
||||
x11.keyMap[XK_b] = KC_B;
|
||||
x11.keyMap[XK_a] = KC_A;
|
||||
x11.keyMap[XK_c] = KC_C;
|
||||
x11.keyMap[XK_d] = KC_D;
|
||||
x11.keyMap[XK_e] = KC_E;
|
||||
x11.keyMap[XK_f] = KC_F;
|
||||
x11.keyMap[XK_g] = KC_G;
|
||||
x11.keyMap[XK_h] = KC_H;
|
||||
x11.keyMap[XK_i] = KC_I;
|
||||
x11.keyMap[XK_j] = KC_J;
|
||||
x11.keyMap[XK_k] = KC_K;
|
||||
x11.keyMap[XK_l] = KC_L;
|
||||
x11.keyMap[XK_m] = KC_M;
|
||||
x11.keyMap[XK_n] = KC_N;
|
||||
x11.keyMap[XK_o] = KC_O;
|
||||
x11.keyMap[XK_p] = KC_P;
|
||||
x11.keyMap[XK_q] = KC_Q;
|
||||
x11.keyMap[XK_r] = KC_R;
|
||||
x11.keyMap[XK_s] = KC_S;
|
||||
x11.keyMap[XK_t] = KC_T;
|
||||
x11.keyMap[XK_u] = KC_U;
|
||||
x11.keyMap[XK_v] = KC_V;
|
||||
x11.keyMap[XK_w] = KC_W;
|
||||
x11.keyMap[XK_x] = KC_X;
|
||||
x11.keyMap[XK_y] = KC_Y;
|
||||
x11.keyMap[XK_z] = KC_Z;
|
||||
|
||||
x11.keyMap[XK_F1] = KC_F1;
|
||||
x11.keyMap[XK_F2] = KC_F2;
|
||||
x11.keyMap[XK_F3] = KC_F3;
|
||||
x11.keyMap[XK_F4] = KC_F4;
|
||||
x11.keyMap[XK_F5] = KC_F5;
|
||||
x11.keyMap[XK_F6] = KC_F6;
|
||||
x11.keyMap[XK_F7] = KC_F7;
|
||||
x11.keyMap[XK_F8] = KC_F8;
|
||||
x11.keyMap[XK_F9] = KC_F9;
|
||||
x11.keyMap[XK_F10] = KC_F10;
|
||||
x11.keyMap[XK_F11] = KC_F11;
|
||||
x11.keyMap[XK_F12] = KC_F12;
|
||||
x11.keyMap[XK_F13] = KC_F13;
|
||||
x11.keyMap[XK_F14] = KC_F14;
|
||||
x11.keyMap[XK_F15] = KC_F15;
|
||||
|
||||
// keypad
|
||||
x11.keyMap[XK_KP_0] = KC_NUMPAD0;
|
||||
x11.keyMap[XK_KP_1] = KC_NUMPAD1;
|
||||
x11.keyMap[XK_KP_2] = KC_NUMPAD2;
|
||||
x11.keyMap[XK_KP_3] = KC_NUMPAD3;
|
||||
x11.keyMap[XK_KP_4] = KC_NUMPAD4;
|
||||
x11.keyMap[XK_KP_5] = KC_NUMPAD5;
|
||||
x11.keyMap[XK_KP_6] = KC_NUMPAD6;
|
||||
x11.keyMap[XK_KP_7] = KC_NUMPAD7;
|
||||
x11.keyMap[XK_KP_8] = KC_NUMPAD8;
|
||||
x11.keyMap[XK_KP_9] = KC_NUMPAD9;
|
||||
x11.keyMap[XK_KP_Add] = KC_ADD;
|
||||
x11.keyMap[XK_KP_Subtract] = KC_SUBTRACT;
|
||||
x11.keyMap[XK_KP_Decimal] = KC_DECIMAL;
|
||||
x11.keyMap[XK_KP_Equal] = KC_NUMPADEQUALS;
|
||||
x11.keyMap[XK_KP_Divide] = KC_DIVIDE;
|
||||
x11.keyMap[XK_KP_Multiply] = KC_MULTIPLY;
|
||||
x11.keyMap[XK_KP_Enter] = KC_NUMPADENTER;
|
||||
|
||||
// keypad with numlock off
|
||||
x11.keyMap[XK_KP_Home] = KC_NUMPAD7;
|
||||
x11.keyMap[XK_KP_Up] = KC_NUMPAD8;
|
||||
x11.keyMap[XK_KP_Page_Up] = KC_NUMPAD9;
|
||||
x11.keyMap[XK_KP_Left] = KC_NUMPAD4;
|
||||
x11.keyMap[XK_KP_Begin] = KC_NUMPAD5;
|
||||
x11.keyMap[XK_KP_Right] = KC_NUMPAD6;
|
||||
x11.keyMap[XK_KP_End] = KC_NUMPAD1;
|
||||
x11.keyMap[XK_KP_Down] = KC_NUMPAD2;
|
||||
x11.keyMap[XK_KP_Page_Down] = KC_NUMPAD3;
|
||||
x11.keyMap[XK_KP_Insert] = KC_NUMPAD0;
|
||||
x11.keyMap[XK_KP_Delete] = KC_DECIMAL;
|
||||
|
||||
x11.keyMap[XK_Up] = KC_UP;
|
||||
x11.keyMap[XK_Down] = KC_DOWN;
|
||||
x11.keyMap[XK_Left] = KC_LEFT;
|
||||
x11.keyMap[XK_Right] = KC_RIGHT;
|
||||
|
||||
x11.keyMap[XK_Page_Up] = KC_PGUP;
|
||||
x11.keyMap[XK_Page_Down] = KC_PGDOWN;
|
||||
x11.keyMap[XK_Home] = KC_HOME;
|
||||
x11.keyMap[XK_End] = KC_END;
|
||||
|
||||
x11.keyMap[XK_Num_Lock] = KC_NUMLOCK;
|
||||
x11.keyMap[XK_Print] = KC_SYSRQ;
|
||||
x11.keyMap[XK_Scroll_Lock] = KC_SCROLL;
|
||||
x11.keyMap[XK_Pause] = KC_PAUSE;
|
||||
|
||||
x11.keyMap[XK_Shift_R] = KC_RSHIFT;
|
||||
x11.keyMap[XK_Shift_L] = KC_LSHIFT;
|
||||
x11.keyMap[XK_Alt_R] = KC_RALT;
|
||||
x11.keyMap[XK_Alt_L] = KC_LALT;
|
||||
|
||||
x11.keyMap[XK_Insert] = KC_INSERT;
|
||||
x11.keyMap[XK_Delete] = KC_DELETE;
|
||||
|
||||
x11.keyMap[XK_Super_L] = KC_LWIN;
|
||||
x11.keyMap[XK_Super_R] = KC_RWIN;
|
||||
x11.keyMap[XK_Menu] = KC_APPS;
|
||||
|
||||
// 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("FATAL ERROR: Failed to open X display");
|
||||
|
||||
// check if GLX is supported on this display
|
||||
if(!glXQueryExtension(x11.display, 0, 0))
|
||||
logFatal("FATAL ERROR: GLX not supported");
|
||||
|
||||
// retrieve GLX version
|
||||
int glxMajor;
|
||||
int glxMinor;
|
||||
if(!glXQueryVersion(x11.display, &glxMajor, &glxMinor))
|
||||
logFatal("FATAL ERROR: 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);
|
||||
|
||||
// force first tick
|
||||
Platform::getTicks();
|
||||
}
|
||||
|
||||
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;
|
||||
static InputEvent inputEvent;
|
||||
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 ConfigureNotify:
|
||||
// window resize
|
||||
static int oldWidth = -1;
|
||||
static int oldHeight = -1;
|
||||
if(oldWidth != event.xconfigure.width || oldHeight != event.xconfigure.height) {
|
||||
m_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;
|
||||
|
||||
inputEvent.ctrl = (event.xkey.state & ControlMask);
|
||||
inputEvent.shift = (event.xkey.state & ShiftMask);
|
||||
inputEvent.alt = (event.xkey.state & Mod1Mask);
|
||||
|
||||
// fire enter text event
|
||||
if(event.type == KeyPress && !inputEvent.ctrl && !inputEvent.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 &&
|
||||
// for some reason these keys produces characters and we don't want that
|
||||
keysym != XK_BackSpace &&
|
||||
keysym != XK_Return &&
|
||||
keysym != XK_Delete &&
|
||||
keysym != XK_Escape &&
|
||||
(uchar)(buf[0]) >= 32
|
||||
) {
|
||||
//logDebug("char: ", buf[0], " code: ", (uint)buf[0]);
|
||||
inputEvent.type = EventTextEnter;
|
||||
inputEvent.keychar = buf[0];
|
||||
inputEvent.keycode = KC_UNKNOWN;
|
||||
m_listener->onInputEvent(inputEvent);
|
||||
}
|
||||
}
|
||||
|
||||
// unmask Shift/Lock to get expected results
|
||||
event.xkey.state &= ~(ShiftMask | LockMask);
|
||||
len = XLookupString(&event.xkey, buf, sizeof(buf), &keysym, 0);
|
||||
|
||||
// fire key up/down event
|
||||
if(x11.keyMap.find(keysym) != x11.keyMap.end()) {
|
||||
inputEvent.keycode = x11.keyMap[keysym];
|
||||
inputEvent.type = (event.type == KeyPress) ? EventKeyDown : EventKeyUp;
|
||||
inputEvent.keychar = (len > 0) ? buf[0] : 0;
|
||||
m_listener->onInputEvent(inputEvent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ButtonPress:
|
||||
case ButtonRelease:
|
||||
switch(event.xbutton.button) {
|
||||
case Button1:
|
||||
inputEvent.type = (event.type == ButtonPress) ? EventMouseLeftButtonDown : EventMouseLeftButtonUp;
|
||||
break;
|
||||
case Button3:
|
||||
inputEvent.type = (event.type == ButtonPress) ? EventMouseRightButtonDown : EventMouseRightButtonUp;
|
||||
break;
|
||||
case Button2:
|
||||
inputEvent.type = (event.type == ButtonPress) ? EventMouseMiddleButtonDown : EventMouseMiddleButtonUp;
|
||||
break;
|
||||
case Button4:
|
||||
inputEvent.type = EventMouseWheelUp;
|
||||
break;
|
||||
case Button5:
|
||||
inputEvent.type = EventMouseWheelDown;
|
||||
break;
|
||||
}
|
||||
m_listener->onInputEvent(inputEvent);
|
||||
break;
|
||||
|
||||
case MotionNotify:
|
||||
{
|
||||
inputEvent.type = EventMouseMove;
|
||||
Point newMousePos(event.xbutton.x, event.xbutton.y);
|
||||
inputEvent.mouseMoved = newMousePos - inputEvent.mousePos;
|
||||
inputEvent.mousePos = newMousePos;
|
||||
m_listener->onInputEvent(inputEvent);
|
||||
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)
|
||||
m_listener->onClose();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Platform::getTicks()
|
||||
{
|
||||
static timeval tv;
|
||||
static ulong firstTick = 0;
|
||||
|
||||
gettimeofday(&tv, 0);
|
||||
if(!firstTick)
|
||||
firstTick = tv.tv_sec;
|
||||
|
||||
return ((tv.tv_sec - firstTick) * 1000) + (tv.tv_usec / 1000);
|
||||
}
|
||||
|
||||
void Platform::sleep(ulong miliseconds)
|
||||
{
|
||||
timespec tv;
|
||||
tv.tv_sec = miliseconds / 1000;
|
||||
tv.tv_nsec = (miliseconds % 1000) * 1000000;
|
||||
nanosleep(&tv, NULL);
|
||||
}
|
||||
|
||||
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("FATAL ERROR: RGBA/Double buffered visual not supported");
|
||||
|
||||
// create GLX context
|
||||
x11.glxContext = glXCreateContext(x11.display, x11.visual, 0, GL_TRUE);
|
||||
if(!x11.glxContext)
|
||||
logFatal("FATAL ERROR: Unable to create GLX context");
|
||||
|
||||
// color map
|
||||
x11.colormap = XCreateColormap(x11.display,
|
||||
RootWindow(x11.display, x11.visual->screen),
|
||||
x11.visual->visual,
|
||||
AllocNone);
|
||||
|
||||
// setup window attributes
|
||||
XSetWindowAttributes wa;
|
||||
wa.colormap = x11.colormap;
|
||||
wa.border_pixel = 0;
|
||||
wa.event_mask = KeyPressMask | KeyReleaseMask |
|
||||
ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
|
||||
ExposureMask | VisibilityChangeMask |
|
||||
StructureNotifyMask | FocusChangeMask;
|
||||
|
||||
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,
|
||||
CWBorderPixel | CWColormap | CWEventMask,
|
||||
&wa);
|
||||
|
||||
if(!x11.window)
|
||||
logFatal("FATAL ERROR: Unable to create X window");
|
||||
|
||||
// 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("ERROR: Unable to create the input context");
|
||||
} else
|
||||
logError("ERROR: Failed to open an input method");
|
||||
} else
|
||||
logError("ERROR: 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
|
||||
glXMakeCurrent(x11.display, x11.window, x11.glxContext);
|
||||
|
||||
x11.width = width;
|
||||
x11.height = height;
|
||||
x11.maximizeOnFirstShow = maximized;
|
||||
|
||||
// call first onResize
|
||||
m_listener->onResize(Size(width, height));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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
|
||||
sleep(100);
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
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("ERROR: Couldn't create directory for saving configuration file. (",sdir.str(),")");
|
||||
return sdir.str();
|
||||
}
|
Reference in New Issue
Block a user