mv src src-c

This commit is contained in:
josh committed 2018-07-25 20:47:02 -04:00
1 parent a0b2f660a2
commit 50cd9efd86
75 files changed

No files matched your search

+590
View File
@@ -0,0 +1,590 @@
#include "BufferPane.h"
#include <cmath>
int BufferPane::Cwd::operator()(uint32_t character)
{
if (character < 0x20u)
{
return 2;
}
else if (character < 0x80u)
{
return 1;
}
else
{
return std::max(1, m_window->font()->get_glyph(character)->get_advance() / m_window->font()->get_advance());
}
}
BufferPane::BufferPane(Window * window, std::shared_ptr<Buffer> buffer)
: m_window(window), m_buffer(buffer), m_cwd(window)
{
m_iterator = buffer->add_cursor();
m_buffer_view = std::make_shared<BufferView>(buffer, m_iterator, m_cwd);
m_buffer_view->set_scroll_offset(5);
m_buffer_view->update();
m_show_status_bar = true;
m_show_line_number_gutter = true;
m_command_mode = false;
m_focused = false;
m_rows = 0;
m_columns = 0;
}
void BufferPane::resize(int width, int height)
{
Pane::resize(width, height);
resize_buffer_view();
}
void BufferPane::resize_buffer_view()
{
if (m_show_line_number_gutter)
{
int max_line_possible = 1;
if (m_iterator->valid())
{
max_line_possible = std::min(m_iterator->line() + m_rows - m_buffer_view->cursor_screen_row(), m_buffer->end().line());
}
m_line_number_gutter_width = log10(max_line_possible) + 1;
}
else
{
m_line_number_gutter_width = 0;
}
int columns = std::max(1, (m_width - col_x(0)) / m_window->font()->get_advance());
int height_subtract = 0;
if (m_show_status_bar)
{
height_subtract = m_window->font()->get_line_height() + 1;
}
int rows = std::max(1, (m_height - height_subtract) / m_window->font()->get_line_height());
if ((rows != m_rows) || (columns != m_columns))
{
m_rows = rows;
m_columns = columns;
m_buffer_view->resize(m_columns, m_rows);
m_buffer_view->update();
}
}
void BufferPane::draw()
{
resize_buffer_view();
if (m_iterator->valid())
{
for (auto line_iterator = m_buffer_view->vert_iter();
line_iterator.is_valid();
line_iterator++)
{
draw_buffer_line(line_iterator.row_offset(), line_iterator.iterator());
}
}
else
{
draw_cursor(col_x(0), row_y(0), 0, 1);
}
if (m_show_line_number_gutter)
{
m_window->gl()->draw_rect(win_x(m_line_number_gutter_width * m_window->font()->get_advance() + 1), win_y(0), 1, m_height, 0.5, 0.5, 0.5, 1.0);
}
if (m_show_status_bar)
{
draw_status_bar();
}
}
void BufferPane::scroll_window_up(Window::ScrollMode scroll_mode)
{
int lines_to_scroll = calculate_lines_to_scroll(scroll_mode);
m_buffer_view->scroll_view_up(lines_to_scroll, insert_mode());
m_buffer_view->update();
m_window->request_redraw();
}
void BufferPane::scroll_window_down(Window::ScrollMode scroll_mode)
{
int lines_to_scroll = calculate_lines_to_scroll(scroll_mode);
m_buffer_view->scroll_view_down(lines_to_scroll, insert_mode());
m_buffer_view->update();
m_window->request_redraw();
}
void BufferPane::draw_buffer_line(int screen_row, std::shared_ptr<Buffer::Iterator> start_of_line)
{
if (m_show_line_number_gutter)
{
draw_line_number(screen_row, start_of_line->line());
}
int last_drawn_crosshair_row = -1;
for (auto it = m_buffer_view->horiz_iter(start_of_line);
it.is_valid();
it++)
{
int draw_row = screen_row + it.row_offset();
int cwidth = std::max(it.character_width(), 1);
if ((draw_row < 0) || (draw_row > m_rows))
continue;
if (it.iterator()->line() == m_iterator->line())
{
if ((draw_row > last_drawn_crosshair_row) &&
((!it.is_eol()) || (it.row_offset() == 0)))
{
draw_crosshair(col_x(0), row_y(draw_row), col_x(m_columns));
last_drawn_crosshair_row = draw_row;
}
}
else if ((m_buffer_view->cursor_virtual_column() >= it.virtual_column()) &&
(m_buffer_view->cursor_virtual_column() < (it.virtual_column() + cwidth)))
{
int col = it.screen_column() + (m_buffer_view->cursor_virtual_column() - it.virtual_column());
int row = draw_row;
while (col >= m_columns)
{
col -= m_columns;
row++;
}
draw_crosshair(col_x(col), row_y(row));
}
if (*it.iterator() == *m_iterator)
{
bool wrap = (it.code_point() == '\t');
int row = draw_row;
int col = it.screen_column();
for (int i = 0; i < cwidth; i++)
{
draw_cursor(col_x(col), row_y(row), i, cwidth);
col++;
if (wrap && (col >= m_columns) && (i < (cwidth - 1)))
{
row++;
col = 0;
/* We draw another crosshair row here so that it can be
* drawn before drawing the cursor over top of it. If we
* did not do this then the draw_crosshair for the next
* character on the new row would overwrite the wrapped
* part of the cursor. */
draw_crosshair(col_x(0), row_y(row), col_x(m_columns));
last_drawn_crosshair_row = row;
}
}
}
if (it.is_eol())
{
if ((m_buffer_view->cursor_virtual_column() > it.virtual_column()) &&
(m_buffer_view->cursor_virtual_column() < (it.virtual_column() + (m_columns - it.screen_column()))))
{
draw_crosshair(col_x(it.screen_column() + (m_buffer_view->cursor_virtual_column() - it.virtual_column())),
row_y(draw_row));
}
}
else
{
draw_buffer_character(it.code_point(), draw_row, it.screen_column());
}
}
}
void BufferPane::draw_buffer_character(uint32_t character, int screen_row, int screen_column)
{
if ((character != ' ') &&
(character != '\t') &&
(character != '\n') &&
(character != Buffer::Iterator::INVALID_CODE_POINT))
{
int x = col_x(screen_column);
int y = row_y(screen_row);
if (character < 0x20u)
{
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(), 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
{
m_window->gl()->draw_character(win_x(x), win_y(y), character, *m_window->font(), 1.0, 1.0, 1.0, 1.0);
}
}
}
void BufferPane::draw_cursor(int x, int y, int i, int columns)
{
if (m_command_mode && (!m_focused))
{
return;
}
int width = m_window->font()->get_advance();
int height = m_window->font()->get_line_height();
if (insert_mode())
{
if (i == 0)
{
m_window->gl()->draw_rect(win_x(x), win_y(y), 3, height, 1.0, 0.2, 1.0, 1.0);
}
}
else if (m_focused)
{
m_window->gl()->draw_rect(win_x(x), win_y(y), width, height, 1.0, 0.2, 1.0, 1.0);
}
else
{
m_window->gl()->draw_rect(win_x(x), win_y(y), width, 1, 1.0, 0.2, 1.0, 1.0);
m_window->gl()->draw_rect(win_x(x), win_y(y) + height - 1, width, 1, 1.0, 0.2, 1.0, 1.0);
if (i == 0)
{
m_window->gl()->draw_rect(win_x(x), win_y(y), 1, height, 1.0, 0.2, 1.0, 1.0);
}
if (i == (columns - 1))
{
m_window->gl()->draw_rect(win_x(x) + width - 1, win_y(y), 1, height, 1.0, 0.2, 1.0, 1.0);
}
}
}
void BufferPane::draw_crosshair(int x, int y, int width, int height)
{
if (!m_command_mode)
{
if (width < 0)
width = m_window->font()->get_advance();
if (height < 0)
height = m_window->font()->get_line_height();
m_window->gl()->draw_rect(win_x(x), win_y(y), width, height, 0.1, 0.1, 0.1, 1.0);
}
}
void BufferPane::draw_line_number(int screen_row, size_t line_number)
{
if (screen_row >= 0)
{
char line_number_string[20];
int length = snprintf(line_number_string, sizeof(line_number_string), "%lu", line_number + 1u);
int x = (m_line_number_gutter_width - length) * m_window->font()->get_advance();
int y = row_y(screen_row);
float r, g;
if (line_number == m_iterator->line())
{
r = 1.0;
g = 1.0;
}
else
{
r = 0.6;
g = 0.6;
}
for (int i = 0; i < length; i++)
{
m_window->gl()->draw_character(win_x(x), win_y(y), line_number_string[i], *m_window->font(), r, g, 0.0, 1.0);
x += m_window->font()->get_advance();
}
}
}
void BufferPane::exit_insert_mode()
{
if (insert_mode())
{
m_buffer->exit_insert_mode();
if (!m_iterator->is_start_of_line())
{
m_iterator->go_back();
}
m_buffer_view->update();
m_window->request_redraw();
}
}
void BufferPane::insert_code_point(uint32_t code_point)
{
if (insert_mode())
{
if (code_point == '\b') /* Backspace */
{
Buffer::Iterator i = *m_iterator;
i.go_back();
if (i.valid())
{
m_buffer->erase_code_point(i);
}
}
else if (code_point == 127) /* Delete */
{
m_buffer->erase_code_point(*m_iterator);
}
else
{
/* TODO: adjust cursor screen row in m_buffer_view. */
m_buffer->insert_code_point(*m_iterator, code_point);
}
m_buffer_view->update();
m_window->request_redraw();
}
}
void BufferPane::kill_character_forward()
{
if (**m_iterator != '\n')
{
m_buffer->erase_code_point(*m_iterator);
if (**m_iterator == '\n')
{
m_iterator->go_left_in_line();
}
m_buffer_view->update();
m_window->request_redraw();
}
}
void BufferPane::kill_character_backward()
{
Buffer::Iterator it = *m_iterator;
if (it.go_left_in_line())
{
m_buffer->erase_code_point(it);
m_buffer_view->update();
m_window->request_redraw();
}
}
size_t BufferPane::display_column() const
{
if (!m_iterator->valid())
{
return 0u;
}
if ((**m_iterator == '\n') && (!insert_mode()))
{
Buffer::Iterator i = *m_iterator;
if (!i.go_left_in_line())
{
return 0u;
}
}
return m_buffer_view->cursor_virtual_column() + 1u;
}
void BufferPane::cursor_move(BufferPane::CursorMovement which, uint32_t c)
{
bool moved = m_buffer_view->cursor_move(which, c, insert_mode());
if (moved)
{
m_buffer_view->update();
m_window->request_redraw();
}
}
void BufferPane::cursor_move_to_line(size_t target_line)
{
bool moved = m_buffer_view->cursor_move_to_line(target_line);
if (moved)
{
m_buffer_view->update();
m_window->request_redraw();
}
}
void BufferPane::enter_insert_mode(Window::EnterInsertModeMode which)
{
if (insert_mode())
return;
switch (which)
{
case Window::EnterInsertModeMode::START_OF_CHAR:
m_buffer->enter_insert_mode();
break;
case Window::EnterInsertModeMode::END_OF_CHAR:
if (**m_iterator != '\n')
{
m_iterator->go_forward();
}
m_buffer->enter_insert_mode();
break;
case Window::EnterInsertModeMode::START_OF_LINE:
m_iterator->go_start_of_line();
enter_insert_mode(Window::EnterInsertModeMode::START_OF_CHAR);
break;
case Window::EnterInsertModeMode::END_OF_LINE:
m_iterator->go_end_of_line(false);
enter_insert_mode(Window::EnterInsertModeMode::END_OF_CHAR);
break;
case Window::EnterInsertModeMode::NEW_LINE_BEFORE:
m_iterator->go_start_of_line();
m_buffer->enter_insert_mode();
m_buffer->insert_code_point(*m_iterator, '\n');
m_buffer->exit_insert_mode();
cursor_move(CursorMovement::UP, 0u);
enter_insert_mode(Window::EnterInsertModeMode::START_OF_CHAR);
break;
case Window::EnterInsertModeMode::NEW_LINE_AFTER:
m_iterator->go_end_of_line(true);
m_buffer->enter_insert_mode();
m_buffer->insert_code_point(*m_iterator, '\n');
break;
}
m_buffer_view->update();
m_window->request_redraw();
}
int BufferPane::calculate_lines_to_scroll(Window::ScrollMode scroll_mode)
{
switch (scroll_mode)
{
case Window::ScrollMode::ONE_LINE:
return 1;
break;
case Window::ScrollMode::WHEEL:
return 3;
break;
case Window::ScrollMode::HALF_SCREEN:
return m_rows / 2;
break;
case Window::ScrollMode::WHOLE_SCREEN:
return m_rows - 1;
break;
}
return 0;
}
void BufferPane::draw_status_bar()
{
m_window->gl()->draw_rect(win_x(0), win_y(m_window->font()->get_line_height()), m_width, 1, 0.5, 0.5, 0.5, 1.0);
m_window->gl()->draw_rect(win_x(0), win_y(0), m_width, m_window->font()->get_line_height(), 0.0, 0.0, 0.0, 1.0);
char cursor_position[20];
sprintf(cursor_position, "%zu, %zu", display_line(), display_column());
int cursor_position_length = strlen(cursor_position);
int x = m_width - m_window->font()->get_advance() * cursor_position_length;
m_window->gl()->draw_rect(win_x(x - 2), win_y(0), 1, m_window->font()->get_line_height(), 0.5, 0.5, 0.5, 1.0);
std::string filename = m_buffer->filename() ? *m_buffer->filename() : "[No Name]";
int filename_x = std::min(0, x - 3 - (int)filename.size() * m_window->font()->get_advance());
for (int i = 0; i < cursor_position_length; i++)
{
m_window->gl()->draw_character(win_x(x), win_y(0), cursor_position[i], *m_window->font(), 1.0, 1.0, 1.0, 1.0);
x += m_window->font()->get_advance();
}
for (size_t i = 0; i < filename.size(); i++)
{
m_window->gl()->draw_character(win_x(filename_x), win_y(0), filename[i], *m_window->font(), 1.0, 1.0, 1.0, 1.0);
filename_x += m_window->font()->get_advance();
}
}
void BufferPane::undo()
{
m_buffer->undo();
m_buffer_view->update();
m_window->request_redraw();
}
void BufferPane::redo()
{
m_buffer->redo();
m_buffer_view->update();
m_window->request_redraw();
}
void BufferPane::delete_motion(const Command::Unit & motion)
{
auto range = get_range_for_motion(motion, false);
if (range)
{
m_buffer->erase_range(*range);
m_buffer_view->update();
}
}
void BufferPane::change_motion(const Command::Unit & motion)
{
auto range = get_range_for_motion(motion, true);
if (range)
{
m_buffer->push_operation();
enter_insert_mode(Window::EnterInsertModeMode::START_OF_CHAR);
m_buffer->erase_range(*range);
m_buffer->pop_operation();
m_buffer_view->update();
}
}
std::shared_ptr<Buffer::Range> BufferPane::get_range_for_motion(const Command::Unit & motion, bool will_insert)
{
if (!m_iterator->valid())
{
return nullptr;
}
auto start = m_iterator->clonep();
auto end = m_iterator->clonep();
switch (motion.id)
{
case Command::Motion::FORWARD_UP_TO_CHAR:
if (!end->go_forward_in_line_up_to_char(motion.following_char))
{
return nullptr;
}
end->go_forward();
break;
case Command::Motion::FORWARD_ON_TO_CHAR:
if (!end->go_forward_in_line_on_to_char(motion.following_char))
{
return nullptr;
}
end->go_forward();
break;
case Command::Motion::BACK_UP_TO_CHAR:
if (!start->go_backward_in_line_up_to_char(motion.following_char))
{
return nullptr;
}
break;
case Command::Motion::BACK_ON_TO_CHAR:
if (!start->go_backward_in_line_on_to_char(motion.following_char))
{
return nullptr;
}
break;
case Command::Motion::START_OF_LINE:
start->go_start_of_line();
break;
case Command::Motion::END_OF_LINE:
end->go_end_of_line(true);
break;
case Command::Motion::THIS_LINE:
start->go_start_of_line();
end->go_end_of_line(!will_insert);
end->go_forward();
break;
default:
return nullptr;
}
return std::make_shared<Buffer::Range>(start, end);
}
void BufferPane::set_command_mode()
{
m_command_mode = true;
set_show_status_bar(false);
set_show_line_number_gutter(false);
enter_insert_mode(Window::EnterInsertModeMode::START_OF_CHAR);
}
void BufferPane::clear()
{
m_buffer->clear();
*m_iterator = m_buffer->begin();
m_buffer_view->update();
}
+100
View File
@@ -0,0 +1,100 @@
#ifndef BUFFERPANE_H
#define BUFFERPANE_H
#include "Buffer.h"
#include "Pane.h"
#include "Font.h"
#include "GL.h"
#include <memory>
#include "Window.h"
#include "CharacterWidthDeterminer.h"
#include "BufferView.h"
class BufferPane : public Pane
{
public:
class Cwd : public CharacterWidthDeterminer
{
public:
Cwd(Window * window)
{
m_window = window;
}
int operator()(uint32_t character);
protected:
Window * m_window;
};
typedef BufferView::CursorMovement CursorMovement;
BufferPane(Window * window, std::shared_ptr<Buffer> buffer);
void resize(int width, int height) override;
void draw();
void cursor_move(CursorMovement which, uint32_t c);
void cursor_move_to_line(size_t target_line);
void enter_insert_mode(Window::EnterInsertModeMode which);
void insert_code_point(uint32_t code_point);
void exit_insert_mode();
void kill_character_forward();
void kill_character_backward();
bool insert_mode() const { return m_buffer->insert_mode(); }
void scroll_window_up(Window::ScrollMode scroll_mode);
void scroll_window_down(Window::ScrollMode scroll_mode);
void undo();
void redo();
void delete_motion(const Command::Unit & motion);
void change_motion(const Command::Unit & motion);
void set_show_status_bar(bool show_status_bar)
{
m_show_status_bar = show_status_bar;
}
void set_show_line_number_gutter(bool show_line_number_gutter)
{
m_show_line_number_gutter = show_line_number_gutter;
}
void set_command_mode();
void set_focused(bool focused)
{
m_focused = focused;
}
void clear();
std::shared_ptr<Buffer> buffer() const { return m_buffer; }
protected:
void draw_buffer_line(int screen_row, std::shared_ptr<Buffer::Iterator> start_of_line);
void draw_buffer_character(uint32_t character, int screen_row, int screen_column);
void draw_cursor(int x, int y, int i, int columns);
void draw_crosshair(int x, int y, int width = -1, int height = -1);
void draw_line_number(int screen_row, size_t line_number);
int col_x(int col)
{
return (m_line_number_gutter_width + col) * m_window->font()->get_advance() + (m_show_line_number_gutter ? 3 : 0);
}
int row_y(int row)
{
return m_height - (row + 1) * m_window->font()->get_line_height();
}
size_t display_line() const { return m_iterator->line() + 1u; }
size_t display_column() const;
void draw_status_bar();
int calculate_lines_to_scroll(Window::ScrollMode scroll_mode);
void resize_buffer_view();
std::shared_ptr<Buffer::Range> get_range_for_motion(const Command::Unit & motion, bool will_insert);
Window * m_window;
std::shared_ptr<Buffer> m_buffer;
Cwd m_cwd;
std::shared_ptr<BufferView> m_buffer_view;
int m_rows;
int m_columns;
int m_line_number_gutter_width;
std::shared_ptr<Buffer::Iterator> m_iterator;
std::list<std::pair<int, Buffer::Iterator>> m_screen_lines;
bool m_show_status_bar;
bool m_show_line_number_gutter;
bool m_command_mode;
bool m_focused;
};
#endif
+91
View File
@@ -0,0 +1,91 @@
#include "Font.h"
#define round_up_26_6(val) (((val) + 63) >> 6u)
static FT_Library ft_library_handle;
static bool ft_initialized;
bool Initialize_FreeType()
{
if (ft_initialized)
{
return true;
}
if (FT_Init_FreeType(&ft_library_handle) != 0)
{
return false;
}
ft_initialized = true;
return true;
}
bool Font::load(const char * fname, int size)
{
if (!Initialize_FreeType())
{
return false;
}
if (FT_New_Face(ft_library_handle, fname, 0, &m_face) != 0)
{
return false;
}
FT_Set_Pixel_Sizes(m_face, 0, size);
return preload_glyphs();
}
bool Font::preload_glyphs()
{
static const char preload_glyph_list[] = "HMgjqxy_|^";
int max_top = -9999;
int min_bottom = 9999;
m_advance = 0;
for (size_t i = 0u; i < sizeof(preload_glyph_list) - 1u; i++)
{
std::shared_ptr<Glyph> g = get_glyph(preload_glyph_list[i]);
if (g == NULL)
{
return false;
}
if (m_face->glyph->bitmap_top > max_top)
{
max_top = m_face->glyph->bitmap_top;
}
int bitmap_bottom = m_face->glyph->bitmap_top - m_face->glyph->bitmap.rows;
if (bitmap_bottom < min_bottom)
{
min_bottom = bitmap_bottom;
}
if (g->get_advance() > m_advance)
{
m_advance = g->get_advance();
}
}
m_line_height = round_up_26_6(m_face->size->metrics.height);
m_baseline_offset = (m_line_height - (max_top - min_bottom)) / 2 - min_bottom;
return true;
}
std::shared_ptr<Glyph> Font::get_glyph(FT_ULong character)
{
auto it = m_glyphs.find(character);
if (it != m_glyphs.end())
{
return it->second;
}
std::shared_ptr<Glyph> glyph = std::make_shared<Glyph>();
if (!glyph->load(m_face, character))
{
glyph = nullptr;
}
m_glyphs[character] = glyph;
return glyph;
}
+27
View File
@@ -0,0 +1,27 @@
#ifndef FONT_H
#define FONT_H
#include <unordered_map>
#include <memory>
#include "Glyph.h"
class Font
{
public:
bool load(const char * fname, int size);
std::shared_ptr<Glyph> get_glyph(FT_ULong character);
int get_advance() { return m_advance; }
int get_line_height() { return m_line_height; }
int get_baseline_offset() { return m_baseline_offset; }
protected:
bool preload_glyphs();
FT_Face m_face;
std::unordered_map<FT_ULong, std::shared_ptr<Glyph>> m_glyphs;
int m_advance;
int m_line_height;
int m_baseline_offset;
};
#endif
+52
View File
@@ -0,0 +1,52 @@
#include "Glyph.h"
#define round_up_26_6(val) (((val) + 63) >> 6u)
bool Glyph::load(FT_Face face, FT_ULong char_code)
{
if (FT_Load_Char(face, char_code, FT_LOAD_RENDER) != 0)
return false;
m_texture = glcxx::Texture::create();
m_texture->bind(GL_TEXTURE_2D);
int width = face->glyph->bitmap.width;
int rounded_width = glcxx::Texture::next_power_of_2(width);
int height = face->glyph->bitmap.rows;
int rounded_height = glcxx::Texture::next_power_of_2(height);
m_advance = round_up_26_6(face->glyph->advance.x);
uint8_t * texture = new uint8_t[rounded_width * rounded_height];
memset(texture, 0, rounded_width * rounded_height);
for (int i = 0; i < height; i++)
{
memcpy(&texture[rounded_width * i],
&face->glyph->bitmap.buffer[width * (height - i - 1)],
width);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, rounded_width, rounded_height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, texture);
delete[] texture;
m_array = glcxx::Array::create();
m_array->bind();
int left = face->glyph->bitmap_left;
int top = face->glyph->bitmap_top;
float s_max = width / (float)rounded_width;
float t_max = height / (float)rounded_height;
m_buffer = glcxx::Buffer::create(GL_ARRAY_BUFFER, GL_STATIC_DRAW, {
(GLfloat)left, (GLfloat)(top - height), 0.0, 0.0,
(GLfloat)(left + width), (GLfloat)(top - height), s_max, 0.0,
(GLfloat)left, (GLfloat)top, 0.0, t_max,
(GLfloat)(left + width), (GLfloat)top, s_max, t_max,
});
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
return true;
}
+27
View File
@@ -0,0 +1,27 @@
#ifndef GLYPH_H
#define GLYPH_H
#include <ft2build.h>
#include FT_FREETYPE_H
#include "glcxx.hpp"
class Glyph
{
public:
bool load(FT_Face face, FT_ULong char_code);
int get_advance() { return m_advance; }
void render()
{
m_array->bind();
m_texture->bind(GL_TEXTURE_2D);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
protected:
int m_advance;
std::shared_ptr<glcxx::Texture> m_texture;
std::shared_ptr<glcxx::Buffer> m_buffer;
std::shared_ptr<glcxx::Array> m_array;
};
#endif
+44
View File
@@ -0,0 +1,44 @@
#ifndef PANE_H
#define PANE_H
class Pane
{
public:
Pane()
{
m_x = 0;
m_y = 0;
m_width = 0;
m_height = 0;
}
virtual void move(int x, int y)
{
m_x = x;
m_y = y;
}
virtual void resize(int width, int height)
{
m_width = width;
m_height = height;
}
int win_x(int offset)
{
return m_x + offset;
}
int win_y(int offset)
{
return m_y + offset;
}
protected:
int m_x;
int m_y;
int m_width;
int m_height;
};
#endif
+681
View File
@@ -0,0 +1,681 @@
#include "gl3w.h"
#include "Window.h"
#include "Runtime.h"
#include "BufferPane.h"
#include <iostream>
#include "jes_icon-32x32.h"
#include <unistd.h>
#include <unordered_map>
#include "DefaultCommandMap.h"
#include "Path.h"
#define INITIAL_WIDTH 800
#define INITIAL_HEIGHT 800
#define FONT_SIZE 16
/* Process multiple queued events for up to this long before stopping to
* redraw the screen. */
#define MAX_EVENT_PROCESS_TIME 50000
/* Amount of time until a status message fades out */
#define STATUS_TIME 4000
/**
* Initialize OpenGL.
*
* @retval true OpenGL was loaded successfully.
* @retval false Loading OpenGL failed.
*/
static bool Initialize_OpenGL()
{
if (gl3wInit() != 0)
{
std::cerr << "Failed to initialize gl3w." << std::endl;
/* Failed to gl3wInit() */
return false;
}
if (!gl3wIsSupported(3, 0))
{
std::cerr << "Error: OpenGL 3.0 is required." << std::endl;
std::cerr << "OpenGL version: "
<< glGetString(GL_VERSION)
<< ", GLSL: "
<< glGetString(GL_SHADING_LANGUAGE_VERSION)
<< std::endl;
/* OpenGL 3.0 is not supported */
return false;
}
glActiveTexture(GL_TEXTURE0);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
return true;
}
void Window::set_window_icon()
{
uint8_t * icon_bgra = (uint8_t *)malloc(32u * 32u * 4u);
uint8_t * out_icon = icon_bgra;
const uint8_t * in_icon = jes_icon_32x32;
for (int row = 0; row < 32; row++)
{
for (int col = 0; col < 32; col++)
{
uint8_t r = *in_icon++;
uint8_t g = *in_icon++;
uint8_t b = *in_icon++;
*out_icon++ = b;
*out_icon++ = g;
*out_icon++ = r;
*out_icon++ = 0xFFu;
}
}
Jtk_SetWindowIcon(m_window, icon_bgra, 32, 32);
free(icon_bgra);
}
/**
* Create a Window.
*
* @retval true The Window was created successfully.
* @retval false There was an error while creating the Window.
*/
bool Window::create(std::shared_ptr<Buffer> buffer)
{
if (!Jtk_Init())
{
std::cerr << "Error initializing GUI" << std::endl;
return false;
}
m_window = Jtk_CreateWindow();
if (m_window == NULL)
{
std::cerr << "Error creating window" << std::endl;
return false;
}
Jtk_SetWindowTitle(m_window, "jes");
set_window_icon();
if (!Initialize_OpenGL())
{
std::cerr << "Error initializing OpenGL" << std::endl;
return false;
}
m_exit_requested = false;
/* TODO: user configurable font, size */
std::string font_path = Runtime::find(Runtime::FONT, "DejaVuSansMono");
if (font_path == "")
{
std::cerr << "Unable to locate font" << std::endl;
return false;
}
m_font = std::make_shared<Font>();
if (!m_font->load(font_path.c_str(), FONT_SIZE))
{
std::cerr << "Error loading font" << std::endl;
return false;
}
m_gl = std::make_shared<GL>();
m_target_column = 0u; /* TODO: fix */
glClearColor (0.0, 0.0, 0.0, 0.0);
m_buffer_pane = std::make_shared<BufferPane>(this, buffer);
m_focused_buffer_pane = m_buffer_pane;
m_buffer_pane->set_focused(true);
m_command_buffer = std::make_shared<Buffer>();
m_command_buffer_pane = std::make_shared<BufferPane>(this, m_command_buffer);
m_command_buffer_pane->set_command_mode();
m_command_buffer_screen_rows = 1;
m_command_invalid = false;
m_status_message_timer = (size_t)-1;
resize(INITIAL_WIDTH, INITIAL_HEIGHT);
m_redraw_requested = true;
return true;
}
/**
* Run the Window event loop.
*
* TODO: make this a static method to support multiple GUI windows.
*/
void Window::run_event_loop()
{
Jtk_Event event;
while (!m_exit_requested)
{
if (m_redraw_requested)
{
m_redraw_requested = false;
redraw();
}
Jtk_WaitEvent(&event);
uint64_t event_time = Jtk_UsTime();
handle_event(event);
while (Jtk_CheckEvent(&event) &&
((Jtk_UsTime() - event_time) < MAX_EVENT_PROCESS_TIME))
{
handle_event(event);
}
}
}
/**
* Handle a Jtk event.
*/
void Window::handle_event(Jtk_Event & event)
{
switch (event.type)
{
case JTK_EVENT_WINDOW_CLOSE:
m_exit_requested = true;
break;
case JTK_EVENT_KEY_PRESS:
#if 0
if (!event.key.repeat)
{
Jtk_BeginKeyRepeat(&event.key, 300u, 25u);
}
#endif
handle_keypress(event.key.key);
m_redraw_requested = true;
break;
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;
case JTK_EVENT_BUTTON_PRESS:
if (event.button.button == 4)
{
m_focused_buffer_pane->scroll_window_up(ScrollMode::WHEEL);
}
else if (event.button.button == 5)
{
m_focused_buffer_pane->scroll_window_down(ScrollMode::WHEEL);
}
break;
case JTK_EVENT_TIMER:
if (event.timer.timer_id == m_status_message_timer)
{
handle_status_message_timer();
}
break;
}
}
void Window::handle_keypress(uint32_t keyval)
{
uint32_t ctrl_keyval = keyval & (JES_KEY_KEYCODE_MASK | JES_KEY_MODS_CTRL);
uint32_t keycode = keyval & JES_KEY_KEYCODE_MASK;
if ((JES_KEY_SHIFT_L <= keycode) && (keycode <= JES_KEY_SUPER_R))
{
/* Ignore modifier key presses. */
}
else if (m_focused_buffer_pane->insert_mode())
{
if (keycode == '\033')
{
if (m_focused_buffer_pane == m_command_buffer_pane)
{
m_command_buffer_pane->clear();
m_command_buffer_screen_rows = 1;
change_focus(m_buffer_pane);
}
else
{
m_focused_buffer_pane->exit_insert_mode();
}
}
else if ((keycode == '\n') && (m_focused_buffer_pane == m_command_buffer_pane))
{
EncodedString command = m_command_buffer->get_string();
m_command_buffer_pane->clear();
m_command_buffer_screen_rows = 1;
change_focus(m_buffer_pane);
handle_command(command);
}
else if (check_insert_mode_command(ctrl_keyval))
{
/* Command was handled, don't do anything else. */
}
else if ((ctrl_keyval & JES_KEY_MODS_CTRL) == 0u)
{
m_focused_buffer_pane->insert_code_point(ctrl_keyval);
}
}
else
{
if ((keycode == '\033') || (keycode == '\b'))
{
m_command_input.clear();
m_command_invalid = false;
}
else
{
clear_status();
m_command_input.push_back(ctrl_keyval);
evaluate_command_input();
}
}
}
void Window::evaluate_command_input()
{
auto cm = DefaultCommandMap::get();
Command command;
switch (cm->lookup_command(&m_command_input[0], m_command_input.size(), command))
{
case CommandMap::COMMAND_INVALID:
m_command_invalid = true;
break;
case CommandMap::COMMAND_INCOMPLETE:
break;
case CommandMap::COMMAND_COMPLETE:
execute_command(command);
m_command_input.clear();
break;
}
}
bool Window::check_insert_mode_command(uint32_t insert_mode_command)
{
auto cm = DefaultCommandMap::get_insert_mode();
Command command;
if (cm->lookup_command(&insert_mode_command, 1u, command) == CommandMap::COMMAND_COMPLETE)
{
execute_command(command);
return true;
}
return false;
}
void Window::execute_command(const Command & command)
{
switch (command.main.id)
{
case Command::GO_FORWARD_UP_TO_CHAR:
m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::FORWARD_UP_TO_CHAR, command.main.following_char);
break;
case Command::GO_FORWARD_ON_TO_CHAR:
m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::FORWARD_ON_TO_CHAR, command.main.following_char);
break;
case Command::GO_BACK_UP_TO_CHAR:
m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::BACK_UP_TO_CHAR, command.main.following_char);
break;
case Command::GO_BACK_ON_TO_CHAR:
m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::BACK_ON_TO_CHAR, command.main.following_char);
break;
case Command::DELETE_MOTION:
m_focused_buffer_pane->delete_motion(command.motion);
break;
case Command::DELETE_LINE:
{
Command::Unit motion = command.motion;
motion.id = Command::Motion::THIS_LINE;
m_focused_buffer_pane->delete_motion(motion);
}
break;
case Command::DELETE_CHAR:
m_focused_buffer_pane->kill_character_forward();
break;
case Command::DELETE_CHAR_BACK:
m_focused_buffer_pane->kill_character_backward();
break;
case Command::CHANGE_MOTION:
m_focused_buffer_pane->change_motion(command.motion);
break;
case Command::CHANGE_LINE:
{
Command::Unit motion = command.motion;
motion.id = Command::Motion::THIS_LINE;
m_focused_buffer_pane->change_motion(motion);
}
break;
case Command::YANK_MOTION:
break;
case Command::YANK_LINE:
break;
case Command::UNDO:
m_focused_buffer_pane->undo();
break;
case Command::REDO:
m_focused_buffer_pane->redo();
break;
case Command::ENTER_INSERT_MODE:
m_focused_buffer_pane->enter_insert_mode(EnterInsertModeMode::START_OF_CHAR);
break;
case Command::ENTER_INSERT_MODE_AFTER_CHAR:
m_focused_buffer_pane->enter_insert_mode(EnterInsertModeMode::END_OF_CHAR);
break;
case Command::ENTER_INSERT_MODE_AFTER_LINE:
m_focused_buffer_pane->enter_insert_mode(EnterInsertModeMode::END_OF_LINE);
break;
case Command::ENTER_INSERT_MODE_START_OF_LINE:
m_focused_buffer_pane->enter_insert_mode(EnterInsertModeMode::START_OF_LINE);
break;
case Command::ENTER_INSERT_MODE_NEW_LINE_BEFORE:
m_focused_buffer_pane->enter_insert_mode(EnterInsertModeMode::NEW_LINE_BEFORE);
break;
case Command::ENTER_INSERT_MODE_NEW_LINE_AFTER:
m_focused_buffer_pane->enter_insert_mode(EnterInsertModeMode::NEW_LINE_AFTER);
break;
case Command::PUT:
break;
case Command::PUT_BEFORE:
break;
case Command::CHANGE_CHAR:
break;
case Command::GO_START_OF_FILE:
m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::START_OF_FILE, 0u);
break;
case Command::GO_END_OF_FILE:
m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::END_OF_FILE, 0u);
break;
case Command::GO_TO_LINE:
m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::FIRST_LINE, 0u);
break;
case Command::GO_TO_LAST_LINE:
m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::LAST_LINE, 0u);
break;
case Command::GO_LEFT:
m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::LEFT, 0u);
break;
case Command::GO_DOWN:
m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::DOWN, 0u);
break;
case Command::GO_UP:
m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::UP, 0u);
break;
case Command::GO_RIGHT:
m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::RIGHT, 0u);
break;
case Command::GO_START_OF_LINE:
m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::SOL, 0u);
break;
case Command::GO_END_OF_LINE:
m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::EOL, 0u);
break;
case Command::GO_TOP_OF_SCREEN:
m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::TOP_OF_SCREEN, 0u);
break;
case Command::GO_MIDDLE_OF_SCREEN:
m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::MIDDLE_OF_SCREEN, 0u);
break;
case Command::GO_BOTTOM_OF_SCREEN:
m_focused_buffer_pane->cursor_move(BufferPane::CursorMovement::BOTTOM_OF_SCREEN, 0u);
break;
case Command::NEXT:
break;
case Command::PREV:
break;
case Command::SCROLL_WINDOW_UP_ONE_LINE:
m_focused_buffer_pane->scroll_window_up(ScrollMode::ONE_LINE);
break;
case Command::SCROLL_WINDOW_DOWN_ONE_LINE:
m_focused_buffer_pane->scroll_window_down(ScrollMode::ONE_LINE);
break;
case Command::SCROLL_WINDOW_UP_HALF_SCREEN:
m_focused_buffer_pane->scroll_window_up(ScrollMode::HALF_SCREEN);
break;
case Command::SCROLL_WINDOW_DOWN_HALF_SCREEN:
m_focused_buffer_pane->scroll_window_down(ScrollMode::HALF_SCREEN);
break;
case Command::SCROLL_WINDOW_UP_WHOLE_SCREEN:
m_focused_buffer_pane->scroll_window_up(ScrollMode::WHOLE_SCREEN);
break;
case Command::SCROLL_WINDOW_DOWN_WHOLE_SCREEN:
m_focused_buffer_pane->scroll_window_down(ScrollMode::WHOLE_SCREEN);
break;
case Command::ENTER_COMMAND_LINE_PROMPT:
m_command_buffer_pane->clear();
change_focus(m_command_buffer_pane);
break;
}
}
void Window::resize(size_t width, size_t 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();
m_buffer_pane->move(0, command_buffer_height + 1);
m_buffer_pane->resize(m_width, m_height - command_buffer_height - 1);
m_command_buffer_pane->resize(m_width, command_buffer_height);
m_command_buffer_pane->move(0, 0);
}
void Window::redraw()
{
static int last_command_buffer_screen_rows = 0;
/* TODO: figure out number of command buffer screen rows. */
m_command_buffer_screen_rows = 1;
if (m_command_buffer_screen_rows != last_command_buffer_screen_rows)
{
resize(m_width, m_height);
}
last_command_buffer_screen_rows = m_command_buffer_screen_rows;
glClear(GL_COLOR_BUFFER_BIT);
m_buffer_pane->draw();
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);
if (m_focused_buffer_pane->insert_mode())
{
m_command_buffer_pane->draw();
}
else if (m_status_message_timer != (size_t)-1)
{
draw_status_message();
}
else
{
draw_command_input();
}
Jtk_SwapBuffers(m_window);
}
void Window::draw_status_message()
{
int x = 0;
uint64_t current_time = Jtk_UsTime();
float alpha, g, b;
if (m_status_error)
{
g = 0.0;
b = 0.0;
}
else
{
g = 1.0;
b = 1.0;
}
if (((current_time - m_status_message_basetime) / 1000) < (STATUS_TIME / 2))
{
alpha = 1.0;
}
else
{
alpha = 1.0 - (((current_time - m_status_message_basetime) / 1000) - (STATUS_TIME / 2)) / (float)(STATUS_TIME / 2);
if (alpha < 0.0)
{
alpha = 0.0;
}
}
for (size_t i = 0u, length = m_status_message.size(); i < length; i++)
{
m_gl->draw_character(x, 0, m_status_message[i], *m_font, 1.0, g, b, alpha);
x += m_font->get_advance();
}
}
void Window::handle_status_message_timer()
{
if (((Jtk_UsTime() - m_status_message_basetime) / 1000) >= STATUS_TIME)
{
Jtk_RemoveTimer(m_status_message_timer);
m_status_message_timer = (size_t)-1;
}
m_redraw_requested = true;
}
void Window::draw_command_input()
{
int x = 0;
float g, b;
if (m_command_invalid)
{
g = 0.0;
b = 0.0;
}
else
{
g = 1.0;
b = 1.0;
}
for (size_t i = 0u, length = m_command_input.size(); i < length; i++)
{
m_gl->draw_character(x, 0, m_command_input[i], *m_font, 1.0, g, b, 1.0);
x += m_font->get_advance();
}
}
void Window::change_focus(std::shared_ptr<BufferPane> buffer_pane)
{
m_focused_buffer_pane->set_focused(false);
m_focused_buffer_pane = buffer_pane;
m_focused_buffer_pane->set_focused(true);
request_redraw();
}
static bool is_number(const EncodedString & s)
{
size_t size = s.size();
if (size < 1u)
return false;
for (size_t i = 0u; i < size; i++)
{
if ((s[i] < '0') || (s[i] > '9'))
return false;
}
return true;
}
void Window::set_status(const std::string & status, bool error)
{
clear_status();
m_status_error = error;
m_status_message = status;
m_status_message_basetime = Jtk_UsTime();
m_status_message_timer = Jtk_AddTimer(STATUS_TIME / 2, 50u, nullptr, nullptr);
}
void Window::clear_status()
{
if (m_status_message_timer != (size_t)-1)
{
Jtk_RemoveTimer(m_status_message_timer);
m_status_message_timer = (size_t)-1;
}
}
void Window::handle_command(const EncodedString & command)
{
CommandParser cp;
if (cp.parse(command))
{
if (cp.size() >= 1)
{
if (cp[0] == "w")
{
command_write_file(cp);
}
else if (cp[0] == "q")
{
command_quit(cp);
}
else if (is_number(cp[0]))
{
size_t target_line = (size_t)atoi((const char *)&cp[0][0]);
if (target_line != 0u)
{
target_line--;
}
m_focused_buffer_pane->cursor_move_to_line(target_line);
}
}
}
else
{
/* TODO: handle command parsing failure */
}
}
void Window::command_write_file(const CommandParser & cp)
{
std::string path;
std::shared_ptr<Buffer> buffer = m_focused_buffer_pane->buffer();
auto filename = buffer->filename();
if (cp.size() >= 2)
{
path = cp[1].to_string();
if (!filename)
{
buffer->set_filename(path);
}
}
else
{
if (filename)
{
path = *filename;
}
}
if (path != "")
{
size_t n_lines;
size_t n_bytes;
if (buffer->write_to_file(Path::clean(path).c_str(), &n_lines, &n_bytes))
{
char msg[100];
sprintf(msg, "Wrote file: %u lines, %u bytes",
(unsigned int)n_lines, (unsigned int)n_bytes);
set_status(msg);
}
else
{
set_status("Error writing file", true);
}
}
m_redraw_requested = true;
}
void Window::command_quit(const CommandParser & cp)
{
m_exit_requested = true;
}
+88
View File
@@ -0,0 +1,88 @@
#ifndef WINDOW_H
#define WINDOW_H
#include <memory>
#include <utility>
#include <vector>
#include "Font.h"
#include "Buffer.h"
#include "GL.h"
#include "EncodedString.h"
#include "CommandParser.h"
#include "Jtk.h"
#include "Command.h"
class BufferPane;
class Window
{
public:
enum class ScrollMode : uint8_t
{
ONE_LINE,
WHEEL,
HALF_SCREEN,
WHOLE_SCREEN,
};
enum class EnterInsertModeMode : uint8_t
{
START_OF_CHAR,
END_OF_CHAR,
START_OF_LINE,
END_OF_LINE,
NEW_LINE_BEFORE,
NEW_LINE_AFTER,
};
bool create(std::shared_ptr<Buffer> buffer);
void run_event_loop();
void request_redraw() { m_redraw_requested = true; }
std::shared_ptr<Font> font() const { return m_font; }
std::shared_ptr<GL> gl() const { return m_gl; }
protected:
void resize(size_t width, size_t height);
void redraw();
void handle_event(Jtk_Event & event);
void handle_keypress(uint32_t keyval);
void change_focus(std::shared_ptr<BufferPane> buffer_pane);
void set_window_icon();
void handle_command(const EncodedString & command);
void evaluate_command_input();
bool check_insert_mode_command(uint32_t insert_mode_command);
void execute_command(const Command & command);
void handle_status_message_timer();
void draw_command_input();
void draw_status_message();
void command_write_file(const CommandParser & cp);
void command_quit(const CommandParser & cp);
void set_status(const std::string & status, bool error = false);
void clear_status();
void * m_window;
bool m_exit_requested;
bool m_redraw_requested;
int m_width;
int m_height;
int m_command_buffer_screen_rows;
std::shared_ptr<Font> m_font;
uint32_t m_target_column;
std::shared_ptr<GL> m_gl;
std::shared_ptr<BufferPane> m_buffer_pane;
std::shared_ptr<BufferPane> m_focused_buffer_pane;
std::shared_ptr<Buffer> m_command_buffer;
std::shared_ptr<BufferPane> m_command_buffer_pane;
std::vector<uint32_t> m_command_input;
std::string m_status_message;
size_t m_status_message_timer;
uint64_t m_status_message_basetime;
bool m_status_error;
bool m_command_invalid;
};
#endif
+17
View File
@@ -0,0 +1,17 @@
#include "FlatShader.h"
#include "Runtime.h"
FlatShader::FlatShader()
{
std::string v_path = Runtime::find(Runtime::SHADER, "flat.v");
std::string f_path = Runtime::find(Runtime::SHADER, "flat.f");
m_program = glcxx::Program::create(
glcxx::Shader::create_from_file(GL_VERTEX_SHADER, v_path.c_str()),
glcxx::Shader::create_from_file(GL_FRAGMENT_SHADER, f_path.c_str()),
"coords", 0);
m_uniforms.viewport_size = m_program->get_uniform_location("viewport_size");
m_uniforms.position = m_program->get_uniform_location("position");
m_uniforms.color = m_program->get_uniform_location("color");
}
+40
View File
@@ -0,0 +1,40 @@
#ifndef FLATSHADER_H
#define FLATSHADER_H
#include "glcxx.hpp"
#include <memory>
class FlatShader
{
public:
FlatShader();
void use() { m_program->use(); }
void set_viewport_size(int width, int height)
{
glUniform2i(m_uniforms.viewport_size, width, height);
}
void set_color(float r, float g, float b, float a)
{
glUniform4f(m_uniforms.color, r, g, b, a);
}
void set_position(int x, int y)
{
glUniform2i(m_uniforms.position, x, y);
}
protected:
std::shared_ptr<glcxx::Program> m_program;
struct
{
GLint viewport_size;
GLint color;
GLint position;
} m_uniforms;
};
#endif
+51
View File
@@ -0,0 +1,51 @@
#include "GL.h"
GL::GL()
{
m_shaders.text = std::make_shared<TextShader>();
m_shaders.text->use();
m_shaders.text->set_texture(0);
m_shaders.text->set_color(1.0, 1.0, 1.0, 1.0);
m_shaders.rect = std::make_shared<RectShader>();
GLint rect_coords[] = {
0, 0,
1, 0,
1, 1,
0, 1};
m_rect_array = glcxx::Array::create();
m_rect_array->bind();
m_rect_buffer = glcxx::Buffer::create(GL_ARRAY_BUFFER, GL_STATIC_DRAW,
rect_coords, sizeof(rect_coords));
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, 0);
}
void GL::draw_rect(int x, int y, int width, int height,
float r, float g, float b, float a)
{
m_rect_array->bind();
m_shaders.rect->use();
m_shaders.rect->set_color(r, g, b, a);
m_shaders.rect->set_position(x, y);
m_shaders.rect->set_size(width, height);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
void GL::draw_character(int x, int y, uint32_t character, Font & font,
float r, float g, float b, float a)
{
m_shaders.text->use();
m_shaders.text->set_color(r, g, b, a);
m_shaders.text->set_position(x, y + font.get_baseline_offset());
font.get_glyph(character)->render();
}
void GL::resize(int width, int height)
{
m_shaders.text->use();
m_shaders.text->set_viewport_size(width, height);
m_shaders.rect->use();
m_shaders.rect->set_viewport_size(width, height);
}
+34
View File
@@ -0,0 +1,34 @@
#ifndef GL_H
#define GL_H
#include "glcxx.hpp"
#include "TextShader.h"
#include "FlatShader.h"
#include "RectShader.h"
#include "Font.h"
#include <memory>
class GL
{
public:
GL();
void draw_rect(int x, int y, int width, int height,
float r, float g, float b, float a);
void draw_character(int x, int y, uint32_t character, Font & font,
float r, float g, float b, float a);
void resize(int width, int height);
protected:
struct
{
std::shared_ptr<TextShader> text;
std::shared_ptr<FlatShader> flat;
std::shared_ptr<RectShader> rect;
} m_shaders;
std::shared_ptr<glcxx::Array> m_rect_array;
std::shared_ptr<glcxx::Buffer> m_rect_buffer;
};
#endif
+18
View File
@@ -0,0 +1,18 @@
#include "RectShader.h"
#include "Runtime.h"
RectShader::RectShader()
{
std::string v_path = Runtime::find(Runtime::SHADER, "rect.v");
std::string f_path = Runtime::find(Runtime::SHADER, "rect.f");
m_program = glcxx::Program::create(
glcxx::Shader::create_from_file(GL_VERTEX_SHADER, v_path.c_str()),
glcxx::Shader::create_from_file(GL_FRAGMENT_SHADER, f_path.c_str()),
"coords", 0);
m_uniforms.viewport_size = m_program->get_uniform_location("viewport_size");
m_uniforms.color = m_program->get_uniform_location("color");
m_uniforms.position = m_program->get_uniform_location("position");
m_uniforms.size = m_program->get_uniform_location("size");
}
+46
View File
@@ -0,0 +1,46 @@
#ifndef RECTSHADER_H
#define RECTSHADER_H
#include "glcxx.hpp"
#include <memory>
class RectShader
{
public:
RectShader();
void use() { m_program->use(); }
void set_viewport_size(int width, int height)
{
glUniform2i(m_uniforms.viewport_size, width, height);
}
void set_color(float r, float g, float b, float a)
{
glUniform4f(m_uniforms.color, r, g, b, a);
}
void set_position(int x, int y)
{
glUniform2i(m_uniforms.position, x, y);
}
void set_size(int x, int y)
{
glUniform2i(m_uniforms.size, x, y);
}
protected:
std::shared_ptr<glcxx::Program> m_program;
struct
{
GLint viewport_size;
GLint color;
GLint position;
GLint size;
} m_uniforms;
};
#endif
+18
View File
@@ -0,0 +1,18 @@
#include "TextShader.h"
#include "Runtime.h"
TextShader::TextShader()
{
std::string v_path = Runtime::find(Runtime::SHADER, "text.v");
std::string f_path = Runtime::find(Runtime::SHADER, "text.f");
m_program = glcxx::Program::create(
glcxx::Shader::create_from_file(GL_VERTEX_SHADER, v_path.c_str()),
glcxx::Shader::create_from_file(GL_FRAGMENT_SHADER, f_path.c_str()),
"coords", 0);
m_uniforms.viewport_size = m_program->get_uniform_location("viewport_size");
m_uniforms.texture = m_program->get_uniform_location("texture");
m_uniforms.color = m_program->get_uniform_location("color");
m_uniforms.position = m_program->get_uniform_location("position");
}
+46
View File
@@ -0,0 +1,46 @@
#ifndef TEXTSHADER_H
#define TEXTSHADER_H
#include "glcxx.hpp"
#include <memory>
class TextShader
{
public:
TextShader();
void use() { m_program->use(); }
void set_viewport_size(int width, int height)
{
glUniform2i(m_uniforms.viewport_size, width, height);
}
void set_texture(int texture)
{
glUniform1i(m_uniforms.texture, texture);
}
void set_color(float r, float g, float b, float a)
{
glUniform4f(m_uniforms.color, r, g, b, a);
}
void set_position(int x, int y)
{
glUniform2i(m_uniforms.position, x, y);
}
protected:
std::shared_ptr<glcxx::Program> m_program;
struct
{
GLint viewport_size;
GLint texture;
GLint color;
GLint position;
} m_uniforms;
};
#endif
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+99
View File
@@ -0,0 +1,99 @@
#include "jes_icon-32x32.h"
/* GIMP RGB C-Source image dump (jes_icon-32x32.c) */
const uint8_t jes_icon_32x32[] = {
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\377\063\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377\063\377\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000PPP\377\377\377\040\040\040\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377\063\377\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000PPP\377\377\377\040\040\040\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377\063\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\377\063\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\310\310\310\377\377\377"
"\377\377\377\377\377\377\040\040\040\000\000\000\000\000\000\000\000\000***\261\261\261\361\361"
"\361\365\365\365\265\265\265\"\"\"\000\000\000\000\000\000\000\000\000\000\000\000;;;\300\300\300"
"\360\360\360\347\347\347\240\240\240\033\033\033\000\000\000\000\000\000\377\063\377\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000PPP\377\377\377\040\040\040\000\000\000\000\000"
"\000&&&\360\360\360\252\252\252\035\035\035\030\030\030\247\247\247\334\334\334\005"
"\005\005\000\000\000\000\000\000\022\022\022\360\360\360\221\221\221\022\022\022\025\025\025[[[\177"
"\177\177\000\000\000\000\000\000\377\063\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"PPP\377\377\377\040\040\040\000\000\000\000\000\000\252\252\252\344\344\344\010\010\010\000\000"
"\000\000\000\000\017\017\017\366\366\366```\000\000\000\000\000\000@@@\377\377\377\064\064\064\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377\063\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000PPP\377\377\377\040\040\040\000\000\000\000\000\000\357\357\357\236\236\236"
"\000\000\000\000\000\000\000\000\000\000\000\000\320\320\320\222\222\222\000\000\000\000\000\000\034\034\034\372"
"\372\372\277\277\277JJJ\017\017\017\000\000\000\000\000\000\000\000\000\000\000\000\377\063\377\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000PPP\377\377\377\040\040\040\000\000\000\000\000\000"
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\255\255\255\000\000\000\000\000\000\000\000\000LLL\310\310\310\375\375\375\376"
"\376\376\307\307\307\061\061\061\000\000\000\000\000\000\377\063\377\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000PPP\377\377\377\040\040\040\000\000\000\000\000\000\360\360\360\211"
"\211\211\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\013\013\013RRR\342\342\342\334\334\334\000\000\000\000\000\000\377\063\377\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000PPP\377\377\377\040\040\040\000\000\000\000\000\000\255\255"
"\255\312\312\312\001\001\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000vvv\372\372\372\003\003\003\000\000\000\377\063\377\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000PPP\377\377\377\040\040\040\000\000\000\000\000\000***\363"
"\363\363\232\232\232\"\"\"\013\013\013\065\065\065\230\230\230TTT\000\000\000\000\000\000M"
"MM\227\227\227\063\063\063\012\012\012)))\320\320\320\266\266\266\000\000\000\000\000\000\377"
"\063\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000PPP\377\377\377\040\040\040"
"\000\000\000\000\000\000\000\000\000+++\255\255\255\355\355\355\365\365\365\312\312\312iii"
"\007\007\007\000\000\000\000\000\000\006\006\006hhh\314\314\314\365\365\365\343\343\343\230\230\230"
"\023\023\023\000\000\000\000\000\000\377\063\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000^^^\377\377\377\016\016\016\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\377\063\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\016\016\016\274\274"
"\274\316\316\316\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\377\063\377\000\000\000\000\000\000\000\000\000\000\000\000\214\214\214\377\377\377\371\371\371"
"\306\306\306+++\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\377\063\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377\063\377\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377\063\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
};
+8
View File
@@ -0,0 +1,8 @@
#ifndef JES_ICON_32X32_H
#define JES_ICON_32X32_H
#include <stdint.h>
extern const uint8_t jes_icon_32x32[];
#endif
+59
View File
@@ -0,0 +1,59 @@
#include "Jtk.h"
#ifdef JTK_X
#include <GL/glx.h>
#include <stdio.h>
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
+12
View File
@@ -0,0 +1,12 @@
#ifndef JTK_H
#define JTK_H
#include "Jtk_event.h"
#include "keys.h"
#include "Jtk_time.h"
#include "Jtk_timer.h"
#include "Jtk_window.h"
bool Jtk_Init();
#endif
+482
View File
@@ -0,0 +1,482 @@
#include "Jtk.h"
#ifdef JTK_X
#include <GL/glx.h>
#include <sys/select.h>
#include <sys/types.h>
#include <unistd.h>
#include "Jtk_internal.h"
#include <unordered_map>
/** Do not wait longer than 100ms */
#define MAX_WAIT_TIME 100000u
static std::unordered_map<unsigned int, size_t> g_repeating_keys;
static std::unordered_map<size_t, unsigned int> 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 |= JES_KEY_MODS_SHIFT;
}
if (x_state & LockMask)
{
mods |= JES_KEY_MODS_LOCK;
}
if (x_state & ControlMask)
{
mods |= JES_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 = JES_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 = JES_KEY_F1; break;
case XK_F2: key = JES_KEY_F2; break;
case XK_F3: key = JES_KEY_F3; break;
case XK_F4: key = JES_KEY_F4; break;
case XK_F5: key = JES_KEY_F5; break;
case XK_F6: key = JES_KEY_F6; break;
case XK_F7: key = JES_KEY_F7; break;
case XK_F8: key = JES_KEY_F8; break;
case XK_F9: key = JES_KEY_F9; break;
case XK_F10: key = JES_KEY_F10; break;
case XK_F11: key = JES_KEY_F11; break;
case XK_F12: key = JES_KEY_F12; break;
case XK_F13: key = JES_KEY_F13; break;
case XK_F14: key = JES_KEY_F14; break;
case XK_F15: key = JES_KEY_F15; break;
case XK_F16: key = JES_KEY_F16; break;
case XK_F17: key = JES_KEY_F17; break;
case XK_F18: key = JES_KEY_F18; break;
case XK_F19: key = JES_KEY_F19; break;
case XK_F20: key = JES_KEY_F20; break;
case XK_F21: key = JES_KEY_F21; break;
case XK_F22: key = JES_KEY_F22; break;
case XK_F23: key = JES_KEY_F23; break;
case XK_F24: key = JES_KEY_F24; break;
case XK_F25: key = JES_KEY_F25; break;
case XK_F26: key = JES_KEY_F26; break;
case XK_F27: key = JES_KEY_F27; break;
case XK_F28: key = JES_KEY_F28; break;
case XK_F29: key = JES_KEY_F29; break;
case XK_F30: key = JES_KEY_F30; break;
case XK_F31: key = JES_KEY_F31; break;
case XK_Shift_L: key = JES_KEY_SHIFT_L; break;
case XK_Shift_R: key = JES_KEY_SHIFT_R; break;
case XK_Control_L: key = JES_KEY_CTRL_L; break;
case XK_Control_R: key = JES_KEY_CTRL_R; break;
case XK_Caps_Lock: key = JES_KEY_CAPS_LOCK; break;
case XK_Shift_Lock: key = JES_KEY_SHIFT_LOCK;break;
case XK_Meta_L: key = JES_KEY_META_L; break;
case XK_Meta_R: key = JES_KEY_META_R; break;
case XK_Alt_L: key = JES_KEY_ALT_L; break;
case XK_Alt_R: key = JES_KEY_ALT_R; break;
case XK_Super_L: key = JES_KEY_SUPER_L; break;
case XK_Super_R: key = JES_KEY_SUPER_R; break;
case XK_Home: key = JES_KEY_HOME; break;
case XK_Left: key = JES_KEY_LEFT; break;
case XK_Up: key = JES_KEY_UP; break;
case XK_Right: key = JES_KEY_RIGHT; break;
case XK_Down: key = JES_KEY_DOWN; break;
case XK_Page_Up: key = JES_KEY_PAGE_UP; break;
case XK_Page_Down: key = JES_KEY_PAGE_DOWN; break;
case XK_End: key = JES_KEY_END; break;
case XK_Begin: key = JES_KEY_BEGIN; break;
case XK_Select: key = JES_KEY_SELECT; break;
case XK_Print: key = JES_KEY_PRINT; break;
case XK_Execute: key = JES_KEY_EXECUTE; break;
case XK_Insert: key = JES_KEY_INSERT; break;
case XK_Undo: key = JES_KEY_UNDO; break;
case XK_Redo: key = JES_KEY_REDO; break;
case XK_Menu: key = JES_KEY_MENU; break;
case XK_Find: key = JES_KEY_FIND; break;
case XK_Cancel: key = JES_KEY_CANCEL; break;
case XK_Help: key = JES_KEY_HELP; break;
case XK_Break: key = JES_KEY_BREAK; break;
case XK_Num_Lock: key = JES_KEY_NUM_LOCK; break;
case XK_KP_Space: key = JES_KEY_KP_SPACE; break;
case XK_KP_Tab: key = JES_KEY_KP_TAB; break;
case XK_KP_Enter: key = JES_KEY_KP_ENTER; break;
case XK_KP_F1: key = JES_KEY_KP_F1; break;
case XK_KP_F2: key = JES_KEY_KP_F2; break;
case XK_KP_F3: key = JES_KEY_KP_F3; break;
case XK_KP_F4: key = JES_KEY_KP_F4; break;
case XK_KP_Home: key = JES_KEY_KP_HOME; break;
case XK_KP_Left: key = JES_KEY_KP_LEFT; break;
case XK_KP_Up: key = JES_KEY_KP_UP; break;
case XK_KP_Right: key = JES_KEY_KP_RIGHT; break;
case XK_KP_Down: key = JES_KEY_KP_DOWN; break;
case XK_KP_Page_Up: key = JES_KEY_KP_PAGE_UP; break;
case XK_KP_Page_Down: key = JES_KEY_KP_PAGE_DOWN; break;
case XK_KP_End: key = JES_KEY_KP_END; break;
case XK_KP_Begin: key = JES_KEY_KP_BEGIN; break;
case XK_KP_Insert: key = JES_KEY_KP_INSERT; break;
case XK_KP_Delete: key = JES_KEY_KP_DELETE; break;
case XK_KP_Equal: key = JES_KEY_KP_EQUAL; break;
case XK_KP_Multiply: key = JES_KEY_KP_MULTIPLY; break;
case XK_KP_Add: key = JES_KEY_KP_ADD; break;
case XK_KP_Separator: key = JES_KEY_KP_SEPARATOR; break;
case XK_KP_Subtract: key = JES_KEY_KP_SUBTRACT; break;
case XK_KP_Decimal: key = JES_KEY_KP_DECIMAL; break;
case XK_KP_Divide: key = JES_KEY_KP_DIVIDE; break;
case XK_KP_0: key = JES_KEY_KP_0; break;
case XK_KP_1: key = JES_KEY_KP_1; break;
case XK_KP_2: key = JES_KEY_KP_2; break;
case XK_KP_3: key = JES_KEY_KP_3; break;
case XK_KP_4: key = JES_KEY_KP_4; break;
case XK_KP_5: key = JES_KEY_KP_5; break;
case XK_KP_6: key = JES_KEY_KP_6; break;
case XK_KP_7: key = JES_KEY_KP_7; break;
case XK_KP_8: key = JES_KEY_KP_8; break;
case XK_KP_9: key = JES_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 ClientMessage event.
*
* @param x_event
* Pointer to the X event.
* @param event
* Pointer to the Jtk event.
*/
static bool ProcessXClientMessageEvent(XEvent * x_event, Jtk_Event * event)
{
Atom wm_delete_window_atom = XInternAtom(g_display, "WM_DELETE_WINDOW", False);
if (x_event->xclient.data.l[0] == (long)wm_delete_window_atom)
{
event->type = JTK_EVENT_WINDOW_CLOSE;
return true;
}
return false;
}
/**
* 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 ClientMessage:
return ProcessXClientMessageEvent(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
+62
View File
@@ -0,0 +1,62 @@
#ifndef JTK_EVENT_H
#define JTK_EVENT_H
#include <stdint.h>
#include <stdlib.h>
#define JTK_EVENT_WINDOW_CLOSE 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
+9
View File
@@ -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
+9
View File
@@ -0,0 +1,9 @@
#include "Jtk.h"
#include <sys/time.h>
uint64_t Jtk_UsTime()
{
struct timeval tv;
gettimeofday(&tv, nullptr);
return (uint64_t)tv.tv_sec * 1000000u + (uint64_t)tv.tv_usec;
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef JTK_TIME_H
#define JTK_TIME_H
#include "Jtk_timer.h"
bool Jtk_Init();
uint64_t Jtk_UsTime();
#endif
+157
View File
@@ -0,0 +1,157 @@
#include "Jtk.h"
#include <vector>
#include <memory>
#include <list>
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<std::shared_ptr<Timer>> g_timers;
/** Linked list used to traverse all active timers. */
static std::list<std::shared_ptr<Timer>> 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>();
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;
}
+14
View File
@@ -0,0 +1,14 @@
#ifndef JTK_TIMER_H
#define JTK_TIMER_H
#include <stdint.h>
#include <stdlib.h>
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
+95
View File
@@ -0,0 +1,95 @@
#include "Jtk.h"
#ifdef JTK_X
#include <GL/glx.h>
#include "Jtk_internal.h"
#include <stdio.h>
#include <X11/Xatom.h>
#include <cstring>
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;
}
/* Disable the window close button. */
Atom wm_delete_window_atom = XInternAtom(g_display, "WM_DELETE_WINDOW", False);
XSetWMProtocols(g_display, window, &wm_delete_window_atom, 1);
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);
}
}
/**
* Set the window icon.
*
* @param window
* The window to operate on.
* @param data
* The format of data must be 32 bits per pixel in BGRA format (e.g. data[0]
* is blue value, data[3] is alpha value of first pixel).
* @param width
* Icon width.
* @param height
* Icon height.
*/
void Jtk_SetWindowIcon(void * window, const uint8_t * data,
size_t width, size_t height)
{
Atom net_wm_icon_atom = XInternAtom(g_display, "_NET_WM_ICON", False);
size_t property_size = (2u + width * height) * sizeof(long);
unsigned long * property_data = (unsigned long *)malloc(property_size);
property_data[0] = width;
property_data[1] = height;
unsigned long * dest = &property_data[2];
const uint32_t * src = (const uint32_t *)data;
for (size_t row = 0u; row < height; row++)
{
for (size_t col = 0u; col < width; col++)
{
*dest++ = *src++;
}
}
XChangeProperty(g_display, (Window)window, net_wm_icon_atom, XA_CARDINAL,
32, PropModeReplace, (uint8_t *)property_data, property_size);
XFlush(g_display);
free(property_data);
}
#endif
+11
View File
@@ -0,0 +1,11 @@
#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);
void Jtk_SetWindowIcon(void * window, const uint8_t * data,
size_t width, size_t height);
#endif