diff --git a/src/core/BufferView.cc b/src/core/BufferView.cc index e7cceae..ee341c0 100644 --- a/src/core/BufferView.cc +++ b/src/core/BufferView.cc @@ -170,6 +170,8 @@ bool BufferView::cursor_move(CursorMovement which, bool allow_eol) { case CursorMovement::LEFT: case CursorMovement::RIGHT: + case CursorMovement::SCREEN_ROW_UP: + case CursorMovement::SCREEN_ROW_DOWN: determine_new_cursor_screen_row(); break; case CursorMovement::UP: diff --git a/src/gui/BufferPane.cc b/src/gui/BufferPane.cc index e83f655..8dd0d76 100644 --- a/src/gui/BufferPane.cc +++ b/src/gui/BufferPane.cc @@ -201,8 +201,8 @@ void BufferPane::draw_buffer_character(uint32_t character, int screen_row, int s m_window->gl()->draw_rect(win_x(x + 2), win_y(y), m_window->font()->get_advance() * 2 - 4, 1, 0, 0.7, 1.0, 1.0); - m_window->gl()->draw_character(win_x(x), win_y(y), '^', *m_window->font(), 1.0, 1.0, 1.0, 1.0); - m_window->gl()->draw_character(win_x(col_x(screen_column + 1)), win_y(y), character | 0x40u, *m_window->font(), 1.0, 1.0, 1.0, 1.0); + m_window->gl()->draw_character(win_x(x), win_y(y), '^', *m_window->font(), 0.0, 0.7, 1.0, 1.0); + m_window->gl()->draw_character(win_x(col_x(screen_column + 1)), win_y(y), character | 0x40u, *m_window->font(), 0.0, 0.7, 1.0, 1.0); } else { diff --git a/src/gui/Window.cc b/src/gui/Window.cc index 95c6628..b115607 100644 --- a/src/gui/Window.cc +++ b/src/gui/Window.cc @@ -4,44 +4,16 @@ #include "BufferPane.h" #include #include "jes_icon-32x32.h" +#include +#include #define INITIAL_WIDTH 800 #define INITIAL_HEIGHT 800 #define FONT_SIZE 16 -struct -{ - SDL_TimerID timer_id; - SDL_Keycode keysym; - bool pressed; - bool event_pending; -} Key_Statuses[SDL_NUM_SCANCODES]; - -/** - * Initialize SDL. - * - * @retval true SDL was loaded successfully. - * @retval false Loading SDL failed. - */ -static bool Initialize_SDL() -{ - static bool initialized = false; - - if (initialized) - { - return true; - } - - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) - { - return false; - } - - atexit(SDL_Quit); - - initialized = true; - return true; -} +/* Process multiple queued events for up to this long before stopping to + * redraw the screen. */ +#define MAX_EVENT_PROCESS_TIME 50000 /** * Initialize OpenGL. @@ -80,6 +52,7 @@ static bool Initialize_OpenGL() return true; } +#if 0 void Window::set_window_icon() { SDL_Surface * surface = SDL_CreateRGBSurfaceFrom((void *)jes_icon_32x32, 32, 32, 24, 32 * 3, 0xFF0000u, 0xFF00u, 0xFFu, 0u); @@ -88,6 +61,7 @@ void Window::set_window_icon() SDL_FreeSurface(surface); } +#endif /** * Create a Window. @@ -97,28 +71,23 @@ void Window::set_window_icon() */ bool Window::create(std::shared_ptr buffer) { - if (!Initialize_SDL()) + if (!Jtk_Init()) { - std::cerr << "Error initializing SDL" << std::endl; + std::cerr << "Error initializing GUI" << std::endl; return false; } - m_window = SDL_CreateWindow( - APPNAME, - SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - INITIAL_WIDTH, - INITIAL_HEIGHT, - SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); + m_window = Jtk_CreateWindow(); if (m_window == NULL) { - std::cerr << "Error creating SDL window" << std::endl; + std::cerr << "Error creating window" << std::endl; return false; } + Jtk_SetWindowTitle(m_window, "jes"); +#if 0 set_window_icon(); - - (void)SDL_GL_CreateContext(m_window); +#endif if (!Initialize_OpenGL()) { @@ -157,7 +126,9 @@ bool Window::create(std::shared_ptr buffer) m_command_buffer_pane->set_command_mode(); m_command_buffer_screen_rows = 1; - resize(); + resize(INITIAL_WIDTH, INITIAL_HEIGHT); + + m_redraw_requested = true; return true; } @@ -169,76 +140,60 @@ bool Window::create(std::shared_ptr buffer) */ void Window::run_event_loop() { - SDL_Event event; + Jtk_Event event; - while ((!m_exit_requested) && SDL_WaitEvent(&event)) + while (!m_exit_requested) { - handle_event(event); if (m_redraw_requested) { m_redraw_requested = false; redraw(); } - } -} - -Uint32 Key_Repeat(Uint32 interval, void * param) -{ - if (Key_Statuses[(uintptr_t)param].pressed) - { - if (!Key_Statuses[(uintptr_t)param].event_pending) + Jtk_WaitEvent(&event); + uint64_t event_time = Jtk_UsTime(); + handle_event(event); + while (Jtk_CheckEvent(&event) && + ((Jtk_UsTime() - event_time) < MAX_EVENT_PROCESS_TIME)) { - SDL_Event event; - - event.user.code = 0; - event.user.data1 = (void *)(uintptr_t)Key_Statuses[(uintptr_t)param].keysym; - event.user.data2 = param; - event.type = SDL_USEREVENT; - - SDL_PushEvent(&event); - Key_Statuses[(uintptr_t)param].event_pending = true; + handle_event(event); } - - return 25u; - } - else - { - return 0u; } } /** - * Handle a SDL event. + * Handle a Jtk event. */ -void Window::handle_event(SDL_Event & event) +void Window::handle_event(Jtk_Event & event) { switch (event.type) { +#if 0 case SDL_QUIT: m_exit_requested = true; break; +#endif - case SDL_KEYDOWN: - m_keymod = event.key.keysym.mod; - if (event.key.repeat == 0) + case JTK_EVENT_KEY_PRESS: +#if 0 + if (!event.key.repeat) { - Key_Statuses[event.key.keysym.scancode].pressed = true; - Key_Statuses[event.key.keysym.scancode].keysym = event.key.keysym.sym; - Key_Statuses[event.key.keysym.scancode].timer_id = SDL_AddTimer(300, Key_Repeat, (void *)event.key.keysym.scancode); - handle_keysym(event.key.keysym.sym); + Jtk_BeginKeyRepeat(&event.key, 300u, 25u); } +#endif + handle_keypress(event.key.key); + m_redraw_requested = true; break; - case SDL_KEYUP: - m_keymod = event.key.keysym.mod; - Key_Statuses[event.key.keysym.scancode].pressed = false; - if (Key_Statuses[event.key.keysym.scancode].timer_id != 0) - { - SDL_RemoveTimer(Key_Statuses[event.key.keysym.scancode].timer_id); - Key_Statuses[event.key.keysym.scancode].timer_id = 0; - } + case JTK_EVENT_WINDOW_EXPOSE: + m_redraw_requested = true; break; + case JTK_EVENT_WINDOW_RESIZE: + resize(event.resize.width, event.resize.height); + m_redraw_requested = true; + break; + +#if 0 case SDL_WINDOWEVENT: switch (event.window.event) { @@ -251,18 +206,14 @@ void Window::handle_event(SDL_Event & event) break; } break; +#endif - case SDL_USEREVENT: - Key_Statuses[(uintptr_t)event.user.data2].event_pending = false; - handle_keysym((uint32_t)(uintptr_t)event.user.data1); - break; - - case SDL_MOUSEWHEEL: - if (event.wheel.y > 0) + case JTK_EVENT_BUTTON_PRESS: + if (event.button.button == 4) { m_focused_buffer_pane->scroll_window_up(ScrollMode::WHEEL); } - else + else if (event.button.button == 5) { m_focused_buffer_pane->scroll_window_down(ScrollMode::WHEEL); } @@ -270,43 +221,39 @@ void Window::handle_event(SDL_Event & event) } } -void Window::handle_keysym(uint32_t keysym) +void Window::handle_keypress(uint32_t keyval) { - handle_keyval(get_keyval(keysym)); -} - -void Window::handle_keyval(uint32_t keyval) -{ - switch (keyval) + uint32_t keycode = keyval & JTK_KEY_KEYCODE_MASK; + switch (keyval & (JTK_KEY_KEYCODE_MASK | JTK_KEY_MODS_CTRL)) { - case SDLK_HOME: + case JTK_KEY_HOME: m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::SOL); break; - case SDLK_END: + case JTK_KEY_END: m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::EOL); break; - case SDLK_RIGHT: + case JTK_KEY_RIGHT: m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::RIGHT); break; - case SDLK_LEFT: + case JTK_KEY_LEFT: m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::LEFT); break; - case SDLK_DOWN: + case JTK_KEY_DOWN: m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::DOWN); break; - case SDLK_UP: + case JTK_KEY_UP: m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::UP); break; - case SDLK_PAGEUP: + case JTK_KEY_PAGE_UP: m_focused_buffer_pane->scroll_window_up(ScrollMode::WHOLE_SCREEN); break; - case SDLK_PAGEDOWN: + case JTK_KEY_PAGE_DOWN: m_focused_buffer_pane->scroll_window_down(ScrollMode::WHOLE_SCREEN); break; default: if (m_focused_buffer_pane->insert_mode()) { - if (keyval == '\033') + if (keycode == '\033') { if (m_focused_buffer_pane == m_command_buffer_pane) { @@ -319,9 +266,9 @@ void Window::handle_keyval(uint32_t keyval) m_focused_buffer_pane->exit_insert_mode(); } } - else if (keyval < 0xFFu) + else if (keycode < 0xFFu) { - if ((keyval == '\n') && (m_focused_buffer_pane == m_command_buffer_pane)) + if ((keycode == '\n') && (m_focused_buffer_pane == m_command_buffer_pane)) { EncodedString command = m_command_buffer->get_string(); m_command_buffer_pane->clear(); @@ -331,13 +278,13 @@ void Window::handle_keyval(uint32_t keyval) } else { - m_focused_buffer_pane->insert_code_point(keyval); + m_focused_buffer_pane->insert_code_point(keycode); } } } else { - switch (keyval) + switch (keyval & (JTK_KEY_KEYCODE_MASK | JTK_KEY_MODS_CTRL)) { case '0': m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::SOL); @@ -400,22 +347,22 @@ void Window::handle_keyval(uint32_t keyval) case 'x': m_focused_buffer_pane->kill_character_at_cursor(); break; - case Keymod::CTRL + 'b': + case JTK_KEY_MODS_CTRL + 'b': m_focused_buffer_pane->scroll_window_up(ScrollMode::WHOLE_SCREEN); break; - case Keymod::CTRL + 'd': + case JTK_KEY_MODS_CTRL + 'd': m_focused_buffer_pane->scroll_window_down(ScrollMode::HALF_SCREEN); break; - case Keymod::CTRL + 'e': + case JTK_KEY_MODS_CTRL + 'e': m_focused_buffer_pane->scroll_window_down(ScrollMode::ONE_LINE); break; - case Keymod::CTRL + 'f': + case JTK_KEY_MODS_CTRL + 'f': m_focused_buffer_pane->scroll_window_down(ScrollMode::WHOLE_SCREEN); break; - case Keymod::CTRL + 'u': + case JTK_KEY_MODS_CTRL + 'u': m_focused_buffer_pane->scroll_window_up(ScrollMode::HALF_SCREEN); break; - case Keymod::CTRL + 'y': + case JTK_KEY_MODS_CTRL + 'y': m_focused_buffer_pane->scroll_window_up(ScrollMode::ONE_LINE); break; } @@ -424,9 +371,10 @@ void Window::handle_keyval(uint32_t keyval) } } -void Window::resize() +void Window::resize(size_t width, size_t height) { - SDL_GetWindowSize(m_window, &m_width, &m_height); + m_width = width; + m_height = height; glViewport(0, 0, m_width, m_height); m_gl->resize(m_width, m_height); int command_buffer_height = m_command_buffer_screen_rows * m_font->get_line_height(); @@ -443,7 +391,7 @@ void Window::redraw() m_command_buffer_screen_rows = 1; if (m_command_buffer_screen_rows != last_command_buffer_screen_rows) { - resize(); + resize(m_width, m_height); } last_command_buffer_screen_rows = m_command_buffer_screen_rows; @@ -453,112 +401,7 @@ void Window::redraw() m_gl->draw_rect(0, m_command_buffer_screen_rows * m_font->get_line_height(), m_width, 1, 0.5, 0.5, 0.5, 1.0); m_command_buffer_pane->draw(); - SDL_GL_SwapWindow(m_window); -} - -uint32_t Window::get_keyval(SDL_Keycode keysym) -{ - uint32_t keyval = keysym; - if ((m_keymod & (KMOD_SHIFT | KMOD_CAPS)) != 0u) - { - uint32_t shifted = get_shifted(keyval); - if (shifted != 0u) - { - keyval = shifted; - } - else - { - keyval |= Keymod::SHIFT; - } - } - switch (keyval) - { - case SDLK_RETURN: keyval = '\n'; break; - case SDLK_KP_DIVIDE: keyval = '/'; break; - case SDLK_KP_MULTIPLY: keyval = '*'; break; - case SDLK_KP_MINUS: keyval = '-'; break; - case SDLK_KP_PLUS: keyval = '+'; break; - case SDLK_KP_ENTER: keyval = '\n'; break; - case SDLK_KP_1: keyval = '1'; break; - case SDLK_KP_2: keyval = '2'; break; - case SDLK_KP_3: keyval = '3'; break; - case SDLK_KP_4: keyval = '4'; break; - case SDLK_KP_5: keyval = '5'; break; - case SDLK_KP_6: keyval = '6'; break; - case SDLK_KP_7: keyval = '7'; break; - case SDLK_KP_8: keyval = '8'; break; - case SDLK_KP_9: keyval = '9'; break; - case SDLK_KP_0: keyval = '0'; break; - case SDLK_KP_PERIOD: keyval = '.'; break; - case SDLK_KP_EQUALS: keyval = '='; break; - case SDLK_KP_COMMA: keyval = ','; break; - case SDLK_KP_LEFTPAREN: keyval = '('; break; - case SDLK_KP_RIGHTPAREN: keyval = ')'; break; - case SDLK_KP_LEFTBRACE: keyval = '{'; break; - case SDLK_KP_RIGHTBRACE: keyval = '}'; break; - case SDLK_KP_TAB: keyval = '\t'; break; - case SDLK_KP_BACKSPACE: keyval = '\b'; break; - case SDLK_KP_PERCENT: keyval = '%'; break; - case SDLK_KP_LESS: keyval = '<'; break; - case SDLK_KP_GREATER: keyval = '>'; break; - case SDLK_KP_AMPERSAND: keyval = '&'; break; - case SDLK_KP_VERTICALBAR: keyval = '|'; break; - case SDLK_KP_COLON: keyval = ':'; break; - case SDLK_KP_HASH: keyval = '#'; break; - case SDLK_KP_SPACE: keyval = ' '; break; - case SDLK_KP_AT: keyval = '@'; break; - case SDLK_KP_EXCLAM: keyval = '!'; break; - } - if ((m_keymod & KMOD_CTRL) != 0u) - { - keyval |= Keymod::CTRL; - } - if ((m_keymod & KMOD_ALT) != 0u) - { - keyval |= Keymod::ALT; - } - if ((m_keymod & KMOD_GUI) != 0u) - { - keyval |= Keymod::GUI; - } - return keyval; -} - -uint32_t Window::get_shifted(uint32_t keysym) -{ - if ((keysym >= 'a') && (keysym <= 'z')) - { - return keysym - ('a' - 'A'); - } - - switch (keysym) - { - case SDLK_QUOTE: return '"'; - case SDLK_COMMA: return '<'; - case SDLK_MINUS: return '_'; - case SDLK_PERIOD: return '>'; - case SDLK_SLASH: return '?'; - case SDLK_0: return ')'; - case SDLK_1: return '!'; - case SDLK_2: return '@'; - case SDLK_3: return '#'; - case SDLK_4: return '$'; - case SDLK_5: return '%'; - case SDLK_6: return '^'; - case SDLK_7: return '&'; - case SDLK_8: return '*'; - case SDLK_9: return '('; - case SDLK_SEMICOLON: return ':'; - case SDLK_EQUALS: return '+'; - case SDLK_LEFTBRACKET: return '{'; - case SDLK_BACKSLASH: return '|'; - case SDLK_RIGHTBRACKET: return '}'; - case SDLK_BACKQUOTE: return '~'; - case SDLK_SPACE: return ' '; - case SDLK_BACKSPACE: return '\b'; - } - - return 0u; + Jtk_SwapBuffers(m_window); } void Window::change_focus(std::shared_ptr buffer_pane) diff --git a/src/gui/Window.h b/src/gui/Window.h index 759d022..5ae05bf 100644 --- a/src/gui/Window.h +++ b/src/gui/Window.h @@ -3,30 +3,18 @@ #include #include -#include #include "Font.h" #include "Buffer.h" #include "GL.h" #include "EncodedString.h" #include "CommandParser.h" +#include "Jtk.h" class BufferPane; class Window { public: - class Keymod - { - public: - enum : uint32_t - { - CTRL = 0x10000, - ALT = 0x20000, - SHIFT = 0x40000, - GUI = 0x80000, - }; - }; - enum class ScrollMode : uint8_t { ONE_LINE, @@ -53,21 +41,24 @@ public: protected: - void resize(); + void resize(size_t width, size_t height); void redraw(); - void handle_event(SDL_Event & event); - void handle_keysym(uint32_t keysym); - void handle_keyval(uint32_t keyval); + void handle_event(Jtk_Event & event); + void handle_keypress(uint32_t keyval); +#if 0 uint32_t get_keyval(SDL_Keycode keysym); uint32_t get_shifted(uint32_t keysym); +#endif void change_focus(std::shared_ptr buffer_pane); +#if 0 void set_window_icon(); +#endif void handle_command(const EncodedString & command); void command_write_file(const CommandParser & cp); void command_quit(const CommandParser & cp); - SDL_Window * m_window; + void * m_window; bool m_exit_requested; bool m_redraw_requested; int m_width; @@ -83,8 +74,6 @@ protected: std::shared_ptr m_focused_buffer_pane; std::shared_ptr m_command_buffer; std::shared_ptr m_command_buffer_pane; - - Uint16 m_keymod; }; #endif diff --git a/src/gui/jtk/Jtk.cc b/src/gui/jtk/Jtk.cc new file mode 100644 index 0000000..f150c72 --- /dev/null +++ b/src/gui/jtk/Jtk.cc @@ -0,0 +1,59 @@ +#include "Jtk.h" + +#ifdef JTK_X + +#include +#include + +Display * g_display; +XVisualInfo * g_vi; +XSetWindowAttributes g_swa; +GLXContext g_context; + +/** + * Initialize the Jtk subsystem. + * + * @return true on success, false on failure + */ +bool Jtk_Init() +{ + static int glx_attribute_list[] = { + GLX_RGBA, + GLX_DOUBLEBUFFER, + None, + }; + + g_display = XOpenDisplay(NULL); + if (g_display == NULL) + { + fprintf(stderr, "XOpenDisplay() failure\n"); + return false; + } + + g_vi = glXChooseVisual(g_display, DefaultScreen(g_display), + glx_attribute_list); + if (g_vi == NULL) + { + fprintf(stderr, "glXChooseVisual() failure\n"); + return false; + } + + g_context = glXCreateContext(g_display, g_vi, NULL, True); + if (g_context == NULL) + { + fprintf(stderr, "glXCreateContext() failure\n"); + return false; + } + + Colormap colormap = XCreateColormap(g_display, + RootWindow(g_display, g_vi->screen), g_vi->visual, AllocNone); + g_swa.colormap = colormap; + g_swa.border_pixel = 0; + g_swa.event_mask = StructureNotifyMask | + KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask; + + return true; +} + +#endif diff --git a/src/gui/jtk/Jtk.h b/src/gui/jtk/Jtk.h new file mode 100644 index 0000000..7eccbf1 --- /dev/null +++ b/src/gui/jtk/Jtk.h @@ -0,0 +1,12 @@ +#ifndef JTK_H +#define JTK_H + +#include "Jtk_event.h" +#include "Jtk_keys.h" +#include "Jtk_time.h" +#include "Jtk_timer.h" +#include "Jtk_window.h" + +bool Jtk_Init(); + +#endif diff --git a/src/gui/jtk/Jtk_event.cc b/src/gui/jtk/Jtk_event.cc new file mode 100644 index 0000000..4cb2276 --- /dev/null +++ b/src/gui/jtk/Jtk_event.cc @@ -0,0 +1,460 @@ +#include "Jtk.h" + +#ifdef JTK_X + +#include +#include +#include +#include +#include "Jtk_internal.h" +#include + +/** Do not wait longer than 100ms */ +#define MAX_WAIT_TIME 100000u + +static std::unordered_map g_repeating_keys; +static std::unordered_map g_key_repeat_timers; + +#if 0 +static void StopKeyRepeat(unsigned int x_keycode) +{ + auto it = g_repeating_keys.find(x_keycode); + if (it != g_repeating_keys.end()) + { + size_t timer_id = it->second; + Jtk_RemoveTimer(timer_id); + g_repeating_keys.erase(x_keycode); + g_key_repeat_timers.erase(timer_id); + } +} +#endif + +#if 0 +static unsigned int GetXState() +{ + Window win; + int i; + unsigned int state; + XQueryPointer(g_display, RootWindow(g_display, DefaultScreen(g_display)), &win, &win, &i, &i, &i, &i, &state); + return state; +} +#endif + +static uint32_t XStateToJtkKeyMods(unsigned int x_state) +{ + uint32_t mods = 0u; + /* OR in the modifier states */ + if (x_state & ShiftMask) + { + mods |= JTK_KEY_MODS_SHIFT; + } + if (x_state & LockMask) + { + mods |= JTK_KEY_MODS_LOCK; + } + if (x_state & ControlMask) + { + mods |= JTK_KEY_MODS_CTRL; + } + return mods; +} + +static uint32_t XKeyToJtkKey(unsigned int x_keycode, unsigned int x_state) +{ + XKeyEvent x_key_event; + x_key_event.type = KeyPress; + x_key_event.display = g_display; + /* Turn off the ControlMask bit for looking up keys. We'll handle control + * keys ourselves. */ + x_key_event.state = x_state & ~ControlMask; + x_key_event.keycode = x_keycode; + char buffer; + KeySym keysym; + uint32_t key = JTK_KEY_UNKNOWN; + + if (XLookupString(&x_key_event, &buffer, 1, &keysym, nullptr) > 0) + { + if (buffer == '\r') + { + key = '\n'; + } + else + { + key = buffer; + } + } + else + { + switch (keysym) + { + case XK_F1: key = JTK_KEY_F1; break; + case XK_F2: key = JTK_KEY_F2; break; + case XK_F3: key = JTK_KEY_F3; break; + case XK_F4: key = JTK_KEY_F4; break; + case XK_F5: key = JTK_KEY_F5; break; + case XK_F6: key = JTK_KEY_F6; break; + case XK_F7: key = JTK_KEY_F7; break; + case XK_F8: key = JTK_KEY_F8; break; + case XK_F9: key = JTK_KEY_F9; break; + case XK_F10: key = JTK_KEY_F10; break; + case XK_F11: key = JTK_KEY_F11; break; + case XK_F12: key = JTK_KEY_F12; break; + case XK_F13: key = JTK_KEY_F13; break; + case XK_F14: key = JTK_KEY_F14; break; + case XK_F15: key = JTK_KEY_F15; break; + case XK_F16: key = JTK_KEY_F16; break; + case XK_F17: key = JTK_KEY_F17; break; + case XK_F18: key = JTK_KEY_F18; break; + case XK_F19: key = JTK_KEY_F19; break; + case XK_F20: key = JTK_KEY_F20; break; + case XK_F21: key = JTK_KEY_F21; break; + case XK_F22: key = JTK_KEY_F22; break; + case XK_F23: key = JTK_KEY_F23; break; + case XK_F24: key = JTK_KEY_F24; break; + case XK_F25: key = JTK_KEY_F25; break; + case XK_F26: key = JTK_KEY_F26; break; + case XK_F27: key = JTK_KEY_F27; break; + case XK_F28: key = JTK_KEY_F28; break; + case XK_F29: key = JTK_KEY_F29; break; + case XK_F30: key = JTK_KEY_F30; break; + case XK_F31: key = JTK_KEY_F31; break; + + case XK_Shift_L: key = JTK_KEY_SHIFT_L; break; + case XK_Shift_R: key = JTK_KEY_SHIFT_R; break; + case XK_Control_L: key = JTK_KEY_CTRL_L; break; + case XK_Control_R: key = JTK_KEY_CTRL_R; break; + case XK_Caps_Lock: key = JTK_KEY_CAPS_LOCK; break; + case XK_Shift_Lock: key = JTK_KEY_SHIFT_LOCK;break; + + case XK_Meta_L: key = JTK_KEY_META_L; break; + case XK_Meta_R: key = JTK_KEY_META_R; break; + case XK_Alt_L: key = JTK_KEY_ALT_L; break; + case XK_Alt_R: key = JTK_KEY_ALT_R; break; + case XK_Super_L: key = JTK_KEY_SUPER_L; break; + case XK_Super_R: key = JTK_KEY_SUPER_R; break; + + case XK_Home: key = JTK_KEY_HOME; break; + case XK_Left: key = JTK_KEY_LEFT; break; + case XK_Up: key = JTK_KEY_UP; break; + case XK_Right: key = JTK_KEY_RIGHT; break; + case XK_Down: key = JTK_KEY_DOWN; break; + case XK_Page_Up: key = JTK_KEY_PAGE_UP; break; + case XK_Page_Down: key = JTK_KEY_PAGE_DOWN; break; + case XK_End: key = JTK_KEY_END; break; + case XK_Begin: key = JTK_KEY_BEGIN; break; + + case XK_Select: key = JTK_KEY_SELECT; break; + case XK_Print: key = JTK_KEY_PRINT; break; + case XK_Execute: key = JTK_KEY_EXECUTE; break; + case XK_Insert: key = JTK_KEY_INSERT; break; + case XK_Undo: key = JTK_KEY_UNDO; break; + case XK_Redo: key = JTK_KEY_REDO; break; + case XK_Menu: key = JTK_KEY_MENU; break; + case XK_Find: key = JTK_KEY_FIND; break; + case XK_Cancel: key = JTK_KEY_CANCEL; break; + case XK_Help: key = JTK_KEY_HELP; break; + case XK_Break: key = JTK_KEY_BREAK; break; + case XK_Num_Lock: key = JTK_KEY_NUM_LOCK; break; + + case XK_KP_Space: key = JTK_KEY_KP_SPACE; break; + case XK_KP_Tab: key = JTK_KEY_KP_TAB; break; + case XK_KP_Enter: key = JTK_KEY_KP_ENTER; break; + case XK_KP_F1: key = JTK_KEY_KP_F1; break; + case XK_KP_F2: key = JTK_KEY_KP_F2; break; + case XK_KP_F3: key = JTK_KEY_KP_F3; break; + case XK_KP_F4: key = JTK_KEY_KP_F4; break; + case XK_KP_Home: key = JTK_KEY_KP_HOME; break; + case XK_KP_Left: key = JTK_KEY_KP_LEFT; break; + case XK_KP_Up: key = JTK_KEY_KP_UP; break; + case XK_KP_Right: key = JTK_KEY_KP_RIGHT; break; + case XK_KP_Down: key = JTK_KEY_KP_DOWN; break; + case XK_KP_Page_Up: key = JTK_KEY_KP_PAGE_UP; break; + case XK_KP_Page_Down: key = JTK_KEY_KP_PAGE_DOWN; break; + case XK_KP_End: key = JTK_KEY_KP_END; break; + case XK_KP_Begin: key = JTK_KEY_KP_BEGIN; break; + case XK_KP_Insert: key = JTK_KEY_KP_INSERT; break; + case XK_KP_Delete: key = JTK_KEY_KP_DELETE; break; + case XK_KP_Equal: key = JTK_KEY_KP_EQUAL; break; + case XK_KP_Multiply: key = JTK_KEY_KP_MULTIPLY; break; + case XK_KP_Add: key = JTK_KEY_KP_ADD; break; + case XK_KP_Separator: key = JTK_KEY_KP_SEPARATOR; break; + case XK_KP_Subtract: key = JTK_KEY_KP_SUBTRACT; break; + case XK_KP_Decimal: key = JTK_KEY_KP_DECIMAL; break; + case XK_KP_Divide: key = JTK_KEY_KP_DIVIDE; break; + + case XK_KP_0: key = JTK_KEY_KP_0; break; + case XK_KP_1: key = JTK_KEY_KP_1; break; + case XK_KP_2: key = JTK_KEY_KP_2; break; + case XK_KP_3: key = JTK_KEY_KP_3; break; + case XK_KP_4: key = JTK_KEY_KP_4; break; + case XK_KP_5: key = JTK_KEY_KP_5; break; + case XK_KP_6: key = JTK_KEY_KP_6; break; + case XK_KP_7: key = JTK_KEY_KP_7; break; + case XK_KP_8: key = JTK_KEY_KP_8; break; + case XK_KP_9: key = JTK_KEY_KP_9; break; + } + } + + /* OR in the modifier states */ + key |= XStateToJtkKeyMods(x_state); + + return key; +} + +#if 0 +static Bool KeyRepeatCheckIfEvent(Display * display, XEvent * chkev, + XPointer arg) +{ + XEvent * release_event = (XEvent *)arg; + if (chkev->type == KeyPress && + chkev->xkey.keycode == release_event->xkey.keycode && + chkev->xkey.time - release_event->xkey.time < 2) + return True; + return False; +} + +/** + * Check to see if this is a repeated key. + */ +static bool IsRepeatKey(Display * display, XEvent * event) +{ + XEvent dummyev; + if (XPending(display)) + { + if (XCheckIfEvent(display, &dummyev, KeyRepeatCheckIfEvent, + (XPointer)event) == True) + { + return true; + } + } + return false; +} +#endif + +static Bool MatchKeyPress(Display * display, XEvent * event, XPointer arg) +{ + XEvent * match_event = (XEvent *)arg; + return (event->type == match_event->type) && + (event->xkey.window == match_event->xkey.window) && + (event->xkey.state == match_event->xkey.state) && + (event->xkey.keycode == match_event->xkey.keycode); +} + +/** + * Process an X key press event. + * + * @param x_event + * Pointer to the X event. + * @param event + * Pointer to the Jtk event. + */ +static bool ProcessXKeyPressEvent(XEvent * x_event, Jtk_Event * event) +{ + unsigned int x_keycode = x_event->xkey.keycode; + event->type = JTK_EVENT_KEY_PRESS; + event->key.repeat = false; + event->key.key = XKeyToJtkKey(x_keycode, x_event->xkey.state); + event->key.x_keycode = x_keycode; + /* Remove any following keypress events for the same keycode from the X + * queue. */ + XEvent remove_event; + while (XCheckIfEvent(g_display, &remove_event, MatchKeyPress, (XPointer)x_event) == True) + { + } + return true; +} + +/** + * Process an X key release event. + * + * @param x_event + * Pointer to the X event. + * @param event + * Pointer to the Jtk event. + */ +static bool ProcessXKeyReleaseEvent(XEvent * x_event, Jtk_Event * event) +{ +#if 0 + if (IsRepeatKey(g_display, x_event)) + { + return false; + } + StopKeyRepeat(x_event->xkey.keycode); +#endif + return false; +} + +static Bool MatchButtonPress(Display * display, XEvent * event, XPointer arg) +{ + XEvent * match_event = (XEvent *)arg; + return (event->type == match_event->type) && + (event->xbutton.window == match_event->xbutton.window) && + (event->xbutton.state == match_event->xbutton.state) && + (event->xbutton.button == match_event->xbutton.button); +} + +/** + * Process an X button press event. + */ +static bool ProcessXButtonPressEvent(XEvent * x_event, Jtk_Event * event) +{ + event->type = JTK_EVENT_BUTTON_PRESS; + event->button.mods = XStateToJtkKeyMods(x_event->xbutton.state); + event->button.button = x_event->xbutton.button; + /* If this is a mouse wheel scroll event, remove any following scroll + * events. */ + if ((event->button.button == 4) || (event->button.button == 5)) + { + XEvent remove_event; + while (XCheckIfEvent(g_display, &remove_event, MatchButtonPress, (XPointer)x_event) == True) + { + } + } + return true; +} + +/** + * Process an X configure event. + * + * @param x_event + * Pointer to the X event. + * @param event + * Pointer to the Jtk event. + */ +static bool ProcessConfigureEvent(XEvent * x_event, Jtk_Event * event) +{ + event->type = JTK_EVENT_WINDOW_RESIZE; + event->resize.width = x_event->xconfigure.width; + event->resize.height = x_event->xconfigure.height; + return true; +} + +/** + * Process an X event. + * + * @param x_event + * Pointer to the X event. + * @param event + * Pointer to the Jtk event. + * + * @retval true + * The event should be passed to the user and event has been filled in. + * @retval false + * The event should not be passed to the user. + */ +static bool ProcessXEvent(XEvent * x_event, Jtk_Event * event) +{ + switch (x_event->type) + { + case KeyPress: + return ProcessXKeyPressEvent(x_event, event); + + case KeyRelease: + return ProcessXKeyReleaseEvent(x_event, event); + + case ButtonPress: + return ProcessXButtonPressEvent(x_event, event); + + case ButtonRelease: + break; + + case Expose: + event->type = JTK_EVENT_WINDOW_EXPOSE; + return true; + + case GraphicsExpose: + event->type = JTK_EVENT_WINDOW_EXPOSE; + return true; + + case MapNotify: + event->type = JTK_EVENT_WINDOW_EXPOSE; + return true; + + case ConfigureNotify: + return ProcessConfigureEvent(x_event, event); + + case MappingNotify: + XRefreshKeyboardMapping(&x_event->xmapping); + return false; + } + + return false; +} + +bool Jtk_CheckEvent(Jtk_Event * event) +{ + /* First check for an X event. */ + while (XPending(g_display) > 0) + { + XEvent x_event; + XNextEvent(g_display, &x_event); + if (ProcessXEvent(&x_event, event)) + return true; + } + + /* Next check if any timer has expired. */ + size_t timer_id = Jtk_GetExpiredTimer(); + if (timer_id != (size_t)-1) + { +#if 0 + auto it = g_key_repeat_timers.find(timer_id); + if (it != g_key_repeat_timers.end()) + { + event->type = JTK_EVENT_KEY_PRESS; + event->key.repeat = true; + event->key.key = XKeyToJtkKey(it->second); + event->key.x_keycode = it->second; + } + else + { +#endif + event->type = JTK_EVENT_TIMER; + event->timer.timer_id = timer_id; + event->timer.user1 = Jtk_GetTimerUserData(timer_id, 0u); + event->timer.user2 = Jtk_GetTimerUserData(timer_id, 1u); +#if 0 + } +#endif + Jtk_ServiceTimer(timer_id); + return true; + } + + return false; +} + +void Jtk_WaitEvent(Jtk_Event * event) +{ + for (;;) + { + if (Jtk_CheckEvent(event)) + return; + + /* Wait for something to happen. */ + uint64_t time_to_wait = Jtk_TimeToNextTimerExpiration(); + if (time_to_wait > MAX_WAIT_TIME) + { + time_to_wait = MAX_WAIT_TIME; + } + + int x_fd = ConnectionNumber(g_display); + fd_set fds; + FD_ZERO(&fds); + FD_SET(x_fd, &fds); + struct timeval tv; + tv.tv_sec = time_to_wait / 1000000u; + tv.tv_usec = time_to_wait % 1000000u; + select(x_fd + 1, &fds, nullptr, nullptr, &tv); + } +} + +#if 0 +void Jtk_BeginKeyRepeat(Jtk_KeyEvent * key_event, uint32_t delay, uint32_t interval) +{ + StopKeyRepeat(key_event->x_keycode); + size_t timer_id = Jtk_AddTimer(delay, interval, nullptr, nullptr); + g_repeating_keys[key_event->x_keycode] = timer_id; + g_key_repeat_timers[timer_id] = key_event->x_keycode; +} +#endif + +#endif diff --git a/src/gui/jtk/Jtk_event.h b/src/gui/jtk/Jtk_event.h new file mode 100644 index 0000000..c8e910d --- /dev/null +++ b/src/gui/jtk/Jtk_event.h @@ -0,0 +1,62 @@ +#ifndef JTK_EVENT_H +#define JTK_EVENT_H + +#include +#include + +#define JTK_EVENT_CLOSE_WINDOW 1u +#define JTK_EVENT_WINDOW_EXPOSE 2u +#define JTK_EVENT_KEY_PRESS 3u +#define JTK_EVENT_KEY_RELEASE 4u +#define JTK_EVENT_BUTTON_PRESS 5u +#define JTK_EVENT_BUTTON_RELEASE 6u +#define JTK_EVENT_TIMER 7u +#define JTK_EVENT_WINDOW_RESIZE 8u + +typedef struct +{ + bool repeat; + uint32_t key; +#ifdef JTK_X + unsigned int x_keycode; +#endif +} Jtk_KeyEvent; + +typedef struct +{ + uint32_t mods; + uint8_t button; +} Jtk_ButtonEvent; + +typedef struct +{ + size_t timer_id; + void * user1; + void * user2; +} Jtk_TimerEvent; + +typedef struct +{ + size_t width; + size_t height; +} Jtk_WindowResizeEvent; + +typedef struct +{ + uint8_t type; + union + { + Jtk_KeyEvent key; + Jtk_ButtonEvent button; + Jtk_TimerEvent timer; + Jtk_WindowResizeEvent resize; + }; +} Jtk_Event; + +bool Jtk_CheckEvent(Jtk_Event * event); +void Jtk_WaitEvent(Jtk_Event * event); +#if 0 +void Jtk_BeginKeyRepeat(Jtk_KeyEvent * key_event, uint32_t delay, uint32_t interval); +#endif + +#endif diff --git a/src/gui/jtk/Jtk_internal.h b/src/gui/jtk/Jtk_internal.h new file mode 100644 index 0000000..5d5c518 --- /dev/null +++ b/src/gui/jtk/Jtk_internal.h @@ -0,0 +1,9 @@ +#ifndef JTK_INTERNAL_H +#define JTK_INTERNAL_H + +extern Display * g_display; +extern XVisualInfo * g_vi; +extern XSetWindowAttributes g_swa; +extern GLXContext g_context; + +#endif diff --git a/src/gui/jtk/Jtk_keys.h b/src/gui/jtk/Jtk_keys.h new file mode 100644 index 0000000..4f1eaeb --- /dev/null +++ b/src/gui/jtk/Jtk_keys.h @@ -0,0 +1,120 @@ +#ifndef JTK_KEYS_H +#define JTK_KEYS_H + +#define JTK_KEY_MODS_MASK 0xFF000000u + +#define JTK_KEY_MODS_SHIFT 0x01000000u +#define JTK_KEY_MODS_LOCK 0x02000000u +#define JTK_KEY_MODS_CTRL 0x04000000u + +#define JTK_KEY_KEYCODE_MASK 0x00FFFFFFu + +#define JTK_KEY_F1 0x00FF0001u +#define JTK_KEY_F2 0x00FF0002u +#define JTK_KEY_F3 0x00FF0003u +#define JTK_KEY_F4 0x00FF0004u +#define JTK_KEY_F5 0x00FF0005u +#define JTK_KEY_F6 0x00FF0006u +#define JTK_KEY_F7 0x00FF0007u +#define JTK_KEY_F8 0x00FF0008u +#define JTK_KEY_F9 0x00FF0009u +#define JTK_KEY_F10 0x00FF000Au +#define JTK_KEY_F11 0x00FF000Bu +#define JTK_KEY_F12 0x00FF000Cu +#define JTK_KEY_F13 0x00FF000Du +#define JTK_KEY_F14 0x00FF000Eu +#define JTK_KEY_F15 0x00FF000Fu +#define JTK_KEY_F16 0x00FF0010u +#define JTK_KEY_F17 0x00FF0011u +#define JTK_KEY_F18 0x00FF0012u +#define JTK_KEY_F19 0x00FF0013u +#define JTK_KEY_F20 0x00FF0014u +#define JTK_KEY_F21 0x00FF0015u +#define JTK_KEY_F22 0x00FF0016u +#define JTK_KEY_F23 0x00FF0017u +#define JTK_KEY_F24 0x00FF0018u +#define JTK_KEY_F25 0x00FF0019u +#define JTK_KEY_F26 0x00FF001Au +#define JTK_KEY_F27 0x00FF001Bu +#define JTK_KEY_F28 0x00FF001Cu +#define JTK_KEY_F29 0x00FF001Du +#define JTK_KEY_F30 0x00FF001Eu +#define JTK_KEY_F31 0x00FF001Fu + +#define JTK_KEY_SHIFT_L 0x00FF0030u +#define JTK_KEY_SHIFT_R 0x00FF0031u +#define JTK_KEY_CTRL_L 0x00FF0032u +#define JTK_KEY_CTRL_R 0x00FF0033u +#define JTK_KEY_CAPS_LOCK 0x00FF0034u +#define JTK_KEY_SHIFT_LOCK 0x00FF0035u + +#define JTK_KEY_META_L 0x00FF0036u +#define JTK_KEY_META_R 0x00FF0037u +#define JTK_KEY_ALT_L 0x00FF0038u +#define JTK_KEY_ALT_R 0x00FF0039u +#define JTK_KEY_SUPER_L 0x00FF003Au +#define JTK_KEY_SUPER_R 0x00FF003Bu + +#define JTK_KEY_HOME 0x00FF003Cu +#define JTK_KEY_LEFT 0x00FF003Du +#define JTK_KEY_UP 0x00FF003Eu +#define JTK_KEY_RIGHT 0x00FF003Fu +#define JTK_KEY_DOWN 0x00FF0040u +#define JTK_KEY_PAGE_UP 0x00FF0042u +#define JTK_KEY_PAGE_DOWN 0x00FF0044u +#define JTK_KEY_END 0x00FF0045u +#define JTK_KEY_BEGIN 0x00FF0046u + +#define JTK_KEY_SELECT 0x00FF0047u +#define JTK_KEY_PRINT 0x00FF0048u +#define JTK_KEY_EXECUTE 0x00FF0049u +#define JTK_KEY_INSERT 0x00FF004Au +#define JTK_KEY_UNDO 0x00FF004Bu +#define JTK_KEY_REDO 0x00FF004Cu +#define JTK_KEY_MENU 0x00FF004Du +#define JTK_KEY_FIND 0x00FF004Eu +#define JTK_KEY_CANCEL 0x00FF004Fu +#define JTK_KEY_HELP 0x00FF0050u +#define JTK_KEY_BREAK 0x00FF0051u +#define JTK_KEY_NUM_LOCK 0x00FF0054u + +#define JTK_KEY_KP_SPACE 0x00FF0055u +#define JTK_KEY_KP_TAB 0x00FF0056u +#define JTK_KEY_KP_ENTER 0x00FF0057u +#define JTK_KEY_KP_F1 0x00FF0058u +#define JTK_KEY_KP_F2 0x00FF0059u +#define JTK_KEY_KP_F3 0x00FF005Au +#define JTK_KEY_KP_F4 0x00FF005Bu +#define JTK_KEY_KP_HOME 0x00FF005Cu +#define JTK_KEY_KP_LEFT 0x00FF005Du +#define JTK_KEY_KP_UP 0x00FF005Eu +#define JTK_KEY_KP_RIGHT 0x00FF005Fu +#define JTK_KEY_KP_DOWN 0x00FF0060u +#define JTK_KEY_KP_PAGE_UP 0x00FF0062u +#define JTK_KEY_KP_PAGE_DOWN 0x00FF0064u +#define JTK_KEY_KP_END 0x00FF0065u +#define JTK_KEY_KP_BEGIN 0x00FF0066u +#define JTK_KEY_KP_INSERT 0x00FF0067u +#define JTK_KEY_KP_DELETE 0x00FF0068u +#define JTK_KEY_KP_EQUAL 0x00FF0069u +#define JTK_KEY_KP_MULTIPLY 0x00FF006Au +#define JTK_KEY_KP_ADD 0x00FF006Bu +#define JTK_KEY_KP_SEPARATOR 0x00FF006Cu +#define JTK_KEY_KP_SUBTRACT 0x00FF006Du +#define JTK_KEY_KP_DECIMAL 0x00FF006Eu +#define JTK_KEY_KP_DIVIDE 0x00FF006Fu + +#define JTK_KEY_KP_0 0x00FF0070u +#define JTK_KEY_KP_1 0x00FF0071u +#define JTK_KEY_KP_2 0x00FF0072u +#define JTK_KEY_KP_3 0x00FF0073u +#define JTK_KEY_KP_4 0x00FF0074u +#define JTK_KEY_KP_5 0x00FF0075u +#define JTK_KEY_KP_6 0x00FF0076u +#define JTK_KEY_KP_7 0x00FF0077u +#define JTK_KEY_KP_8 0x00FF0078u +#define JTK_KEY_KP_9 0x00FF0079u + +#define JTK_KEY_UNKNOWN 0x00FFFFFFu + +#endif diff --git a/src/gui/jtk/Jtk_time.cc b/src/gui/jtk/Jtk_time.cc new file mode 100644 index 0000000..bd07440 --- /dev/null +++ b/src/gui/jtk/Jtk_time.cc @@ -0,0 +1,9 @@ +#include "Jtk.h" +#include + +uint64_t Jtk_UsTime() +{ + struct timeval tv; + gettimeofday(&tv, nullptr); + return (uint64_t)tv.tv_sec * 1000000u + (uint64_t)tv.tv_usec; +} diff --git a/src/gui/jtk/Jtk_time.h b/src/gui/jtk/Jtk_time.h new file mode 100644 index 0000000..130bf75 --- /dev/null +++ b/src/gui/jtk/Jtk_time.h @@ -0,0 +1,9 @@ +#ifndef JTK_TIME_H +#define JTK_TIME_H + +#include "Jtk_timer.h" + +bool Jtk_Init(); +uint64_t Jtk_UsTime(); + +#endif diff --git a/src/gui/jtk/Jtk_timer.cc b/src/gui/jtk/Jtk_timer.cc new file mode 100644 index 0000000..4be3a85 --- /dev/null +++ b/src/gui/jtk/Jtk_timer.cc @@ -0,0 +1,157 @@ +#include "Jtk.h" +#include +#include +#include + +typedef struct +{ + uint64_t next; /**< Time the timer next expires (us). */ + uint64_t interval; /**< Timer interval (us). */ + size_t id; /**< Timer ID. */ + void * user[2]; /**< User data pointers. */ +} Timer; + +/** Vector used to allocate timer IDs. */ +static std::vector> g_timers; + +/** Linked list used to traverse all active timers. */ +static std::list> g_active_timers; + +static size_t AllocateTimerID() +{ + for (size_t i = 0; i < g_timers.size(); i++) + { + if (!g_timers[i]) + { + return i; + } + } + g_timers.push_back(nullptr); + return g_timers.size() - 1u; +} + +/** + * Add a timer. + * + * @param delay + * Delay time in milliseconds. + * @param interval + * Interval time in milliseconds. A value of 0 indicates that the timer + * is not periodic. + * @param user1 + * User data pointer 1. This value is not used by Jtk but can be retrieved by + * the user when a timer expires. + * @param user2 + * User data pointer 2. This value is not used by Jtk but can be retrieved by + * the user when a timer expires. + * + * @return + * Timer ID. + */ +size_t Jtk_AddTimer(uint32_t delay, uint32_t interval, void * user1, void * user2) +{ + uint64_t current_system_time = Jtk_UsTime(); + size_t timer_id = AllocateTimerID(); + auto timer = std::make_shared(); + timer->next = current_system_time + (delay * 1000u); + timer->interval = interval * 1000u; + timer->id = timer_id; + timer->user[0] = user1; + timer->user[1] = user2; + g_timers[timer_id] = timer; + g_active_timers.push_back(timer); + return timer_id; +} + +/** + * Remove a timer. + * + * @param timer_id + * The ID of the timer to remove. + */ +void Jtk_RemoveTimer(size_t timer_id) +{ + if (timer_id < g_timers.size()) + { + auto timer = g_timers[timer_id]; + g_active_timers.remove(timer); + g_timers[timer_id] = nullptr; + } +} + +/** + * Determine the amount of time until the next timer expires (in us). + * + * @return + * Time (in us) until the next timer expires. This will be 0 if an active + * timer has already expired, and will be (uint64_t)-1 if there are no + * active timers. + */ +uint64_t Jtk_TimeToNextTimerExpiration() +{ + uint64_t time = (uint64_t)-1; + uint64_t current_system_time = Jtk_UsTime(); + for (auto & timer : g_active_timers) + { + if (timer->next <= current_system_time) + { + return 0; + } + uint64_t time_until_this_timer = timer->next - current_system_time; + if (time_until_this_timer < time) + { + time = time_until_this_timer; + } + } + return time; +} + +/** + * Service a timer. + * + * This will increment the timer's next activation time by its interval, or + * for a timer with an interval of 0, will remove the timer. + * + * @param timer_id + * The ID of the timer to service. + */ +void Jtk_ServiceTimer(size_t timer_id) +{ + auto timer = g_timers[timer_id]; + if (timer->interval == 0u) + { + Jtk_RemoveTimer(timer_id); + } + else + { + timer->next += timer->interval; + } +} + +/** + * Return the ID of an expired timer. + * + * @return + * The ID of an expired timer, or (size_t)-1 if no timer is expired. + */ +size_t Jtk_GetExpiredTimer() +{ + uint64_t current_system_time = Jtk_UsTime(); + for (auto & timer : g_active_timers) + { + if (timer->next <= current_system_time) + { + return timer->id; + } + } + return (size_t)-1; +} + +void * Jtk_GetTimerUserData(size_t timer_id, uint8_t index) +{ + if (timer_id < g_timers.size()) + { + return g_timers[timer_id]->user[index]; + } + return nullptr; +} diff --git a/src/gui/jtk/Jtk_timer.h b/src/gui/jtk/Jtk_timer.h new file mode 100644 index 0000000..d3a4e6a --- /dev/null +++ b/src/gui/jtk/Jtk_timer.h @@ -0,0 +1,14 @@ +#ifndef JTK_TIMER_H +#define JTK_TIMER_H + +#include +#include + +size_t Jtk_AddTimer(uint32_t delay, uint32_t interval, void * user1, void * user2); +void Jtk_RemoveTimer(size_t timer_id); +uint64_t Jtk_TimeToNextTimerExpiration(); +void Jtk_ServiceTimer(size_t timer_id); +size_t Jtk_GetExpiredTimer(); +void * Jtk_GetTimerUserData(size_t timer_id, uint8_t index); + +#endif diff --git a/src/gui/jtk/Jtk_window.cc b/src/gui/jtk/Jtk_window.cc new file mode 100644 index 0000000..cf01a0a --- /dev/null +++ b/src/gui/jtk/Jtk_window.cc @@ -0,0 +1,55 @@ + +#include "Jtk.h" + +#ifdef JTK_X + +#include +#include "Jtk_internal.h" +#include +#include + +static Bool WaitForNotify(Display * display, XEvent * event, XPointer arg) +{ + return (event->type == MapNotify) && (event->xmap.window == (Window)arg); +} + +void * Jtk_CreateWindow() +{ + XEvent event; + Window window = XCreateWindow(g_display, + RootWindow(g_display, g_vi->screen), + 0, 0, 800, 800, 0, g_vi->depth, InputOutput, g_vi->visual, + CWBorderPixel | CWColormap | CWEventMask, &g_swa); + XMapWindow(g_display, window); + XIfEvent(g_display, &event, WaitForNotify, (XPointer)window); + if (glXMakeCurrent(g_display, window, g_context) == False) + { + fprintf(stderr, "glXMakeCurrent() failure\n"); + XDestroyWindow(g_display, window); + return nullptr; + } + + return (void *)window; +} + +void Jtk_SwapBuffers(void * window) +{ + glXSwapBuffers(g_display, (Window)window); +} + +void Jtk_CloseWindow(void * window) +{ + XDestroyWindow(g_display, (Window)window); +} + +void Jtk_SetWindowTitle(void * window, const char * title) +{ + XTextProperty title_property; + if (XStringListToTextProperty((char **)&title, 1, &title_property) != 0) + { + XSetTextProperty(g_display, (Window)window, &title_property, XA_WM_NAME); + XFree(title_property.value); + } +} + +#endif diff --git a/src/gui/jtk/Jtk_window.h b/src/gui/jtk/Jtk_window.h new file mode 100644 index 0000000..720c036 --- /dev/null +++ b/src/gui/jtk/Jtk_window.h @@ -0,0 +1,9 @@ +#ifndef JTK_WINDOW_H +#define JTK_WINDOW_H + +void * Jtk_CreateWindow(); +void Jtk_SwapBuffers(void * window); +void Jtk_CloseWindow(void * window); +void Jtk_SetWindowTitle(void * window, const char * title); + +#endif diff --git a/wscript b/wscript index 4ac4744..c67b05a 100644 --- a/wscript +++ b/wscript @@ -11,8 +11,11 @@ def options(opt): def configure(conf): conf.load("compiler_c compiler_cxx") conf.check(header_name = "getopt.h", global_define = False) - conf.check_cfg(package = "sdl2", args = "--cflags --libs") conf.check_cfg(package = "freetype2", uselib_store = "FreeType2", args = "--cflags --libs") + if platform.system() == "Linux": + conf.check(header_name = "X11/Xlib.h", global_define = False) + elif platform.system() == "Windows": + conf.check(header_name = "windows.h", global_define = False) def build(bld): defines = ['APPNAME="%s"' % APPNAME] @@ -22,9 +25,11 @@ def build(bld): libs = [] if platform.system() == "Linux": defines += ["PLATFORM_LINUX"] - libs += ["dl", "GL"] - elif re.search(r'MINGW', platform.system()): + defines += ["JTK_X"] + libs += ["dl", "GL", "X11"] + elif platform.system() == "Windows": defines += ["PLATFORM_WINDOWS"] + defines += ["JTK_WINDOWS"] libs += ["opengl32"] defines += ['GLCXX_GL_INCLUDE="gl3w.h"'] sources = bld.path.ant_glob(["src/**/*.cc", "src/**/*.c", "libs/glcxx/src/glcxx/*"]) @@ -35,7 +40,7 @@ def build(bld): defines = defines, cxxflags = ["-Wall", "-std=gnu++14", "-O2", "-Wno-switch"], lib = libs, - uselib = ["SDL2", "FreeType2"]) + uselib = ["FreeType2"]) test_libs = libs + [] if platform.system() == "Linux": @@ -56,4 +61,4 @@ def build(bld): lib = test_libs, cxxflags = ["-Wall", "-std=gnu++14", "--coverage", "-Wno-switch", "-include", "iostream"], linkflags = ["--coverage"], - uselib = ["SDL2", "FreeType2"]) + uselib = ["FreeType2"])