* basic core design (Platform, Engine, Graphics classes)
* logger * x11 platform implementation
This commit is contained in:
parent
ca2e22ec41
commit
f58ce52be8
|
@ -4,4 +4,7 @@ CMakeCache.txt
|
||||||
cmake_install.cmake
|
cmake_install.cmake
|
||||||
Makefile
|
Makefile
|
||||||
otclient
|
otclient
|
||||||
|
|
||||||
|
otclient.kdev4
|
||||||
|
CMakeLists.txt.user
|
||||||
!.gitignore
|
!.gitignore
|
||||||
|
|
|
@ -1,4 +1,61 @@
|
||||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
|
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
|
||||||
PROJECT(otclient)
|
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 @@
|
||||||
|
#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 @@
|
||||||
|
#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 @@
|
||||||
|
#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 @@
|
||||||
|
#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 @@
|
||||||
|
#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 @@
|
||||||
|
#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 @@
|
||||||
|
#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 @@
|
||||||
|
#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 @@
|
||||||
|
#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 @@
|
||||||
|
#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 @@
|
||||||
|
#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 @@
|
||||||
|
#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 @@
|
||||||
|
#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;
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
int main(int argc, const char *argv[])
|
|
||||||
{
|
|
||||||
std::cout << "Hello World!" << std::endl;
|
|
||||||
return 0;
|
|
||||||
}
|
|
Loading…
Reference in New Issue