
16 changed files with 1359 additions and 8 deletions
@ -1,4 +1,61 @@
@@ -1,4 +1,61 @@
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.6) |
||||
PROJECT(otclient) |
||||
ADD_EXECUTABLE(otclient srcs/main.cpp) |
||||
|
||||
# find needed packages |
||||
SET(Boost_USE_STATIC_LIBS ON) |
||||
FIND_PACKAGE(Boost COMPONENTS thread filesystem REQUIRED) |
||||
FIND_PACKAGE(OpenGL REQUIRED) |
||||
FIND_PACKAGE(Lua51 REQUIRED) |
||||
|
||||
# choose a default build type if not specified |
||||
IF(NOT CMAKE_BUILD_TYPE) |
||||
SET(CMAKE_BUILD_TYPE RelWithDebInfo) |
||||
ENDIF(NOT CMAKE_BUILD_TYPE) |
||||
MESSAGE(STATUS "BUILD TYPE: " ${CMAKE_BUILD_TYPE}) |
||||
|
||||
# setup compiler options |
||||
IF(CMAKE_COMPILER_IS_GNUCXX) |
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wl,--as-needed") |
||||
SET(CMAKE_CXX_FLAGS_DEBUG "-O1 -g -ggdb -fno-inline") |
||||
SET(CMAKE_CXX_FLAGS_RELEASE "-O2 -Wl,-s") |
||||
SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") |
||||
ENDIF(CMAKE_COMPILER_IS_GNUCXX) |
||||
|
||||
INCLUDE_DIRECTORIES( |
||||
${LUA_INCLUDE_DIRS} |
||||
${Boost_INCLUDE_DIRS}) |
||||
|
||||
LINK_DIRECTORIES( |
||||
${Boost_LIBRARY_DIRS} |
||||
${LUA_LIBRARY_DIRS}) |
||||
|
||||
# setup definitions |
||||
ADD_DEFINITIONS(-D_REENTRANT) |
||||
|
||||
IF(CMAKE_BUILD_TYPE STREQUAL "Debug") |
||||
ADD_DEFINITIONS(-D_DEBUG) |
||||
ENDIF(CMAKE_BUILD_TYPE STREQUAL "Debug") |
||||
|
||||
# find sources |
||||
SET(SOURCES |
||||
src/main.cpp |
||||
src/engine.cpp |
||||
src/graphics.cpp |
||||
src/logger.cpp |
||||
src/util.cpp) |
||||
|
||||
IF(WIN32) |
||||
SET(SOURCES ${SOURCES} src/win32platform.cpp) |
||||
ELSE(WIN32) |
||||
SET(SOURCES ${SOURCES} src/x11platform.cpp) |
||||
ENDIF(WIN32) |
||||
|
||||
# target executable |
||||
ADD_EXECUTABLE(otclient ${SOURCES}) |
||||
|
||||
# target link libraries |
||||
TARGET_LINK_LIBRARIES(otclient |
||||
${Boost_LIBRARIES} |
||||
${OPENGL_LIBRARY} |
||||
${LUA51_LIBRARY}) |
||||
|
||||
|
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
#ifndef VERSION_H |
||||
#define VERSION_H |
||||
|
||||
#define APP_VERSION "0.1.0" |
||||
#define APP_NAME "OTClient" |
||||
#define APP_LONGNAME APP_NAME " " APP_VERSION |
||||
#define APP_ICON "data/otclient.bmp" |
||||
|
||||
#endif |
||||
|
@ -0,0 +1,105 @@
@@ -0,0 +1,105 @@
|
||||
#include "engine.h" |
||||
#include "platform.h" |
||||
#include "graphics.h" |
||||
#include "const.h" |
||||
#include "input.h" |
||||
|
||||
Engine g_engine; |
||||
|
||||
Engine::Engine() : |
||||
m_stopping(false), |
||||
m_running(false), |
||||
m_lastFrameTicks(0) |
||||
{ |
||||
} |
||||
|
||||
Engine::~Engine() |
||||
{ |
||||
} |
||||
|
||||
void Engine::init() |
||||
{ |
||||
Platform::init(); |
||||
|
||||
int width = 640; |
||||
int height = 480; |
||||
|
||||
// create the window
|
||||
Platform::createWindow(width, height, 550, 450); |
||||
Platform::setWindowTitle(APP_NAME); |
||||
Platform::setVsync(); |
||||
|
||||
// initialize graphics stuff
|
||||
g_graphics.init(); |
||||
|
||||
// finally show the window
|
||||
onResize(width, height); |
||||
Platform::showWindow(); |
||||
Platform::hideMouseCursor(); |
||||
} |
||||
|
||||
void Engine::terminate() |
||||
{ |
||||
Platform::showMouseCursor(); |
||||
Platform::terminate(); |
||||
g_graphics.terminate(); |
||||
} |
||||
|
||||
void Engine::run() |
||||
{ |
||||
unsigned long ticks; |
||||
|
||||
m_running = true; |
||||
m_lastFrameTicks = Platform::getTicks(); |
||||
|
||||
while(!m_stopping) { |
||||
// fire platform events
|
||||
Platform::poll(); |
||||
|
||||
// update
|
||||
ticks = Platform::getTicks(); |
||||
update(ticks - m_lastFrameTicks); |
||||
m_lastFrameTicks = ticks; |
||||
|
||||
// render
|
||||
render(); |
||||
|
||||
// swap buffers
|
||||
Platform::swapBuffers(); |
||||
} |
||||
|
||||
m_lastFrameTicks = 0; |
||||
m_stopping = false; |
||||
m_running = false; |
||||
} |
||||
|
||||
void Engine::stop() |
||||
{ |
||||
m_stopping = true; |
||||
} |
||||
|
||||
void Engine::render() |
||||
{ |
||||
g_graphics.beginRender(); |
||||
g_graphics.endRender(); |
||||
} |
||||
|
||||
void Engine::update(int elapsedTicks) |
||||
{ |
||||
|
||||
} |
||||
|
||||
void Engine::onClose() |
||||
{ |
||||
stop(); |
||||
} |
||||
|
||||
void Engine::onResize(int width, int height) |
||||
{ |
||||
|
||||
} |
||||
|
||||
void Engine::onInputEvent(InputEvent *event) |
||||
{ |
||||
|
||||
} |
@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
|
||||
#ifndef ENGINE_H |
||||
#define ENGINE_H |
||||
|
||||
struct InputEvent; |
||||
|
||||
class Engine |
||||
{ |
||||
public: |
||||
Engine(); |
||||
~Engine(); |
||||
|
||||
void init(); |
||||
void terminate(); |
||||
|
||||
void run(); |
||||
void stop(); |
||||
|
||||
bool isRunning() const { return m_running; } |
||||
bool isStopping() const { return m_stopping; } |
||||
unsigned long getLastFrameTicks() const { return m_lastFrameTicks; } |
||||
|
||||
// events fired by platform
|
||||
void onClose(); |
||||
void onResize(int width, int height); |
||||
void onInputEvent(InputEvent *event); |
||||
|
||||
private: |
||||
void render(); |
||||
void update(int elapsedTicks); |
||||
|
||||
bool m_stopping; |
||||
bool m_running; |
||||
|
||||
unsigned long m_lastFrameTicks; |
||||
}; |
||||
|
||||
extern Engine g_engine; |
||||
|
||||
#endif // ENGINE_H
|
||||
|
@ -0,0 +1,63 @@
@@ -0,0 +1,63 @@
|
||||
#include "graphics.h" |
||||
|
||||
#include <GL/gl.h> |
||||
#include <GL/glu.h> |
||||
|
||||
Graphics g_graphics; |
||||
|
||||
Graphics::Graphics() |
||||
{ |
||||
|
||||
} |
||||
|
||||
Graphics::~Graphics() |
||||
{ |
||||
|
||||
} |
||||
|
||||
void Graphics::init() |
||||
{ |
||||
// setup opengl
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // black background
|
||||
glEnable(GL_ALPHA_TEST); // enable alpha
|
||||
glAlphaFunc(GL_GREATER, 0.0f); // default alpha mode
|
||||
glDisable(GL_DEPTH_TEST); // we are rendering 2D only, we don't need it
|
||||
} |
||||
|
||||
void Graphics::terminate() |
||||
{ |
||||
|
||||
} |
||||
|
||||
void Graphics::resize(int width, int height) |
||||
{ |
||||
// resize gl viewport
|
||||
glViewport(0, 0, width, height); |
||||
|
||||
/*
|
||||
0,0---------0,w |
||||
| | |
||||
| | |
||||
| | |
||||
h,0---------h,w |
||||
*/ |
||||
// setup view region like above
|
||||
glMatrixMode(GL_PROJECTION); |
||||
glLoadIdentity(); |
||||
gluOrtho2D(0.0f, width, height, 0.0f); |
||||
|
||||
// back to model view
|
||||
glMatrixMode(GL_MODELVIEW); |
||||
glLoadIdentity(); |
||||
} |
||||
|
||||
void Graphics::beginRender() |
||||
{ |
||||
glClear(GL_COLOR_BUFFER_BIT); |
||||
glLoadIdentity(); |
||||
} |
||||
|
||||
void Graphics::endRender() |
||||
{ |
||||
|
||||
} |
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
#ifndef GRAPHICS_H |
||||
#define GRAPHICS_H |
||||
|
||||
class Graphics |
||||
{ |
||||
public: |
||||
Graphics(); |
||||
~Graphics(); |
||||
|
||||
void init(); |
||||
void terminate(); |
||||
|
||||
void resize(int width, int height); |
||||
|
||||
void beginRender(); |
||||
void endRender(); |
||||
}; |
||||
|
||||
extern Graphics g_graphics; |
||||
|
||||
#endif // GRAPHICS_H
|
@ -0,0 +1,187 @@
@@ -0,0 +1,187 @@
|
||||
#ifndef INPUT_H |
||||
#define INPUT_H |
||||
|
||||
enum EKeyCode { |
||||
KC_UNKNOWN = 0x00, |
||||
KC_ESCAPE = 0x01, |
||||
KC_1 = 0x02, |
||||
KC_2 = 0x03, |
||||
KC_3 = 0x04, |
||||
KC_4 = 0x05, |
||||
KC_5 = 0x06, |
||||
KC_6 = 0x07, |
||||
KC_7 = 0x08, |
||||
KC_8 = 0x09, |
||||
KC_9 = 0x0A, |
||||
KC_0 = 0x0B, |
||||
KC_MINUS = 0x0C, // - on main keyboard
|
||||
KC_EQUALS = 0x0D, |
||||
KC_BACK = 0x0E, // backspace
|
||||
KC_TAB = 0x0F, |
||||
KC_Q = 0x10, |
||||
KC_W = 0x11, |
||||
KC_E = 0x12, |
||||
KC_R = 0x13, |
||||
KC_T = 0x14, |
||||
KC_Y = 0x15, |
||||
KC_U = 0x16, |
||||
KC_I = 0x17, |
||||
KC_O = 0x18, |
||||
KC_P = 0x19, |
||||
KC_LBRACKET = 0x1A, |
||||
KC_RBRACKET = 0x1B, |
||||
KC_RETURN = 0x1C, // Enter on main keyboard
|
||||
KC_LCONTROL = 0x1D, |
||||
KC_A = 0x1E, |
||||
KC_S = 0x1F, |
||||
KC_D = 0x20, |
||||
KC_F = 0x21, |
||||
KC_G = 0x22, |
||||
KC_H = 0x23, |
||||
KC_J = 0x24, |
||||
KC_K = 0x25, |
||||
KC_L = 0x26, |
||||
KC_SEMICOLON = 0x27, |
||||
KC_APOSTROPHE = 0x28, |
||||
KC_GRAVE = 0x29, // accent
|
||||
KC_LSHIFT = 0x2A, |
||||
KC_BACKSLASH = 0x2B, |
||||
KC_Z = 0x2C, |
||||
KC_X = 0x2D, |
||||
KC_C = 0x2E, |
||||
KC_V = 0x2F, |
||||
KC_B = 0x30, |
||||
KC_N = 0x31, |
||||
KC_M = 0x32, |
||||
KC_COMMA = 0x33, |
||||
KC_PERIOD = 0x34, // . on main keyboard
|
||||
KC_SLASH = 0x35, // / on main keyboard
|
||||
KC_RSHIFT = 0x36, |
||||
KC_MULTIPLY = 0x37, // * on numeric keypad
|
||||
KC_LALT = 0x38, // left Alt
|
||||
KC_SPACE = 0x39, |
||||
KC_CAPITAL = 0x3A, |
||||
KC_F1 = 0x3B, |
||||
KC_F2 = 0x3C, |
||||
KC_F3 = 0x3D, |
||||
KC_F4 = 0x3E, |
||||
KC_F5 = 0x3F, |
||||
KC_F6 = 0x40, |
||||
KC_F7 = 0x41, |
||||
KC_F8 = 0x42, |
||||
KC_F9 = 0x43, |
||||
KC_F10 = 0x44, |
||||
KC_NUMLOCK = 0x45, |
||||
KC_SCROLL = 0x46, // Scroll Lock
|
||||
KC_NUMPAD7 = 0x47, |
||||
KC_NUMPAD8 = 0x48, |
||||
KC_NUMPAD9 = 0x49, |
||||
KC_SUBTRACT = 0x4A, // - on numeric keypad
|
||||
KC_NUMPAD4 = 0x4B, |
||||
KC_NUMPAD5 = 0x4C, |
||||
KC_NUMPAD6 = 0x4D, |
||||
KC_ADD = 0x4E, // + on numeric keypad
|
||||
KC_NUMPAD1 = 0x4F, |
||||
KC_NUMPAD2 = 0x50, |
||||
KC_NUMPAD3 = 0x51, |
||||
KC_NUMPAD0 = 0x52, |
||||
KC_DECIMAL = 0x53, // . on numeric keypad
|
||||
KC_OEM_102 = 0x56, // < > | on UK/Germany keyboards
|
||||
KC_F11 = 0x57, |
||||
KC_F12 = 0x58, |
||||
KC_F13 = 0x64, // (NEC PC98)
|
||||
KC_F14 = 0x65, // (NEC PC98)
|
||||
KC_F15 = 0x66, // (NEC PC98)
|
||||
KC_KANA = 0x70, // (Japanese keyboard)
|
||||
KC_ABNT_C1 = 0x73, // / ? on Portugese (Brazilian) keyboards
|
||||
KC_CONVERT = 0x79, // (Japanese keyboard)
|
||||
KC_NOCONVERT = 0x7B, // (Japanese keyboard)
|
||||
KC_YEN = 0x7D, // (Japanese keyboard)
|
||||
KC_ABNT_C2 = 0x7E, // Numpad . on Portugese (Brazilian) keyboards
|
||||
KC_NUMPADEQUALS= 0x8D, // = on numeric keypad (NEC PC98)
|
||||
KC_PREVTRACK = 0x90, // Previous Track (KC_CIRCUMFLEX on Japanese keyboard)
|
||||
KC_AT = 0x91, // (NEC PC98)
|
||||
KC_COLON = 0x92, // (NEC PC98)
|
||||
KC_UNDERLINE = 0x93, // (NEC PC98)
|
||||
KC_KANJI = 0x94, // (Japanese keyboard)
|
||||
KC_STOP = 0x95, // (NEC PC98)
|
||||
KC_AX = 0x96, // (Japan AX)
|
||||
KC_UNLABELED = 0x97, // (J3100)
|
||||
KC_NEXTTRACK = 0x99, // Next Track
|
||||
KC_NUMPADENTER = 0x9C, // Enter on numeric keypad
|
||||
KC_RCONTROL = 0x9D, |
||||
KC_MUTE = 0xA0, // Mute
|
||||
KC_CALCULATOR = 0xA1, // Calculator
|
||||
KC_PLAYPAUSE = 0xA2, // Play / Pause
|
||||
KC_MEDIASTOP = 0xA4, // Media Stop
|
||||
KC_VOLUMEDOWN = 0xAE, // Volume -
|
||||
KC_VOLUMEUP = 0xB0, // Volume +
|
||||
KC_WEBHOME = 0xB2, // Web home
|
||||
KC_NUMPADCOMMA = 0xB3, // , on numeric keypad (NEC PC98)
|
||||
KC_DIVIDE = 0xB5, // / on numeric keypad
|
||||
KC_SYSRQ = 0xB7, |
||||
KC_RALT = 0xB8, // right Alt
|
||||
KC_PAUSE = 0xC5, // Pause
|
||||
KC_HOME = 0xC7, // Home on arrow keypad
|
||||
KC_UP = 0xC8, // UpArrow on arrow keypad
|
||||
KC_PGUP = 0xC9, // PgUp on arrow keypad
|
||||
KC_LEFT = 0xCB, // LeftArrow on arrow keypad
|
||||
KC_RIGHT = 0xCD, // RightArrow on arrow keypad
|
||||
KC_END = 0xCF, // End on arrow keypad
|
||||
KC_DOWN = 0xD0, // DownArrow on arrow keypad
|
||||
KC_PGDOWN = 0xD1, // PgDn on arrow keypad
|
||||
KC_INSERT = 0xD2, // Insert on arrow keypad
|
||||
KC_DELETE = 0xD3, // Delete on arrow keypad
|
||||
KC_LWIN = 0xDB, // Left Windows key
|
||||
KC_RWIN = 0xDC, // Right Windows key
|
||||
KC_APPS = 0xDD, // AppMenu key
|
||||
KC_POWER = 0xDE, // System Power
|
||||
KC_SLEEP = 0xDF, // System Sleep
|
||||
KC_WAKE = 0xE3, // System Wake
|
||||
KC_WEBSEARCH = 0xE5, // Web Search
|
||||
KC_WEBFAVORITES= 0xE6, // Web Favorites
|
||||
KC_WEBREFRESH = 0xE7, // Web Refresh
|
||||
KC_WEBSTOP = 0xE8, // Web Stop
|
||||
KC_WEBFORWARD = 0xE9, // Web Forward
|
||||
KC_WEBBACK = 0xEA, // Web Back
|
||||
KC_MYCOMPUTER = 0xEB, // My Computer
|
||||
KC_MAIL = 0xEC, // Mail
|
||||
KC_MEDIASELECT = 0xED // Media Select
|
||||
}; |
||||
|
||||
enum EEvent { |
||||
EV_KEY_DOWN = 0, |
||||
EV_KEY_UP, |
||||
EV_TEXT_ENTER, |
||||
EV_MOUSE_LDOWN, |
||||
EV_MOUSE_LUP, |
||||
EV_MOUSE_MDOWN, |
||||
EV_MOUSE_MUP, |
||||
EV_MOUSE_RDOWN, |
||||
EV_MOUSE_RUP, |
||||
EV_MOUSE_WHEEL_UP, |
||||
EV_MOUSE_WHEEL_DOWN, |
||||
EV_MOUSE_MOVE |
||||
}; |
||||
|
||||
struct KeyEvent { |
||||
char keychar; |
||||
unsigned char keycode; |
||||
bool ctrl; |
||||
bool shift; |
||||
bool alt; |
||||
}; |
||||
|
||||
struct MouseEvent { |
||||
int x, y; |
||||
}; |
||||
|
||||
struct InputEvent { |
||||
EEvent type; |
||||
union { |
||||
KeyEvent key; |
||||
MouseEvent mouse; |
||||
}; |
||||
}; |
||||
|
||||
#endif // INPUT_H
|
@ -0,0 +1,53 @@
@@ -0,0 +1,53 @@
|
||||
#include "logger.h" |
||||
#include "util.h" |
||||
|
||||
#include <iostream> |
||||
#include <sstream> |
||||
#include <cstdarg> |
||||
#include <cstdlib> |
||||
#include <boost/algorithm/string.hpp> |
||||
|
||||
void _log(int level, const char *trace, const char *format, ...) |
||||
{ |
||||
va_list args; |
||||
std::stringstream out; |
||||
std::string strace; |
||||
|
||||
va_start(args, format); |
||||
std::string text = vformat(format, args); |
||||
va_end(args); |
||||
|
||||
if(trace) { |
||||
strace = trace; |
||||
strace = strace.substr(0, strace.find_first_of('(')); |
||||
if(strace.find_last_of(' ') != std::string::npos) |
||||
strace = strace.substr(strace.find_last_of(' ') + 1); |
||||
} |
||||
|
||||
#ifdef linux |
||||
static char const *colors[] = { "\033[01;31m ", "\033[01;31m", "\033[01;33m", "\033[0;32m", "\033[01;34m" }; |
||||
static bool colored = getenv("COLORED_OUTPUT"); |
||||
if(colored) |
||||
out << colors[level]; |
||||
#endif |
||||
|
||||
if(!strace.empty()) |
||||
out << "[" << strace << "] "; |
||||
|
||||
static char const *prefixes[] = { "FATAL ERROR: ", "ERROR: ", "WARNING: ", "", "", "" }; |
||||
out << prefixes[level]; |
||||
out << text; |
||||
|
||||
#ifdef linux |
||||
if(colored) |
||||
out << "\033[0m"; |
||||
#endif |
||||
|
||||
if(level <= LWARNING) |
||||
std::cerr << out.str() << std::endl; |
||||
else |
||||
std::cout << out.str() << std::endl; |
||||
|
||||
if(level == LFATAL) |
||||
exit(-1); |
||||
} |
@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
|
||||
#ifndef LOGGER_H |
||||
#define LOGGER_H |
||||
|
||||
#include <string> |
||||
|
||||
enum ELogLevel { |
||||
LFATAL = 0, |
||||
LERROR, |
||||
LWARNING, |
||||
LNOTICE, |
||||
LDEBUG |
||||
}; |
||||
|
||||
void _log(int level, const char *trace, const char *format, ...); |
||||
|
||||
#define fatal(...) _log(LFATAL, NULL, ## __VA_ARGS__) |
||||
#define error(...) _log(LERROR, NULL, ## __VA_ARGS__) |
||||
#define warning(...) _log(LWARNING, NULL, ## __VA_ARGS__) |
||||
#define debug(...) _log(LDEBUG, NULL, ## __VA_ARGS__) |
||||
#define notice(...) _log(LNOTICE, NULL, ## __VA_ARGS__) |
||||
|
||||
#endif |
@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
|
||||
#include "engine.h" |
||||
#include "const.h" |
||||
#include "logger.h" |
||||
|
||||
#include <csignal> |
||||
|
||||
// catches terminate signals to exit nicely
|
||||
void signal_handler(int sig) |
||||
{ |
||||
switch(sig) { |
||||
case SIGTERM: |
||||
case SIGINT: |
||||
case SIGQUIT: |
||||
{ |
||||
static bool stopping = false; |
||||
if(!stopping) { |
||||
stopping = true; |
||||
g_engine.stop(); |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
int main(int argc, const char *argv[]) |
||||
{ |
||||
// install our signal handler
|
||||
signal(SIGTERM, signal_handler); |
||||
signal(SIGINT, signal_handler); |
||||
signal(SIGQUIT, signal_handler); |
||||
|
||||
notice(APP_LONGNAME); |
||||
|
||||
// setup the engine and run
|
||||
g_engine.init(); |
||||
g_engine.run(); |
||||
g_engine.terminate(); |
||||
return 0; |
||||
} |
@ -0,0 +1,34 @@
@@ -0,0 +1,34 @@
|
||||
#ifndef PLATFORM_H |
||||
#define PLATFORM_H |
||||
|
||||
namespace Platform |
||||
{ |
||||
void init(); |
||||
void terminate(); |
||||
|
||||
void poll(); |
||||
|
||||
unsigned long getTicks(); |
||||
void sleep(unsigned long miliseconds); |
||||
|
||||
bool createWindow(int width, int height, int minWidth, int minHeight); |
||||
void destroyWindow(); |
||||
void showWindow(); |
||||
void setWindowTitle(const char *title); |
||||
bool isWindowFocused(); |
||||
bool isWindowVisible(); |
||||
|
||||
void *getExtensionProcAddress(const char *ext); |
||||
bool isExtensionSupported(const char *ext); |
||||
|
||||
const char *getTextFromClipboard(); |
||||
void copyToClipboard(const char *text); |
||||
|
||||
void hideMouseCursor(); |
||||
void showMouseCursor(); |
||||
|
||||
void setVsync(bool enable = true); |
||||
void swapBuffers(); |
||||
}; |
||||
|
||||
#endif // PLATFORM_H
|
@ -0,0 +1,30 @@
@@ -0,0 +1,30 @@
|
||||
#include "util.h" |
||||
|
||||
#include <cstdio> |
||||
|
||||
std::string vformat(const char *format, va_list args) |
||||
{ |
||||
if(!format) |
||||
return ""; |
||||
int result = -1, length = 256; |
||||
char *buffer = 0; |
||||
while(result == -1) { |
||||
if(buffer) |
||||
delete [] buffer; |
||||
buffer = new char [length + 1]; |
||||
result = vsnprintf(buffer, length, format, args); |
||||
length *= 2; |
||||
} |
||||
std::string s(buffer); |
||||
delete [] buffer; |
||||
return s; |
||||
} |
||||
|
||||
std::string format(const char *format, ...) |
||||
{ |
||||
va_list args; |
||||
va_start(args, format); |
||||
std::string s = vformat(format, args); |
||||
va_end(args); |
||||
return s; |
||||
} |
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
#ifndef UTIL_H |
||||
#define UTIL_H |
||||
|
||||
#include <string> |
||||
#include <cstdarg> |
||||
|
||||
std::string vformat(const char *format, va_list args); |
||||
std::string format(const char *format, ...); |
||||
|
||||
#endif |
@ -0,0 +1,684 @@
@@ -0,0 +1,684 @@
|
||||
#include "platform.h" |
||||
#include "engine.h" |
||||
#include "input.h" |
||||
#include "logger.h" |
||||
|
||||
#include <cstring> |
||||
#include <ctime> |
||||
|
||||
#include <string> |
||||
#include <algorithm> |
||||
#include <map> |
||||
|
||||
#include <sys/time.h> |
||||
#include <X11/Xlib.h> |
||||
#include <X11/Xatom.h> |
||||
#include <GL/glx.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; |
||||
bool visible; |
||||
bool focused; |
||||
int width; |
||||
int height; |
||||
std::string clipboardText; |
||||
std::map<int, unsigned char> keyMap; |
||||
} x11; |
||||
|
||||
void Platform::init() |
||||
{ |
||||
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; |
||||
|
||||
// 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; |
||||
|
||||
// open display
|
||||
x11.display = XOpenDisplay(0); |
||||
if(!x11.display) |
||||
fatal("Failed to open X display"); |
||||
|
||||
// check if GLX is supported on this display
|
||||
if(!glXQueryExtension(x11.display, 0, 0)) |
||||
fatal("GLX not supported"); |
||||
|
||||
// retrieve GLX version
|
||||
int glxMajor; |
||||
int glxMinor; |
||||
if(!glXQueryVersion(x11.display, &glxMajor, &glxMinor)) |
||||
fatal("Unable to query GLX version"); |
||||
notice("GLX version %d.%d", 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); |
||||
} |
||||
|
||||
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
|
||||
if(x11.width != event.xconfigure.width || x11.height != event.xconfigure.height) { |
||||
x11.width = event.xconfigure.width; |
||||
x11.height = event.xconfigure.height; |
||||
g_engine.onResize(x11.width, x11.height); |
||||
} |
||||
break; |
||||
|
||||
case KeyPress: |
||||
case KeyRelease: { |
||||
KeySym keysym; |
||||
char buf[32]; |
||||
int len; |
||||
|
||||
inputEvent.key.ctrl = (event.xkey.state & ControlMask); |
||||
inputEvent.key.shift = (event.xkey.state & ShiftMask); |
||||
inputEvent.key.alt = (event.xkey.state & Mod1Mask); |
||||
|
||||
// fire enter text event
|
||||
if(event.type == KeyPress && !inputEvent.key.ctrl && !inputEvent.key.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 |
||||
) { |
||||
inputEvent.type = EV_TEXT_ENTER; |
||||
inputEvent.key.keychar = buf[0]; |
||||
inputEvent.key.keycode = KC_UNKNOWN; |
||||
g_engine.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.key.keycode = x11.keyMap[keysym]; |
||||
inputEvent.type = (event.type == KeyPress) ? EV_KEY_DOWN : EV_KEY_UP; |
||||
inputEvent.key.keychar = (len > 0) ? buf[0] : 0; |
||||
g_engine.onInputEvent(&inputEvent); |
||||
} |
||||
break; |
||||
} |
||||
case ButtonPress: |
||||
case ButtonRelease: |
||||
switch(event.xbutton.button) { |
||||
case Button1: |
||||
inputEvent.type = (event.type == ButtonPress) ? EV_MOUSE_LDOWN : EV_MOUSE_LUP; |
||||
break; |
||||
case Button3: |
||||
inputEvent.type = (event.type == ButtonPress) ? EV_MOUSE_RDOWN : EV_MOUSE_RUP; |
||||
break; |
||||
case Button2: |
||||
inputEvent.type = (event.type == ButtonPress) ? EV_MOUSE_MDOWN : EV_MOUSE_MUP; |
||||
break; |
||||
case Button4: |
||||
inputEvent.type = EV_MOUSE_WHEEL_UP; |
||||
break; |
||||
case Button5: |
||||
inputEvent.type = EV_MOUSE_WHEEL_DOWN; |
||||
break; |
||||
} |
||||
g_engine.onInputEvent(&inputEvent); |
||||
break; |
||||
|
||||
case MotionNotify: |
||||
inputEvent.type = EV_MOUSE_MOVE; |
||||
inputEvent.mouse.x = event.xbutton.x; |
||||
inputEvent.mouse.y = event.xbutton.y; |
||||
g_engine.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, |
||||
(unsigned char *) &typeList, |
||||
sizeof(typeList)); |
||||
respond.xselection.property = req->property; |
||||
} else { |
||||
XChangeProperty(x11.display, |
||||
req->requestor, |
||||
req->property, req->target, |
||||
8, |
||||
PropModeReplace, |
||||
(unsigned char*) 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) |
||||
g_engine.onClose(); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
unsigned long Platform::getTicks() |
||||
{ |
||||
static timeval tv; |
||||
static unsigned long firstTick = 0; |
||||
|
||||
gettimeofday(&tv, 0); |
||||
if(!firstTick) |
||||
firstTick = tv.tv_sec; |
||||
|
||||
return ((tv.tv_sec - firstTick) * 1000) + (tv.tv_usec / 1000); |
||||
} |
||||
|
||||
void Platform::sleep(unsigned long miliseconds) |
||||
{ |
||||
timespec tv; |
||||
tv.tv_sec = miliseconds / 1000; |
||||
tv.tv_nsec = (miliseconds % 1000) * 1000000; |
||||
nanosleep(&tv, NULL); |
||||
} |
||||
|
||||
bool Platform::createWindow(int width, int height, int minWidth, int minHeight) |
||||
{ |
||||
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) |
||||
fatal("RGBA/Double buffered visual not supported"); |
||||
|
||||
// create GLX context
|
||||
x11.glxContext = glXCreateContext(x11.display, x11.visual, 0, GL_TRUE); |
||||
if(!x11.glxContext) |
||||
fatal("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; |
||||
|
||||
// calculate center position
|
||||
int x = (XDisplayHeight(x11.display, DefaultScreen(x11.display)) - width) / 2; |
||||
int y = (XDisplayHeight(x11.display, DefaultScreen(x11.display)) - height) / 2; |
||||
|
||||
// create the window
|
||||
x11.window = XCreateWindow(x11.display, |
||||
RootWindow(x11.display, x11.visual->screen), |
||||
x, y, |
||||
width, height, |
||||
0, |
||||
x11.visual->depth, |
||||
InputOutput, |
||||
x11.visual->visual, |
||||
CWBorderPixel | CWColormap | CWEventMask, |
||||
&wa); |
||||
|
||||
if(!x11.window) |
||||
fatal("Unable to create X window"); |
||||
|
||||
// setup locale to en_US.ISO-8859-1 characters
|
||||
// and create input context (to get special characters from input)
|
||||
if(setlocale(LC_ALL, "en_US.ISO-8859-1")) { |
||||
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) |
||||
error("Unable to create the input context"); |
||||
} else |
||||
error("Failed to open an input method"); |
||||
} else |
||||
error("X11 does not support the current locale"); |
||||
} |
||||
else |
||||
error("Failed setting locale to latin1"); |
||||
|
||||
if(!x11.xic) |
||||
warning("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; |
||||
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) { |
||||
XUnmapWindow(x11.display, x11.window); |
||||
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); |
||||
} |
||||
|
||||
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 true; |
||||
} |
||||
|
||||
const char *Platform::getTextFromClipboard() |
||||
{ |
||||
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; |
||||
unsigned long numItems, bytesLeft, dummy; |
||||
unsigned char *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::copyToClipboard(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::setVsync(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; |
||||
} |
Loading…
Reference in new issue