* 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 | ||||
| Makefile | ||||
| otclient | ||||
| 
 | ||||
| otclient.kdev4 | ||||
| CMakeLists.txt.user | ||||
| !.gitignore | ||||
|  |  | |||
|  | @ -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 @@ | |||
| #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
	
	 Eduardo Bart
						Eduardo Bart