From d6271e513d58513a3b54fa898566efbd56566d13 Mon Sep 17 00:00:00 2001 From: Chris West Date: Sat, 30 Mar 2019 07:18:42 +0000 Subject: [PATCH] Rewrite linux backend in rust (#70) * x11-rs : don't build libminifb_native.a * x11-rs : commented out all calls to the mfb_xxx functions. * x11-rs : added handle field to Window, type is xlib::Window. Also implemented the get_window_handle() method. * x11-rs : created a DisplayInfo struct with a setup() method. * x11-rs : fleshed out DisplayInfo::setup() method some more. * x11-rs : updated Cargo.toml to require x11_dl "2.16". That is because we need the XContext type, which seems to be unavailable in the 2.14 version. * x11-rs : code to call the XCreateWindow() function. * x11-rs : call XMapRaise() and XFlush() on our window. * x11-rs : more work on Window::new() -- ported more stuff from mfb_open(). * x11-rs : create the 'ximage' field in Window::new(). * x11-rs : removed SharedData struct, moved fields into Window. * x11-rs : ported the set_window_title() method. * x11-rs : ported creating a "draw_buffer" and the 1:1 scaling code. * x11-rs : ported the scale_2x() function. * x11-rs : ported the scale_4x() function. * x11-rs : ported the set_position() method. * x11-rs : ported the mfb_close() code, i.e. Drop trait on a Window. * x11-rs : tidied up code computing "scale" value for a new window. * x11-rs : implemented Drop trait for DisplayInfo. * x11-rs : updated the get_scroll_wheel() method. * x11-rs : tidy up update_with_buffer() and check buffer size. * x11-rs : ported the check_formats() code. * x11-rs : ported the init_cursors() code. * x11-rs : ported the set_cursor_style() code. * x11-rs : ported the raw_get_mouse_pos() code. * x11-rs : moved the code implementing Drop trait for Window. * x11-rs : ported the basic event loop (i.e. raw_process_events). * x11-rs : ported code handling the WM_DELETE_WINDOW client message. * x11-rs : added code file "src/os/unix/key_mapping.rs" This file contains code to map an X keysym to a Unicode char, which is done via a binary search on a large table. I ported the code to Rust and rewrote the binary search as a recursive function. * x11-rs : ported the XkbQueryExtension() code. * x11-rs : fleshed out the process_key() code. * x11-rs : added test code for keysym_to_unicode(), fixed a table entry. * x11-rs : another test value for the key_mapping test code. * x11-rs : ported the update_key_state() code. * x11-rs : ported the char_callback() code. * x11-rs : ported the mouse button handling code. * x11-rs : ported code handling the XConfigureNotify event. * x11-rs : code tidying, silence most compiler warnings. * x11-rs : removed the native C code: X11MiniFB.c * x11-rs : fixed bug in the button handling code. * x11-rs : made the scale_2x() and scale_4x() code faster. * merge error * whitespace * fix cursors: bad string handling * upgrade x11-dl * merge scaling implementations * multi-window example --- Cargo.toml | 13 +- build.rs | 4 - examples/multi.rs | 31 + src/native/x11/X11MiniFB.c | 1709 ------------------------------------ src/os/unix/key_mapping.rs | 914 +++++++++++++++++++ src/os/unix/mod.rs | 1161 +++++++++++++++++------- 6 files changed, 1809 insertions(+), 2023 deletions(-) create mode 100644 examples/multi.rs delete mode 100644 src/native/x11/X11MiniFB.c create mode 100644 src/os/unix/key_mapping.rs diff --git a/Cargo.toml b/Cargo.toml index fad6e7e..f2fc770 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ travis-ci = { repository = "emoon/rust_minifb" } cc = "1.0" [dependencies] +cast = "0.2" time = "0.1.34" [target.'cfg(windows)'.dependencies.winapi] @@ -31,22 +32,22 @@ features = [ ] [target.i686-unknown-linux-gnu.dependencies] -x11-dl = "~2.14" +x11-dl = "2.18.3" [target.x86_64-unknown-linux-gnu.dependencies] -x11-dl = "~2.14" +x11-dl = "2.18.3" [target.arm-unknown-linux-gnueabihf.dependencies] -x11-dl = "~2.14" +x11-dl = "2.18.3" [target.aarch64-unknown-linux-gnu.dependencies] -x11-dl = "~2.14" +x11-dl = "2.18.3" [target.x86_64-unknown-dragonfly.dependencies] -x11-dl = "~2.14" +x11-dl = "2.18.3" [target.x86_64-unknown-freebsd.dependencies] -x11-dl = "~2.14" +x11-dl = "2.18.3" [target.x86_64-unknown-redox.dependencies] orbclient = "0.3.20" diff --git a/build.rs b/build.rs index 0a27206..c585ee9 100644 --- a/build.rs +++ b/build.rs @@ -12,9 +12,5 @@ fn main() { .compile("libminifb_native.a"); println!("cargo:rustc-link-lib=framework=Metal"); println!("cargo:rustc-link-lib=framework=MetalKit"); - } else if env.contains("linux") { - cc::Build::new() - .file("src/native/x11/X11MiniFB.c") - .compile("libminifb_native.a"); } } diff --git a/examples/multi.rs b/examples/multi.rs new file mode 100644 index 0000000..a58f839 --- /dev/null +++ b/examples/multi.rs @@ -0,0 +1,31 @@ +extern crate minifb; + +use minifb::{WindowOptions, Window, Scale, Key}; + +fn main() { + let width = 640; + let height = 320; + let mut buffer = vec![0u32; width * height]; + let mut orig = Window::new("Smaller", width, height, WindowOptions { + resize: true, + ..WindowOptions::default() + }).unwrap(); + let mut double = Window::new("Larger", width, height, WindowOptions { + resize: true, + scale: Scale::X2, + ..WindowOptions::default() + }).unwrap(); + + let mut pos = 13; + + while orig.is_open() && double.is_open() + && !orig.is_key_down(Key::Escape) + && !double.is_key_down(Key::Escape) { + orig.update_with_buffer(&buffer).unwrap(); + double.update_with_buffer(&buffer).unwrap(); + pos += 7; + pos *= 13; + pos %= buffer.len(); + buffer[pos] = 0xff_ff_ff; + } +} diff --git a/src/native/x11/X11MiniFB.c b/src/native/x11/X11MiniFB.c deleted file mode 100644 index 7ae47ee..0000000 --- a/src/native/x11/X11MiniFB.c +++ /dev/null @@ -1,1709 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -#define KEY_FUNCTION 0xFF -#define KEY_ESC 0x1B -#define Button6 6 -#define Button7 7 - -static long keySym2Unicode(unsigned int keysym); - -// window_handler.rs -const uint32_t WINDOW_BORDERLESS = 1 << 1; -const uint32_t WINDOW_RESIZE = 1 << 2; -const uint32_t WINDOW_TITLE = 1 << 3; - -void mfb_close(void* window_info); - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -static int s_window_count = 0; -static Display* s_display; -static int s_screen; -static GC s_gc; -static int s_depth; -static int s_setup_done = 0; -static Visual* s_visual; -static int s_screen_width; -static int s_screen_height; -static int s_keyb_ext = 0; -static XContext s_context; -static Atom s_wm_delete_window; - -// Needs to match lib.rs enum -enum CursorStyle { - CursorStyle_Arrow, - CursorStyle_Ibeam, - CursorStyle_Crosshair, - CursorStyle_ClosedHand, - CursorStyle_OpenHand, - CursorStyle_ResizeLeftRight, - CursorStyle_ResizeUpDown, - CursorStyle_SizeAll, - CursorStyle_Count, -}; - -static Cursor s_cursors[CursorStyle_Count]; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -typedef struct SharedData { - uint32_t width; - uint32_t height; - float scale; - float mouse_x; - float mouse_y; - float scroll_x; - float scroll_y; - uint8_t state[3]; -} SharedData; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -typedef struct WindowInfo { - void (*key_callback)(void* user_data, int key, int state); - void (*char_callback)(void* user_data, unsigned int c); - void* rust_data; - SharedData* shared_data; - Window window; - XImage* ximage; - void* draw_buffer; - int scale; - int width; - int height; - int update; - int prev_cursor; -} WindowInfo; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -static void init_cursors() { - s_cursors[CursorStyle_Arrow] = XcursorLibraryLoadCursor(s_display, "arrow"); - s_cursors[CursorStyle_Ibeam] = XcursorLibraryLoadCursor(s_display, "xterm"); - s_cursors[CursorStyle_Crosshair] = XcursorLibraryLoadCursor(s_display, "crosshair"); - s_cursors[CursorStyle_ClosedHand] = XcursorLibraryLoadCursor(s_display, "hand2"); - s_cursors[CursorStyle_OpenHand] = XcursorLibraryLoadCursor(s_display, "hand2"); - s_cursors[CursorStyle_ResizeLeftRight] = XcursorLibraryLoadCursor(s_display, "sb_h_double_arrow"); - s_cursors[CursorStyle_ResizeUpDown] = XcursorLibraryLoadCursor(s_display, "sb_v_double_arrow"); - s_cursors[CursorStyle_SizeAll] = XcursorLibraryLoadCursor(s_display, "diamond_cross"); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -static int setup_display() { - int major = 1; - int minor = 0; - int majorOpcode = 0; - int eventBase = 0; - int errorBase = 0; - - int depth, i, formatCount, convDepth = -1; - XPixmapFormatValues* formats; - - if (s_setup_done) { - return 1; - } - - s_display = XOpenDisplay(0); - - if (!s_display) { - printf("Unable to open X11 display\n"); - return 0; - } - - s_context = XUniqueContext(); - s_screen = DefaultScreen(s_display); - s_visual = DefaultVisual(s_display, s_screen); - formats = XListPixmapFormats(s_display, &formatCount); - depth = DefaultDepth(s_display, s_screen); - - for (i = 0; i < formatCount; ++i) { - if (depth == formats[i].depth) { - convDepth = formats[i].bits_per_pixel; - break; - } - } - - XFree(formats); - - // We only support 32-bit right now - if (convDepth != 32) { - printf("Unable to find 32-bit format for X11 display\n"); - XCloseDisplay(s_display); - return 0; - } - - s_depth = depth; - - s_gc = DefaultGC(s_display, s_screen); - - s_screen_width = DisplayWidth(s_display, s_screen); - s_screen_height = DisplayHeight(s_display, s_screen); - - const char* wmDeleteWindowName = "WM_DELETE_WINDOW"; - XInternAtoms(s_display, (char**)&wmDeleteWindowName, 1, False, &s_wm_delete_window); - - s_setup_done = 1; - - init_cursors(); - - s_keyb_ext = XkbQueryExtension(s_display, &majorOpcode, &eventBase, &errorBase, &major, &minor); - - return 1; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void* mfb_open(const char* title, int width, int height, unsigned int flags, int scale) -{ - XSetWindowAttributes windowAttributes; - XSizeHints sizeHints; - XImage* image; - Window window; - WindowInfo* window_info; - - - if (!setup_display()) { - return 0; - } - - //TODO: Handle no title/borderless - (void)flags; - - width *= scale; - height *= scale; - - Window defaultRootWindow = DefaultRootWindow(s_display); - - windowAttributes.border_pixel = BlackPixel(s_display, s_screen); - windowAttributes.background_pixel = BlackPixel(s_display, s_screen); - windowAttributes.backing_store = NotUseful; - - window = XCreateWindow(s_display, defaultRootWindow, (s_screen_width - width) / 2, - (s_screen_height - height) / 2, width, height, 0, s_depth, InputOutput, - s_visual, CWBackPixel | CWBorderPixel | CWBackingStore, - &windowAttributes); - if (!window) { - printf("Unable to create X11 Window\n"); - return 0; - } - - //XSelectInput(s_display, s_window, KeyPressMask | KeyReleaseMask); - XStoreName(s_display, window, title); - - XSelectInput(s_display, window, - StructureNotifyMask | - ButtonPressMask | KeyPressMask | KeyReleaseMask | ButtonReleaseMask); - - if (!(flags & WINDOW_RESIZE)) { - sizeHints.flags = PPosition | PMinSize | PMaxSize; - sizeHints.x = 0; - sizeHints.y = 0; - sizeHints.min_width = width; - sizeHints.max_width = width; - sizeHints.min_height = height; - sizeHints.max_height = height; - XSetWMNormalHints(s_display, window, &sizeHints); - } - - XClearWindow(s_display, window); - XMapRaised(s_display, window); - XFlush(s_display); - - image = XCreateImage(s_display, CopyFromParent, s_depth, ZPixmap, 0, NULL, width, height, 32, width * 4); - - if (!image) { - XDestroyWindow(s_display, window); - printf("Unable to create XImage\n"); - return 0; - } - - window_info = (WindowInfo*)malloc(sizeof(WindowInfo)); - window_info->key_callback = 0; - window_info->char_callback = 0; - window_info->rust_data = 0; - window_info->window = window; - window_info->ximage = image; - window_info->scale = scale; - window_info->width = width; - window_info->height = height; - window_info->draw_buffer = malloc(width * height * 4); - window_info->update = 1; - - XSetWMProtocols(s_display, window, &s_wm_delete_window, 1); - - XSaveContext(s_display, window, s_context, (XPointer) window_info); - - image->data = (char*)window_info->draw_buffer; - - s_window_count += 1; - - return (void*)window_info; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void mfb_set_title(void* window_info, const char* title) -{ - WindowInfo* info = (WindowInfo*)window_info; - XStoreName(s_display, info->window, title); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -static WindowInfo* find_handle(Window handle) -{ - WindowInfo* info; - - if (XFindContext(s_display, handle, s_context, (XPointer*) &info) != 0) { - return 0; - } - - return info; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void mfb_set_cursor_style(void* window_info, int cursor) -{ - WindowInfo* info = (WindowInfo*)window_info; - - if (info->prev_cursor == cursor) - return; - - if (cursor < 0 || cursor >= CursorStyle_Count) { - printf("cursor out of range %d\n", cursor); - return; - } - - XDefineCursor(s_display, info->window, s_cursors[cursor]); - - info->prev_cursor = cursor; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -static int handle_special_keys(WindowInfo* info, XEvent* event, int down) { - int keySym; - - if (!s_keyb_ext) - return 0; - - keySym = XkbKeycodeToKeysym(s_display, event->xkey.keycode, 0, 1); - - switch (keySym) - { - case XK_KP_0: - case XK_KP_1: - case XK_KP_2: - case XK_KP_3: - case XK_KP_4: - case XK_KP_5: - case XK_KP_6: - case XK_KP_7: - case XK_KP_8: - case XK_KP_9: - case XK_KP_Separator: - case XK_KP_Decimal: - case XK_KP_Equal: - case XK_KP_Enter: - { - if (info->key_callback) { - info->key_callback(info->rust_data, keySym, down); - return 1; - } - } - } - - return 0; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -static int process_event(XEvent* event) { - KeySym sym; - - WindowInfo* info = find_handle(event->xany.window); - - if (!info) - return 1; - - if (event->type == ClientMessage) { - if ((Atom)event->xclient.data.l[0] == s_wm_delete_window) { - info->update = 0; - mfb_close(info); - - return 0; - } - } - - switch (event->type) - { - case KeyPress: - { - sym = XLookupKeysym(&event->xkey, 0); - - if (handle_special_keys(info, event, 1)) - break; - - if (info->key_callback) - info->key_callback(info->rust_data, sym, 1); - - if (info->char_callback) { - unsigned int t = keySym2Unicode(sym); - if (t != -1) - info->char_callback(info->rust_data, t); - } - - break; - } - - case KeyRelease: - { - if (handle_special_keys(info, event, 0)) - break; - - sym = XLookupKeysym(&event->xkey, 0); - - if (info->key_callback) - info->key_callback(info->rust_data, sym, 0); - break; - } - - case ButtonPress: - { - if (!info->shared_data) - break; - - if (event->xbutton.button == Button1) - info->shared_data->state[0] = 1; - else if (event->xbutton.button == Button2) - info->shared_data->state[1] = 1; - else if (event->xbutton.button == Button3) - info->shared_data->state[2] = 1; - else if (event->xbutton.button == Button4) - info->shared_data->scroll_y = 10.0f; - else if (event->xbutton.button == Button5) - info->shared_data->scroll_y = -10.0f; - else if (event->xbutton.button == Button6) - info->shared_data->scroll_x = 10.0f; - else if (event->xbutton.button == Button7) - info->shared_data->scroll_y = -10.0f; - - break; - } - - case ButtonRelease: - { - if (!info->shared_data) - break; - - if (event->xbutton.button == Button1) - info->shared_data->state[0] = 0; - else if (event->xbutton.button == Button2) - info->shared_data->state[1] = 0; - else if (event->xbutton.button == Button3) - info->shared_data->state[2] = 0; - - break; - } - - case ConfigureNotify: - { - info->width = event->xconfigure.width; - info->height = event->xconfigure.height; - break; - } - } - - return 1; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -static void get_mouse_pos(WindowInfo* info) { - Window root, child; - int rootX, rootY, childX, childY; - unsigned int mask; - - XQueryPointer(s_display, info->window, - &root, &child, - &rootX, &rootY, &childX, &childY, - &mask); - - if (info->shared_data) { - info->shared_data->mouse_x = (float)childX; - info->shared_data->mouse_y = (float)childY; - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -static int process_events() -{ - int count; - XEvent event; - KeySym sym; - - count = XPending(s_display); - - while (count--) - { - XEvent event; - XNextEvent(s_display, &event); - - // Don't process any more messages if event is 0 - if (process_event(&event) == 0) - return 0; - } - - return 0; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -static void scale_2x(unsigned int* dest, unsigned int* source, int width, int height, int scale) { - int x, y; - for (y = 0; y < height; y += scale) { - for (x = 0; x < width; x += scale) { - const unsigned int t = *source++; - dest[0] = t; - dest[1] = t; - dest[width + 0] = t; - dest[width + 1] = t; - dest += scale; - } - - dest += width * (scale - 1); - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -static void scale_4x(unsigned int* dest, unsigned int* source, int width, int height, int scale) { - int x, y; - for (y = 0; y < height; y += scale) { - for (x = 0; x < width; x += scale) { - const unsigned int t = *source++; - dest[(width * 0) + 0] = t; - dest[(width * 0) + 1] = t; - dest[(width * 0) + 2] = t; - dest[(width * 0) + 3] = t; - dest[(width * 1) + 0] = t; - dest[(width * 1) + 1] = t; - dest[(width * 1) + 2] = t; - dest[(width * 1) + 3] = t; - dest[(width * 2) + 0] = t; - dest[(width * 2) + 1] = t; - dest[(width * 2) + 2] = t; - dest[(width * 2) + 3] = t; - dest[(width * 3) + 0] = t; - dest[(width * 3) + 1] = t; - dest[(width * 3) + 2] = t; - dest[(width * 3) + 3] = t; - dest += scale; - } - - dest += width * (scale - 1); - } -} - - -#define write_8(offset) \ - dest[(width * offset) + 0] = t; \ - dest[(width * offset) + 1] = t; \ - dest[(width * offset) + 2] = t; \ - dest[(width * offset) + 3] = t; \ - dest[(width * offset) + 4] = t; \ - dest[(width * offset) + 5] = t; \ - dest[(width * offset) + 6] = t; \ - dest[(width * offset) + 7] = t; - -#define write_16(offset) \ - dest[(width * offset) + 0] = t; \ - dest[(width * offset) + 1] = t; \ - dest[(width * offset) + 2] = t; \ - dest[(width * offset) + 3] = t; \ - dest[(width * offset) + 4] = t; \ - dest[(width * offset) + 5] = t; \ - dest[(width * offset) + 6] = t; \ - dest[(width * offset) + 7] = t; \ - dest[(width * offset) + 8] = t; \ - dest[(width * offset) + 9] = t; \ - dest[(width * offset) + 10] = t; \ - dest[(width * offset) + 11] = t; \ - dest[(width * offset) + 12] = t; \ - dest[(width * offset) + 13] = t; \ - dest[(width * offset) + 14] = t; \ - dest[(width * offset) + 15] = t; - -#define write_32(offset) \ - dest[(width * offset) + 0] = t; \ - dest[(width * offset) + 1] = t; \ - dest[(width * offset) + 2] = t; \ - dest[(width * offset) + 3] = t; \ - dest[(width * offset) + 4] = t; \ - dest[(width * offset) + 5] = t; \ - dest[(width * offset) + 6] = t; \ - dest[(width * offset) + 7] = t; \ - dest[(width * offset) + 8] = t; \ - dest[(width * offset) + 9] = t; \ - dest[(width * offset) + 10] = t; \ - dest[(width * offset) + 11] = t; \ - dest[(width * offset) + 12] = t; \ - dest[(width * offset) + 13] = t; \ - dest[(width * offset) + 14] = t; \ - dest[(width * offset) + 15] = t; \ - dest[(width * offset) + 16] = t; \ - dest[(width * offset) + 17] = t; \ - dest[(width * offset) + 18] = t; \ - dest[(width * offset) + 19] = t; \ - dest[(width * offset) + 20] = t; \ - dest[(width * offset) + 21] = t; \ - dest[(width * offset) + 22] = t; \ - dest[(width * offset) + 23] = t; \ - dest[(width * offset) + 24] = t; \ - dest[(width * offset) + 25] = t; \ - dest[(width * offset) + 26] = t; \ - dest[(width * offset) + 27] = t; \ - dest[(width * offset) + 28] = t; \ - dest[(width * offset) + 29] = t; \ - dest[(width * offset) + 30] = t; \ - dest[(width * offset) + 31] = t; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void scale_8x(unsigned int* dest, unsigned int* source, int width, int height, int scale) { - int x, y; - scale = 8; - for (y = 0; y < height; y += 8) { - for (x = 0; x < width; x += 8) { - const unsigned int t = *source++; - - write_8(0); - write_8(1); - write_8(2); - write_8(3); - write_8(4); - write_8(5); - write_8(6); - write_8(7); - - dest += scale; - } - - dest += width * (scale - 1); - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void scale_16x(unsigned int* dest, unsigned int* source, int width, int height, int scale) { - int x, y; - scale = 16; - for (y = 0; y < height; y += scale) { - for (x = 0; x < width; x += scale) { - const unsigned int t = *source++; - - write_16(0); - write_16(1); - write_16(2); - write_16(3); - write_16(4); - write_16(5); - write_16(6); - write_16(7); - write_16(8); - write_16(9); - write_16(10); - write_16(11); - write_16(12); - write_16(13); - write_16(14); - write_16(15); - - dest += scale; - } - - dest += width * (scale - 1); - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void scale_32x(unsigned int* dest, unsigned int* source, int width, int height, int scale) { - int x, y; - - for (y = 0; y < scale; y += scale) { - for (x = 0; x < width; x += scale) { - const unsigned int t = *source++; - - write_32(0); - write_32(1); - write_32(2); - write_32(3); - write_32(4); - write_32(5); - write_32(6); - write_32(7); - write_32(8); - write_32(9); - write_32(10); - write_32(11); - write_32(12); - write_32(13); - write_32(14); - write_32(15); - - write_32(16); - write_32(17); - write_32(18); - write_32(19); - write_32(20); - write_32(21); - write_32(22); - write_32(23); - write_32(24); - write_32(25); - write_32(26); - write_32(27); - write_32(28); - write_32(29); - write_32(30); - write_32(31); - - dest += scale; - } - - dest += width * (scale - 1); - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void mfb_update_with_buffer(void* window_info, void* buffer) -{ - WindowInfo* info = (WindowInfo*)window_info; - int width = info->width; - int height = info->height; - int scale = info->scale; - - if (info->update && buffer) { - switch (scale) { - case 1: { - memcpy(info->draw_buffer, buffer, width * height * 4); - break; - } - case 2: { - scale_2x(info->draw_buffer, buffer, width, height, scale); - break; - } - - case 4: { - scale_4x(info->draw_buffer, buffer, width, height, scale); - break; - } - - case 8: { - scale_8x(info->draw_buffer, buffer, width, height, scale); - break; - } - - case 16: { - scale_16x(info->draw_buffer, buffer, width, height, scale); - break; - } - - case 32: { - scale_32x(info->draw_buffer, buffer, width, height, scale); - break; - } - } - - XPutImage(s_display, info->window, s_gc, info->ximage, 0, 0, 0, 0, width, height); - XFlush(s_display); - } - - // clear before processing new events - - if (info->shared_data) { - info->shared_data->scroll_x = 0.0f; - info->shared_data->scroll_y = 0.0f; - } - - get_mouse_pos(info); - process_events(); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void mfb_update(void* window_info, void* buffer) -{ - mfb_update_with_buffer(window_info, 0); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void mfb_set_position(void* window, int x, int y) -{ - WindowInfo* info = (WindowInfo*)window; - XMoveWindow(s_display, info->window, x, y); - XFlush(s_display); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void mfb_close(void* window_info) -{ - WindowInfo* info = (WindowInfo*)window_info; - - if (!info->draw_buffer) - return; - - XSaveContext(s_display, info->window, s_context, (XPointer)0); - - free(info->draw_buffer); - - info->ximage->data = NULL; - info->draw_buffer = 0; - - XDestroyImage(info->ximage); - XDestroyWindow(s_display, info->window); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void mfb_set_key_callback(void* window, void* rust_data, - void (*key_callback)(void* user_data, int key, int state), - void (*char_callback)(void* user_data, uint32_t key)) - -{ - WindowInfo* win = (WindowInfo*)window; - win->key_callback = key_callback; - win->char_callback = char_callback; - win->rust_data = rust_data; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void mfb_set_shared_data(void* window, SharedData* data) -{ - WindowInfo* win = (WindowInfo*)window; - win->shared_data = data; - win->shared_data->width = win->width; - win->shared_data->height = win->height; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int mfb_should_close(void* window) { - WindowInfo* win = (WindowInfo*)window; - return !!win->update; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -unsigned int mfb_get_screen_size() { - setup_display(); - return (s_screen_width << 16) | s_screen_height; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void* mfb_get_window_handle(void* window) { - WindowInfo* win = (WindowInfo*)window; - return (void*)(uintptr_t)win->window; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// This code has been taken from glfw -// -// GLFW 3.2 X11 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would -// be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not -// be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source -// distribution. -// -//======================================================================== - -/* - * Marcus: This code was originally written by Markus G. Kuhn. - * I have made some slight changes (trimmed it down a bit from >60 KB to - * 20 KB), but the functionality is the same. - */ - -/* - * This module converts keysym values into the corresponding ISO 10646 - * (UCS, Unicode) values. - * - * The array keysymtab[] contains pairs of X11 keysym values for graphical - * characters and the corresponding Unicode value. The function - * _glfwKeySym2Unicode() maps a keysym onto a Unicode value using a binary - * search, therefore keysymtab[] must remain SORTED by keysym value. - * - * We allow to represent any UCS character in the range U-00000000 to - * U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff. - * This admittedly does not cover the entire 31-bit space of UCS, but - * it does cover all of the characters up to U-10FFFF, which can be - * represented by UTF-16, and more, and it is very unlikely that higher - * UCS codes will ever be assigned by ISO. So to get Unicode character - * U+ABCD you can directly use keysym 0x0100abcd. - * - * Original author: Markus G. Kuhn , University of - * Cambridge, April 2001 - * - * Special thanks to Richard Verhoeven for preparing - * an initial draft of the mapping table. - * - */ - - -//************************************************************************ -//**** KeySym to Unicode mapping table **** -//************************************************************************ - -static const struct codepair { - unsigned short keysym; - unsigned short ucs; -} keysymtab[] = { - { 0x01a1, 0x0104 }, - { 0x01a2, 0x02d8 }, - { 0x01a3, 0x0141 }, - { 0x01a5, 0x013d }, - { 0x01a6, 0x015a }, - { 0x01a9, 0x0160 }, - { 0x01aa, 0x015e }, - { 0x01ab, 0x0164 }, - { 0x01ac, 0x0179 }, - { 0x01ae, 0x017d }, - { 0x01af, 0x017b }, - { 0x01b1, 0x0105 }, - { 0x01b2, 0x02db }, - { 0x01b3, 0x0142 }, - { 0x01b5, 0x013e }, - { 0x01b6, 0x015b }, - { 0x01b7, 0x02c7 }, - { 0x01b9, 0x0161 }, - { 0x01ba, 0x015f }, - { 0x01bb, 0x0165 }, - { 0x01bc, 0x017a }, - { 0x01bd, 0x02dd }, - { 0x01be, 0x017e }, - { 0x01bf, 0x017c }, - { 0x01c0, 0x0154 }, - { 0x01c3, 0x0102 }, - { 0x01c5, 0x0139 }, - { 0x01c6, 0x0106 }, - { 0x01c8, 0x010c }, - { 0x01ca, 0x0118 }, - { 0x01cc, 0x011a }, - { 0x01cf, 0x010e }, - { 0x01d0, 0x0110 }, - { 0x01d1, 0x0143 }, - { 0x01d2, 0x0147 }, - { 0x01d5, 0x0150 }, - { 0x01d8, 0x0158 }, - { 0x01d9, 0x016e }, - { 0x01db, 0x0170 }, - { 0x01de, 0x0162 }, - { 0x01e0, 0x0155 }, - { 0x01e3, 0x0103 }, - { 0x01e5, 0x013a }, - { 0x01e6, 0x0107 }, - { 0x01e8, 0x010d }, - { 0x01ea, 0x0119 }, - { 0x01ec, 0x011b }, - { 0x01ef, 0x010f }, - { 0x01f0, 0x0111 }, - { 0x01f1, 0x0144 }, - { 0x01f2, 0x0148 }, - { 0x01f5, 0x0151 }, - { 0x01f8, 0x0159 }, - { 0x01f9, 0x016f }, - { 0x01fb, 0x0171 }, - { 0x01fe, 0x0163 }, - { 0x01ff, 0x02d9 }, - { 0x02a1, 0x0126 }, - { 0x02a6, 0x0124 }, - { 0x02a9, 0x0130 }, - { 0x02ab, 0x011e }, - { 0x02ac, 0x0134 }, - { 0x02b1, 0x0127 }, - { 0x02b6, 0x0125 }, - { 0x02b9, 0x0131 }, - { 0x02bb, 0x011f }, - { 0x02bc, 0x0135 }, - { 0x02c5, 0x010a }, - { 0x02c6, 0x0108 }, - { 0x02d5, 0x0120 }, - { 0x02d8, 0x011c }, - { 0x02dd, 0x016c }, - { 0x02de, 0x015c }, - { 0x02e5, 0x010b }, - { 0x02e6, 0x0109 }, - { 0x02f5, 0x0121 }, - { 0x02f8, 0x011d }, - { 0x02fd, 0x016d }, - { 0x02fe, 0x015d }, - { 0x03a2, 0x0138 }, - { 0x03a3, 0x0156 }, - { 0x03a5, 0x0128 }, - { 0x03a6, 0x013b }, - { 0x03aa, 0x0112 }, - { 0x03ab, 0x0122 }, - { 0x03ac, 0x0166 }, - { 0x03b3, 0x0157 }, - { 0x03b5, 0x0129 }, - { 0x03b6, 0x013c }, - { 0x03ba, 0x0113 }, - { 0x03bb, 0x0123 }, - { 0x03bc, 0x0167 }, - { 0x03bd, 0x014a }, - { 0x03bf, 0x014b }, - { 0x03c0, 0x0100 }, - { 0x03c7, 0x012e }, - { 0x03cc, 0x0116 }, - { 0x03cf, 0x012a }, - { 0x03d1, 0x0145 }, - { 0x03d2, 0x014c }, - { 0x03d3, 0x0136 }, - { 0x03d9, 0x0172 }, - { 0x03dd, 0x0168 }, - { 0x03de, 0x016a }, - { 0x03e0, 0x0101 }, - { 0x03e7, 0x012f }, - { 0x03ec, 0x0117 }, - { 0x03ef, 0x012b }, - { 0x03f1, 0x0146 }, - { 0x03f2, 0x014d }, - { 0x03f3, 0x0137 }, - { 0x03f9, 0x0173 }, - { 0x03fd, 0x0169 }, - { 0x03fe, 0x016b }, - { 0x047e, 0x203e }, - { 0x04a1, 0x3002 }, - { 0x04a2, 0x300c }, - { 0x04a3, 0x300d }, - { 0x04a4, 0x3001 }, - { 0x04a5, 0x30fb }, - { 0x04a6, 0x30f2 }, - { 0x04a7, 0x30a1 }, - { 0x04a8, 0x30a3 }, - { 0x04a9, 0x30a5 }, - { 0x04aa, 0x30a7 }, - { 0x04ab, 0x30a9 }, - { 0x04ac, 0x30e3 }, - { 0x04ad, 0x30e5 }, - { 0x04ae, 0x30e7 }, - { 0x04af, 0x30c3 }, - { 0x04b0, 0x30fc }, - { 0x04b1, 0x30a2 }, - { 0x04b2, 0x30a4 }, - { 0x04b3, 0x30a6 }, - { 0x04b4, 0x30a8 }, - { 0x04b5, 0x30aa }, - { 0x04b6, 0x30ab }, - { 0x04b7, 0x30ad }, - { 0x04b8, 0x30af }, - { 0x04b9, 0x30b1 }, - { 0x04ba, 0x30b3 }, - { 0x04bb, 0x30b5 }, - { 0x04bc, 0x30b7 }, - { 0x04bd, 0x30b9 }, - { 0x04be, 0x30bb }, - { 0x04bf, 0x30bd }, - { 0x04c0, 0x30bf }, - { 0x04c1, 0x30c1 }, - { 0x04c2, 0x30c4 }, - { 0x04c3, 0x30c6 }, - { 0x04c4, 0x30c8 }, - { 0x04c5, 0x30ca }, - { 0x04c6, 0x30cb }, - { 0x04c7, 0x30cc }, - { 0x04c8, 0x30cd }, - { 0x04c9, 0x30ce }, - { 0x04ca, 0x30cf }, - { 0x04cb, 0x30d2 }, - { 0x04cc, 0x30d5 }, - { 0x04cd, 0x30d8 }, - { 0x04ce, 0x30db }, - { 0x04cf, 0x30de }, - { 0x04d0, 0x30df }, - { 0x04d1, 0x30e0 }, - { 0x04d2, 0x30e1 }, - { 0x04d3, 0x30e2 }, - { 0x04d4, 0x30e4 }, - { 0x04d5, 0x30e6 }, - { 0x04d6, 0x30e8 }, - { 0x04d7, 0x30e9 }, - { 0x04d8, 0x30ea }, - { 0x04d9, 0x30eb }, - { 0x04da, 0x30ec }, - { 0x04db, 0x30ed }, - { 0x04dc, 0x30ef }, - { 0x04dd, 0x30f3 }, - { 0x04de, 0x309b }, - { 0x04df, 0x309c }, - { 0x05ac, 0x060c }, - { 0x05bb, 0x061b }, - { 0x05bf, 0x061f }, - { 0x05c1, 0x0621 }, - { 0x05c2, 0x0622 }, - { 0x05c3, 0x0623 }, - { 0x05c4, 0x0624 }, - { 0x05c5, 0x0625 }, - { 0x05c6, 0x0626 }, - { 0x05c7, 0x0627 }, - { 0x05c8, 0x0628 }, - { 0x05c9, 0x0629 }, - { 0x05ca, 0x062a }, - { 0x05cb, 0x062b }, - { 0x05cc, 0x062c }, - { 0x05cd, 0x062d }, - { 0x05ce, 0x062e }, - { 0x05cf, 0x062f }, - { 0x05d0, 0x0630 }, - { 0x05d1, 0x0631 }, - { 0x05d2, 0x0632 }, - { 0x05d3, 0x0633 }, - { 0x05d4, 0x0634 }, - { 0x05d5, 0x0635 }, - { 0x05d6, 0x0636 }, - { 0x05d7, 0x0637 }, - { 0x05d8, 0x0638 }, - { 0x05d9, 0x0639 }, - { 0x05da, 0x063a }, - { 0x05e0, 0x0640 }, - { 0x05e1, 0x0641 }, - { 0x05e2, 0x0642 }, - { 0x05e3, 0x0643 }, - { 0x05e4, 0x0644 }, - { 0x05e5, 0x0645 }, - { 0x05e6, 0x0646 }, - { 0x05e7, 0x0647 }, - { 0x05e8, 0x0648 }, - { 0x05e9, 0x0649 }, - { 0x05ea, 0x064a }, - { 0x05eb, 0x064b }, - { 0x05ec, 0x064c }, - { 0x05ed, 0x064d }, - { 0x05ee, 0x064e }, - { 0x05ef, 0x064f }, - { 0x05f0, 0x0650 }, - { 0x05f1, 0x0651 }, - { 0x05f2, 0x0652 }, - { 0x06a1, 0x0452 }, - { 0x06a2, 0x0453 }, - { 0x06a3, 0x0451 }, - { 0x06a4, 0x0454 }, - { 0x06a5, 0x0455 }, - { 0x06a6, 0x0456 }, - { 0x06a7, 0x0457 }, - { 0x06a8, 0x0458 }, - { 0x06a9, 0x0459 }, - { 0x06aa, 0x045a }, - { 0x06ab, 0x045b }, - { 0x06ac, 0x045c }, - { 0x06ae, 0x045e }, - { 0x06af, 0x045f }, - { 0x06b0, 0x2116 }, - { 0x06b1, 0x0402 }, - { 0x06b2, 0x0403 }, - { 0x06b3, 0x0401 }, - { 0x06b4, 0x0404 }, - { 0x06b5, 0x0405 }, - { 0x06b6, 0x0406 }, - { 0x06b7, 0x0407 }, - { 0x06b8, 0x0408 }, - { 0x06b9, 0x0409 }, - { 0x06ba, 0x040a }, - { 0x06bb, 0x040b }, - { 0x06bc, 0x040c }, - { 0x06be, 0x040e }, - { 0x06bf, 0x040f }, - { 0x06c0, 0x044e }, - { 0x06c1, 0x0430 }, - { 0x06c2, 0x0431 }, - { 0x06c3, 0x0446 }, - { 0x06c4, 0x0434 }, - { 0x06c5, 0x0435 }, - { 0x06c6, 0x0444 }, - { 0x06c7, 0x0433 }, - { 0x06c8, 0x0445 }, - { 0x06c9, 0x0438 }, - { 0x06ca, 0x0439 }, - { 0x06cb, 0x043a }, - { 0x06cc, 0x043b }, - { 0x06cd, 0x043c }, - { 0x06ce, 0x043d }, - { 0x06cf, 0x043e }, - { 0x06d0, 0x043f }, - { 0x06d1, 0x044f }, - { 0x06d2, 0x0440 }, - { 0x06d3, 0x0441 }, - { 0x06d4, 0x0442 }, - { 0x06d5, 0x0443 }, - { 0x06d6, 0x0436 }, - { 0x06d7, 0x0432 }, - { 0x06d8, 0x044c }, - { 0x06d9, 0x044b }, - { 0x06da, 0x0437 }, - { 0x06db, 0x0448 }, - { 0x06dc, 0x044d }, - { 0x06dd, 0x0449 }, - { 0x06de, 0x0447 }, - { 0x06df, 0x044a }, - { 0x06e0, 0x042e }, - { 0x06e1, 0x0410 }, - { 0x06e2, 0x0411 }, - { 0x06e3, 0x0426 }, - { 0x06e4, 0x0414 }, - { 0x06e5, 0x0415 }, - { 0x06e6, 0x0424 }, - { 0x06e7, 0x0413 }, - { 0x06e8, 0x0425 }, - { 0x06e9, 0x0418 }, - { 0x06ea, 0x0419 }, - { 0x06eb, 0x041a }, - { 0x06ec, 0x041b }, - { 0x06ed, 0x041c }, - { 0x06ee, 0x041d }, - { 0x06ef, 0x041e }, - { 0x06f0, 0x041f }, - { 0x06f1, 0x042f }, - { 0x06f2, 0x0420 }, - { 0x06f3, 0x0421 }, - { 0x06f4, 0x0422 }, - { 0x06f5, 0x0423 }, - { 0x06f6, 0x0416 }, - { 0x06f7, 0x0412 }, - { 0x06f8, 0x042c }, - { 0x06f9, 0x042b }, - { 0x06fa, 0x0417 }, - { 0x06fb, 0x0428 }, - { 0x06fc, 0x042d }, - { 0x06fd, 0x0429 }, - { 0x06fe, 0x0427 }, - { 0x06ff, 0x042a }, - { 0x07a1, 0x0386 }, - { 0x07a2, 0x0388 }, - { 0x07a3, 0x0389 }, - { 0x07a4, 0x038a }, - { 0x07a5, 0x03aa }, - { 0x07a7, 0x038c }, - { 0x07a8, 0x038e }, - { 0x07a9, 0x03ab }, - { 0x07ab, 0x038f }, - { 0x07ae, 0x0385 }, - { 0x07af, 0x2015 }, - { 0x07b1, 0x03ac }, - { 0x07b2, 0x03ad }, - { 0x07b3, 0x03ae }, - { 0x07b4, 0x03af }, - { 0x07b5, 0x03ca }, - { 0x07b6, 0x0390 }, - { 0x07b7, 0x03cc }, - { 0x07b8, 0x03cd }, - { 0x07b9, 0x03cb }, - { 0x07ba, 0x03b0 }, - { 0x07bb, 0x03ce }, - { 0x07c1, 0x0391 }, - { 0x07c2, 0x0392 }, - { 0x07c3, 0x0393 }, - { 0x07c4, 0x0394 }, - { 0x07c5, 0x0395 }, - { 0x07c6, 0x0396 }, - { 0x07c7, 0x0397 }, - { 0x07c8, 0x0398 }, - { 0x07c9, 0x0399 }, - { 0x07ca, 0x039a }, - { 0x07cb, 0x039b }, - { 0x07cc, 0x039c }, - { 0x07cd, 0x039d }, - { 0x07ce, 0x039e }, - { 0x07cf, 0x039f }, - { 0x07d0, 0x03a0 }, - { 0x07d1, 0x03a1 }, - { 0x07d2, 0x03a3 }, - { 0x07d4, 0x03a4 }, - { 0x07d5, 0x03a5 }, - { 0x07d6, 0x03a6 }, - { 0x07d7, 0x03a7 }, - { 0x07d8, 0x03a8 }, - { 0x07d9, 0x03a9 }, - { 0x07e1, 0x03b1 }, - { 0x07e2, 0x03b2 }, - { 0x07e3, 0x03b3 }, - { 0x07e4, 0x03b4 }, - { 0x07e5, 0x03b5 }, - { 0x07e6, 0x03b6 }, - { 0x07e7, 0x03b7 }, - { 0x07e8, 0x03b8 }, - { 0x07e9, 0x03b9 }, - { 0x07ea, 0x03ba }, - { 0x07eb, 0x03bb }, - { 0x07ec, 0x03bc }, - { 0x07ed, 0x03bd }, - { 0x07ee, 0x03be }, - { 0x07ef, 0x03bf }, - { 0x07f0, 0x03c0 }, - { 0x07f1, 0x03c1 }, - { 0x07f2, 0x03c3 }, - { 0x07f3, 0x03c2 }, - { 0x07f4, 0x03c4 }, - { 0x07f5, 0x03c5 }, - { 0x07f6, 0x03c6 }, - { 0x07f7, 0x03c7 }, - { 0x07f8, 0x03c8 }, - { 0x07f9, 0x03c9 }, - { 0x08a1, 0x23b7 }, - { 0x08a2, 0x250c }, - { 0x08a3, 0x2500 }, - { 0x08a4, 0x2320 }, - { 0x08a5, 0x2321 }, - { 0x08a6, 0x2502 }, - { 0x08a7, 0x23a1 }, - { 0x08a8, 0x23a3 }, - { 0x08a9, 0x23a4 }, - { 0x08aa, 0x23a6 }, - { 0x08ab, 0x239b }, - { 0x08ac, 0x239d }, - { 0x08ad, 0x239e }, - { 0x08ae, 0x23a0 }, - { 0x08af, 0x23a8 }, - { 0x08b0, 0x23ac }, - { 0x08bc, 0x2264 }, - { 0x08bd, 0x2260 }, - { 0x08be, 0x2265 }, - { 0x08bf, 0x222b }, - { 0x08c0, 0x2234 }, - { 0x08c1, 0x221d }, - { 0x08c2, 0x221e }, - { 0x08c5, 0x2207 }, - { 0x08c8, 0x223c }, - { 0x08c9, 0x2243 }, - { 0x08cd, 0x21d4 }, - { 0x08ce, 0x21d2 }, - { 0x08cf, 0x2261 }, - { 0x08d6, 0x221a }, - { 0x08da, 0x2282 }, - { 0x08db, 0x2283 }, - { 0x08dc, 0x2229 }, - { 0x08dd, 0x222a }, - { 0x08de, 0x2227 }, - { 0x08df, 0x2228 }, - { 0x08ef, 0x2202 }, - { 0x08f6, 0x0192 }, - { 0x08fb, 0x2190 }, - { 0x08fc, 0x2191 }, - { 0x08fd, 0x2192 }, - { 0x08fe, 0x2193 }, - { 0x09e0, 0x25c6 }, - { 0x09e1, 0x2592 }, - { 0x09e2, 0x2409 }, - { 0x09e3, 0x240c }, - { 0x09e4, 0x240d }, - { 0x09e5, 0x240a }, - { 0x09e8, 0x2424 }, - { 0x09e9, 0x240b }, - { 0x09ea, 0x2518 }, - { 0x09eb, 0x2510 }, - { 0x09ec, 0x250c }, - { 0x09ed, 0x2514 }, - { 0x09ee, 0x253c }, - { 0x09ef, 0x23ba }, - { 0x09f0, 0x23bb }, - { 0x09f1, 0x2500 }, - { 0x09f2, 0x23bc }, - { 0x09f3, 0x23bd }, - { 0x09f4, 0x251c }, - { 0x09f5, 0x2524 }, - { 0x09f6, 0x2534 }, - { 0x09f7, 0x252c }, - { 0x09f8, 0x2502 }, - { 0x0aa1, 0x2003 }, - { 0x0aa2, 0x2002 }, - { 0x0aa3, 0x2004 }, - { 0x0aa4, 0x2005 }, - { 0x0aa5, 0x2007 }, - { 0x0aa6, 0x2008 }, - { 0x0aa7, 0x2009 }, - { 0x0aa8, 0x200a }, - { 0x0aa9, 0x2014 }, - { 0x0aaa, 0x2013 }, - { 0x0aae, 0x2026 }, - { 0x0aaf, 0x2025 }, - { 0x0ab0, 0x2153 }, - { 0x0ab1, 0x2154 }, - { 0x0ab2, 0x2155 }, - { 0x0ab3, 0x2156 }, - { 0x0ab4, 0x2157 }, - { 0x0ab5, 0x2158 }, - { 0x0ab6, 0x2159 }, - { 0x0ab7, 0x215a }, - { 0x0ab8, 0x2105 }, - { 0x0abb, 0x2012 }, - { 0x0abc, 0x2329 }, - { 0x0abe, 0x232a }, - { 0x0ac3, 0x215b }, - { 0x0ac4, 0x215c }, - { 0x0ac5, 0x215d }, - { 0x0ac6, 0x215e }, - { 0x0ac9, 0x2122 }, - { 0x0aca, 0x2613 }, - { 0x0acc, 0x25c1 }, - { 0x0acd, 0x25b7 }, - { 0x0ace, 0x25cb }, - { 0x0acf, 0x25af }, - { 0x0ad0, 0x2018 }, - { 0x0ad1, 0x2019 }, - { 0x0ad2, 0x201c }, - { 0x0ad3, 0x201d }, - { 0x0ad4, 0x211e }, - { 0x0ad6, 0x2032 }, - { 0x0ad7, 0x2033 }, - { 0x0ad9, 0x271d }, - { 0x0adb, 0x25ac }, - { 0x0adc, 0x25c0 }, - { 0x0add, 0x25b6 }, - { 0x0ade, 0x25cf }, - { 0x0adf, 0x25ae }, - { 0x0ae0, 0x25e6 }, - { 0x0ae1, 0x25ab }, - { 0x0ae2, 0x25ad }, - { 0x0ae3, 0x25b3 }, - { 0x0ae4, 0x25bd }, - { 0x0ae5, 0x2606 }, - { 0x0ae6, 0x2022 }, - { 0x0ae7, 0x25aa }, - { 0x0ae8, 0x25b2 }, - { 0x0ae9, 0x25bc }, - { 0x0aea, 0x261c }, - { 0x0aeb, 0x261e }, - { 0x0aec, 0x2663 }, - { 0x0aed, 0x2666 }, - { 0x0aee, 0x2665 }, - { 0x0af0, 0x2720 }, - { 0x0af1, 0x2020 }, - { 0x0af2, 0x2021 }, - { 0x0af3, 0x2713 }, - { 0x0af4, 0x2717 }, - { 0x0af5, 0x266f }, - { 0x0af6, 0x266d }, - { 0x0af7, 0x2642 }, - { 0x0af8, 0x2640 }, - { 0x0af9, 0x260e }, - { 0x0afa, 0x2315 }, - { 0x0afb, 0x2117 }, - { 0x0afc, 0x2038 }, - { 0x0afd, 0x201a }, - { 0x0afe, 0x201e }, - { 0x0ba3, 0x003c }, - { 0x0ba6, 0x003e }, - { 0x0ba8, 0x2228 }, - { 0x0ba9, 0x2227 }, - { 0x0bc0, 0x00af }, - { 0x0bc2, 0x22a5 }, - { 0x0bc3, 0x2229 }, - { 0x0bc4, 0x230a }, - { 0x0bc6, 0x005f }, - { 0x0bca, 0x2218 }, - { 0x0bcc, 0x2395 }, - { 0x0bce, 0x22a4 }, - { 0x0bcf, 0x25cb }, - { 0x0bd3, 0x2308 }, - { 0x0bd6, 0x222a }, - { 0x0bd8, 0x2283 }, - { 0x0bda, 0x2282 }, - { 0x0bdc, 0x22a2 }, - { 0x0bfc, 0x22a3 }, - { 0x0cdf, 0x2017 }, - { 0x0ce0, 0x05d0 }, - { 0x0ce1, 0x05d1 }, - { 0x0ce2, 0x05d2 }, - { 0x0ce3, 0x05d3 }, - { 0x0ce4, 0x05d4 }, - { 0x0ce5, 0x05d5 }, - { 0x0ce6, 0x05d6 }, - { 0x0ce7, 0x05d7 }, - { 0x0ce8, 0x05d8 }, - { 0x0ce9, 0x05d9 }, - { 0x0cea, 0x05da }, - { 0x0ceb, 0x05db }, - { 0x0cec, 0x05dc }, - { 0x0ced, 0x05dd }, - { 0x0cee, 0x05de }, - { 0x0cef, 0x05df }, - { 0x0cf0, 0x05e0 }, - { 0x0cf1, 0x05e1 }, - { 0x0cf2, 0x05e2 }, - { 0x0cf3, 0x05e3 }, - { 0x0cf4, 0x05e4 }, - { 0x0cf5, 0x05e5 }, - { 0x0cf6, 0x05e6 }, - { 0x0cf7, 0x05e7 }, - { 0x0cf8, 0x05e8 }, - { 0x0cf9, 0x05e9 }, - { 0x0cfa, 0x05ea }, - { 0x0da1, 0x0e01 }, - { 0x0da2, 0x0e02 }, - { 0x0da3, 0x0e03 }, - { 0x0da4, 0x0e04 }, - { 0x0da5, 0x0e05 }, - { 0x0da6, 0x0e06 }, - { 0x0da7, 0x0e07 }, - { 0x0da8, 0x0e08 }, - { 0x0da9, 0x0e09 }, - { 0x0daa, 0x0e0a }, - { 0x0dab, 0x0e0b }, - { 0x0dac, 0x0e0c }, - { 0x0dad, 0x0e0d }, - { 0x0dae, 0x0e0e }, - { 0x0daf, 0x0e0f }, - { 0x0db0, 0x0e10 }, - { 0x0db1, 0x0e11 }, - { 0x0db2, 0x0e12 }, - { 0x0db3, 0x0e13 }, - { 0x0db4, 0x0e14 }, - { 0x0db5, 0x0e15 }, - { 0x0db6, 0x0e16 }, - { 0x0db7, 0x0e17 }, - { 0x0db8, 0x0e18 }, - { 0x0db9, 0x0e19 }, - { 0x0dba, 0x0e1a }, - { 0x0dbb, 0x0e1b }, - { 0x0dbc, 0x0e1c }, - { 0x0dbd, 0x0e1d }, - { 0x0dbe, 0x0e1e }, - { 0x0dbf, 0x0e1f }, - { 0x0dc0, 0x0e20 }, - { 0x0dc1, 0x0e21 }, - { 0x0dc2, 0x0e22 }, - { 0x0dc3, 0x0e23 }, - { 0x0dc4, 0x0e24 }, - { 0x0dc5, 0x0e25 }, - { 0x0dc6, 0x0e26 }, - { 0x0dc7, 0x0e27 }, - { 0x0dc8, 0x0e28 }, - { 0x0dc9, 0x0e29 }, - { 0x0dca, 0x0e2a }, - { 0x0dcb, 0x0e2b }, - { 0x0dcc, 0x0e2c }, - { 0x0dcd, 0x0e2d }, - { 0x0dce, 0x0e2e }, - { 0x0dcf, 0x0e2f }, - { 0x0dd0, 0x0e30 }, - { 0x0dd1, 0x0e31 }, - { 0x0dd2, 0x0e32 }, - { 0x0dd3, 0x0e33 }, - { 0x0dd4, 0x0e34 }, - { 0x0dd5, 0x0e35 }, - { 0x0dd6, 0x0e36 }, - { 0x0dd7, 0x0e37 }, - { 0x0dd8, 0x0e38 }, - { 0x0dd9, 0x0e39 }, - { 0x0dda, 0x0e3a }, - { 0x0ddf, 0x0e3f }, - { 0x0de0, 0x0e40 }, - { 0x0de1, 0x0e41 }, - { 0x0de2, 0x0e42 }, - { 0x0de3, 0x0e43 }, - { 0x0de4, 0x0e44 }, - { 0x0de5, 0x0e45 }, - { 0x0de6, 0x0e46 }, - { 0x0de7, 0x0e47 }, - { 0x0de8, 0x0e48 }, - { 0x0de9, 0x0e49 }, - { 0x0dea, 0x0e4a }, - { 0x0deb, 0x0e4b }, - { 0x0dec, 0x0e4c }, - { 0x0ded, 0x0e4d }, - { 0x0df0, 0x0e50 }, - { 0x0df1, 0x0e51 }, - { 0x0df2, 0x0e52 }, - { 0x0df3, 0x0e53 }, - { 0x0df4, 0x0e54 }, - { 0x0df5, 0x0e55 }, - { 0x0df6, 0x0e56 }, - { 0x0df7, 0x0e57 }, - { 0x0df8, 0x0e58 }, - { 0x0df9, 0x0e59 }, - { 0x0ea1, 0x3131 }, - { 0x0ea2, 0x3132 }, - { 0x0ea3, 0x3133 }, - { 0x0ea4, 0x3134 }, - { 0x0ea5, 0x3135 }, - { 0x0ea6, 0x3136 }, - { 0x0ea7, 0x3137 }, - { 0x0ea8, 0x3138 }, - { 0x0ea9, 0x3139 }, - { 0x0eaa, 0x313a }, - { 0x0eab, 0x313b }, - { 0x0eac, 0x313c }, - { 0x0ead, 0x313d }, - { 0x0eae, 0x313e }, - { 0x0eaf, 0x313f }, - { 0x0eb0, 0x3140 }, - { 0x0eb1, 0x3141 }, - { 0x0eb2, 0x3142 }, - { 0x0eb3, 0x3143 }, - { 0x0eb4, 0x3144 }, - { 0x0eb5, 0x3145 }, - { 0x0eb6, 0x3146 }, - { 0x0eb7, 0x3147 }, - { 0x0eb8, 0x3148 }, - { 0x0eb9, 0x3149 }, - { 0x0eba, 0x314a }, - { 0x0ebb, 0x314b }, - { 0x0ebc, 0x314c }, - { 0x0ebd, 0x314d }, - { 0x0ebe, 0x314e }, - { 0x0ebf, 0x314f }, - { 0x0ec0, 0x3150 }, - { 0x0ec1, 0x3151 }, - { 0x0ec2, 0x3152 }, - { 0x0ec3, 0x3153 }, - { 0x0ec4, 0x3154 }, - { 0x0ec5, 0x3155 }, - { 0x0ec6, 0x3156 }, - { 0x0ec7, 0x3157 }, - { 0x0ec8, 0x3158 }, - { 0x0ec9, 0x3159 }, - { 0x0eca, 0x315a }, - { 0x0ecb, 0x315b }, - { 0x0ecc, 0x315c }, - { 0x0ecd, 0x315d }, - { 0x0ece, 0x315e }, - { 0x0ecf, 0x315f }, - { 0x0ed0, 0x3160 }, - { 0x0ed1, 0x3161 }, - { 0x0ed2, 0x3162 }, - { 0x0ed3, 0x3163 }, - { 0x0ed4, 0x11a8 }, - { 0x0ed5, 0x11a9 }, - { 0x0ed6, 0x11aa }, - { 0x0ed7, 0x11ab }, - { 0x0ed8, 0x11ac }, - { 0x0ed9, 0x11ad }, - { 0x0eda, 0x11ae }, - { 0x0edb, 0x11af }, - { 0x0edc, 0x11b0 }, - { 0x0edd, 0x11b1 }, - { 0x0ede, 0x11b2 }, - { 0x0edf, 0x11b3 }, - { 0x0ee0, 0x11b4 }, - { 0x0ee1, 0x11b5 }, - { 0x0ee2, 0x11b6 }, - { 0x0ee3, 0x11b7 }, - { 0x0ee4, 0x11b8 }, - { 0x0ee5, 0x11b9 }, - { 0x0ee6, 0x11ba }, - { 0x0ee7, 0x11bb }, - { 0x0ee8, 0x11bc }, - { 0x0ee9, 0x11bd }, - { 0x0eea, 0x11be }, - { 0x0eeb, 0x11bf }, - { 0x0eec, 0x11c0 }, - { 0x0eed, 0x11c1 }, - { 0x0eee, 0x11c2 }, - { 0x0eef, 0x316d }, - { 0x0ef0, 0x3171 }, - { 0x0ef1, 0x3178 }, - { 0x0ef2, 0x317f }, - { 0x0ef3, 0x3181 }, - { 0x0ef4, 0x3184 }, - { 0x0ef5, 0x3186 }, - { 0x0ef6, 0x318d }, - { 0x0ef7, 0x318e }, - { 0x0ef8, 0x11eb }, - { 0x0ef9, 0x11f0 }, - { 0x0efa, 0x11f9 }, - { 0x0eff, 0x20a9 }, - { 0x13a4, 0x20ac }, - { 0x13bc, 0x0152 }, - { 0x13bd, 0x0153 }, - { 0x13be, 0x0178 }, - { 0x20ac, 0x20ac }, - // Numeric keypad with numlock on - { 0xff80 /*XKB_KEY_KP_Space*/, ' ' }, - { 0xffbd /*XKB_KEY_KP_Equal*/, '=' }, - { 0xffaa /*XKB_KEY_KP_Multiply*/, '*' }, - { 0xffab /*XKB_KEY_KP_Add*/, '+' }, - { 0xffac /*XKB_KEY_KP_Separator*/, ',' }, - { 0xffad /*XKB_KEY_KP_Subtract*/, '-' }, - { 0xffae /*XKB_KEY_KP_Decimal*/, '.' }, - { 0xffaf /*XKB_KEY_KP_Divide*/, '/' }, - { 0xffb0 /*XKB_KEY_KP_0*/, 0x0030 }, - { 0xffb1 /*XKB_KEY_KP_1*/, 0x0031 }, - { 0xffb2 /*XKB_KEY_KP_2*/, 0x0032 }, - { 0xffb3 /*XKB_KEY_KP_3*/, 0x0033 }, - { 0xffb4 /*XKB_KEY_KP_4*/, 0x0034 }, - { 0xffb5 /*XKB_KEY_KP_5*/, 0x0035 }, - { 0xffb6 /*XKB_KEY_KP_6*/, 0x0036 }, - { 0xffb7 /*XKB_KEY_KP_7*/, 0x0037 }, - { 0xffb8 /*XKB_KEY_KP_8*/, 0x0038 }, - { 0xffb9 /*XKB_KEY_KP_9*/, 0x0039 } -}; - -static long keySym2Unicode(unsigned int keysym) -{ - int min = 0; - int max = sizeof(keysymtab) / sizeof(struct codepair) - 1; - int mid; - - // First check for Latin-1 characters (1:1 mapping) - if ((keysym >= 0x0020 && keysym <= 0x007e) || - (keysym >= 0x00a0 && keysym <= 0x00ff)) - { - return keysym; - } - - // Also check for directly encoded 24-bit UCS characters - if ((keysym & 0xff000000) == 0x01000000) - return keysym & 0x00ffffff; - - // Binary search in table - while (max >= min) - { - mid = (min + max) / 2; - if (keysymtab[mid].keysym < keysym) - min = mid + 1; - else if (keysymtab[mid].keysym > keysym) - max = mid - 1; - else - return keysymtab[mid].ucs; - } - - // No matching Unicode value found - return -1; -} - - - diff --git a/src/os/unix/key_mapping.rs b/src/os/unix/key_mapping.rs new file mode 100644 index 0000000..b3c4fc0 --- /dev/null +++ b/src/os/unix/key_mapping.rs @@ -0,0 +1,914 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// This code has been taken from glfw +// +// GLFW 3.2 X11 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +/* + * Marcus: This code was originally written by Markus G. Kuhn. + * I have made some slight changes (trimmed it down a bit from >60 KB to + * 20 KB), but the functionality is the same. + */ + +/* andrewj: converted to Rust, October 2017 */ + +/* + * This module converts keysym values into the corresponding ISO 10646 + * (UCS, Unicode) values. + * + * The array keysymtab[] contains pairs of X11 keysym values for graphical + * characters and the corresponding Unicode value. The function + * _glfwKeySym2Unicode() maps a keysym onto a Unicode value using a binary + * search, therefore keysymtab[] must remain SORTED by keysym value. + * + * We allow to represent any UCS character in the range U-00000000 to + * U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff. + * This admittedly does not cover the entire 31-bit space of UCS, but + * it does cover all of the characters up to U-10FFFF, which can be + * represented by UTF-16, and more, and it is very unlikely that higher + * UCS codes will ever be assigned by ISO. So to get Unicode character + * U+ABCD you can directly use keysym 0x0100abcd. + * + * Original author: Markus G. Kuhn , University of + * Cambridge, April 2001 + * + * Special thanks to Richard Verhoeven for preparing + * an initial draft of the mapping table. + * + */ + + +//************************************************************************ +//**** KeySym to Unicode mapping table **** +//************************************************************************ + +type CodePair = (u32 /* keysym */, u32 /* ucs */); + +const LENGTH: usize = 776; + +#[allow(non_upper_case_globals)] +const keysymtab: [CodePair ; LENGTH] = [ + ( 0x01a1, 0x0104 ), + ( 0x01a2, 0x02d8 ), + ( 0x01a3, 0x0141 ), + ( 0x01a5, 0x013d ), + ( 0x01a6, 0x015a ), + ( 0x01a9, 0x0160 ), + ( 0x01aa, 0x015e ), + ( 0x01ab, 0x0164 ), + ( 0x01ac, 0x0179 ), + ( 0x01ae, 0x017d ), + ( 0x01af, 0x017b ), + ( 0x01b1, 0x0105 ), + ( 0x01b2, 0x02db ), + ( 0x01b3, 0x0142 ), + ( 0x01b5, 0x013e ), + ( 0x01b6, 0x015b ), + ( 0x01b7, 0x02c7 ), + ( 0x01b9, 0x0161 ), + ( 0x01ba, 0x015f ), + ( 0x01bb, 0x0165 ), + ( 0x01bc, 0x017a ), + ( 0x01bd, 0x02dd ), + ( 0x01be, 0x017e ), + ( 0x01bf, 0x017c ), + ( 0x01c0, 0x0154 ), + ( 0x01c3, 0x0102 ), + ( 0x01c5, 0x0139 ), + ( 0x01c6, 0x0106 ), + ( 0x01c8, 0x010c ), + ( 0x01ca, 0x0118 ), + ( 0x01cc, 0x011a ), + ( 0x01cf, 0x010e ), + ( 0x01d0, 0x0110 ), + ( 0x01d1, 0x0143 ), + ( 0x01d2, 0x0147 ), + ( 0x01d5, 0x0150 ), + ( 0x01d8, 0x0158 ), + ( 0x01d9, 0x016e ), + ( 0x01db, 0x0170 ), + ( 0x01de, 0x0162 ), + ( 0x01e0, 0x0155 ), + ( 0x01e3, 0x0103 ), + ( 0x01e5, 0x013a ), + ( 0x01e6, 0x0107 ), + ( 0x01e8, 0x010d ), + ( 0x01ea, 0x0119 ), + ( 0x01ec, 0x011b ), + ( 0x01ef, 0x010f ), + ( 0x01f0, 0x0111 ), + ( 0x01f1, 0x0144 ), + ( 0x01f2, 0x0148 ), + ( 0x01f5, 0x0151 ), + ( 0x01f8, 0x0159 ), + ( 0x01f9, 0x016f ), + ( 0x01fb, 0x0171 ), + ( 0x01fe, 0x0163 ), + ( 0x01ff, 0x02d9 ), + ( 0x02a1, 0x0126 ), + ( 0x02a6, 0x0124 ), + ( 0x02a9, 0x0130 ), + ( 0x02ab, 0x011e ), + ( 0x02ac, 0x0134 ), + ( 0x02b1, 0x0127 ), + ( 0x02b6, 0x0125 ), + ( 0x02b9, 0x0131 ), + ( 0x02bb, 0x011f ), + ( 0x02bc, 0x0135 ), + ( 0x02c5, 0x010a ), + ( 0x02c6, 0x0108 ), + ( 0x02d5, 0x0120 ), + ( 0x02d8, 0x011c ), + ( 0x02dd, 0x016c ), + ( 0x02de, 0x015c ), + ( 0x02e5, 0x010b ), + ( 0x02e6, 0x0109 ), + ( 0x02f5, 0x0121 ), + ( 0x02f8, 0x011d ), + ( 0x02fd, 0x016d ), + ( 0x02fe, 0x015d ), + ( 0x03a2, 0x0138 ), + ( 0x03a3, 0x0156 ), + ( 0x03a5, 0x0128 ), + ( 0x03a6, 0x013b ), + ( 0x03aa, 0x0112 ), + ( 0x03ab, 0x0122 ), + ( 0x03ac, 0x0166 ), + ( 0x03b3, 0x0157 ), + ( 0x03b5, 0x0129 ), + ( 0x03b6, 0x013c ), + ( 0x03ba, 0x0113 ), + ( 0x03bb, 0x0123 ), + ( 0x03bc, 0x0167 ), + ( 0x03bd, 0x014a ), + ( 0x03bf, 0x014b ), + ( 0x03c0, 0x0100 ), + ( 0x03c7, 0x012e ), + ( 0x03cc, 0x0116 ), + ( 0x03cf, 0x012a ), + ( 0x03d1, 0x0145 ), + ( 0x03d2, 0x014c ), + ( 0x03d3, 0x0136 ), + ( 0x03d9, 0x0172 ), + ( 0x03dd, 0x0168 ), + ( 0x03de, 0x016a ), + ( 0x03e0, 0x0101 ), + ( 0x03e7, 0x012f ), + ( 0x03ec, 0x0117 ), + ( 0x03ef, 0x012b ), + ( 0x03f1, 0x0146 ), + ( 0x03f2, 0x014d ), + ( 0x03f3, 0x0137 ), + ( 0x03f9, 0x0173 ), + ( 0x03fd, 0x0169 ), + ( 0x03fe, 0x016b ), + ( 0x047e, 0x203e ), + ( 0x04a1, 0x3002 ), + ( 0x04a2, 0x300c ), + ( 0x04a3, 0x300d ), + ( 0x04a4, 0x3001 ), + ( 0x04a5, 0x30fb ), + ( 0x04a6, 0x30f2 ), + ( 0x04a7, 0x30a1 ), + ( 0x04a8, 0x30a3 ), + ( 0x04a9, 0x30a5 ), + ( 0x04aa, 0x30a7 ), + ( 0x04ab, 0x30a9 ), + ( 0x04ac, 0x30e3 ), + ( 0x04ad, 0x30e5 ), + ( 0x04ae, 0x30e7 ), + ( 0x04af, 0x30c3 ), + ( 0x04b0, 0x30fc ), + ( 0x04b1, 0x30a2 ), + ( 0x04b2, 0x30a4 ), + ( 0x04b3, 0x30a6 ), + ( 0x04b4, 0x30a8 ), + ( 0x04b5, 0x30aa ), + ( 0x04b6, 0x30ab ), + ( 0x04b7, 0x30ad ), + ( 0x04b8, 0x30af ), + ( 0x04b9, 0x30b1 ), + ( 0x04ba, 0x30b3 ), + ( 0x04bb, 0x30b5 ), + ( 0x04bc, 0x30b7 ), + ( 0x04bd, 0x30b9 ), + ( 0x04be, 0x30bb ), + ( 0x04bf, 0x30bd ), + ( 0x04c0, 0x30bf ), + ( 0x04c1, 0x30c1 ), + ( 0x04c2, 0x30c4 ), + ( 0x04c3, 0x30c6 ), + ( 0x04c4, 0x30c8 ), + ( 0x04c5, 0x30ca ), + ( 0x04c6, 0x30cb ), + ( 0x04c7, 0x30cc ), + ( 0x04c8, 0x30cd ), + ( 0x04c9, 0x30ce ), + ( 0x04ca, 0x30cf ), + ( 0x04cb, 0x30d2 ), + ( 0x04cc, 0x30d5 ), + ( 0x04cd, 0x30d8 ), + ( 0x04ce, 0x30db ), + ( 0x04cf, 0x30de ), + ( 0x04d0, 0x30df ), + ( 0x04d1, 0x30e0 ), + ( 0x04d2, 0x30e1 ), + ( 0x04d3, 0x30e2 ), + ( 0x04d4, 0x30e4 ), + ( 0x04d5, 0x30e6 ), + ( 0x04d6, 0x30e8 ), + ( 0x04d7, 0x30e9 ), + ( 0x04d8, 0x30ea ), + ( 0x04d9, 0x30eb ), + ( 0x04da, 0x30ec ), + ( 0x04db, 0x30ed ), + ( 0x04dc, 0x30ef ), + ( 0x04dd, 0x30f3 ), + ( 0x04de, 0x309b ), + ( 0x04df, 0x309c ), + ( 0x05ac, 0x060c ), + ( 0x05bb, 0x061b ), + ( 0x05bf, 0x061f ), + ( 0x05c1, 0x0621 ), + ( 0x05c2, 0x0622 ), + ( 0x05c3, 0x0623 ), + ( 0x05c4, 0x0624 ), + ( 0x05c5, 0x0625 ), + ( 0x05c6, 0x0626 ), + ( 0x05c7, 0x0627 ), + ( 0x05c8, 0x0628 ), + ( 0x05c9, 0x0629 ), + ( 0x05ca, 0x062a ), + ( 0x05cb, 0x062b ), + ( 0x05cc, 0x062c ), + ( 0x05cd, 0x062d ), + ( 0x05ce, 0x062e ), + ( 0x05cf, 0x062f ), + ( 0x05d0, 0x0630 ), + ( 0x05d1, 0x0631 ), + ( 0x05d2, 0x0632 ), + ( 0x05d3, 0x0633 ), + ( 0x05d4, 0x0634 ), + ( 0x05d5, 0x0635 ), + ( 0x05d6, 0x0636 ), + ( 0x05d7, 0x0637 ), + ( 0x05d8, 0x0638 ), + ( 0x05d9, 0x0639 ), + ( 0x05da, 0x063a ), + ( 0x05e0, 0x0640 ), + ( 0x05e1, 0x0641 ), + ( 0x05e2, 0x0642 ), + ( 0x05e3, 0x0643 ), + ( 0x05e4, 0x0644 ), + ( 0x05e5, 0x0645 ), + ( 0x05e6, 0x0646 ), + ( 0x05e7, 0x0647 ), + ( 0x05e8, 0x0648 ), + ( 0x05e9, 0x0649 ), + ( 0x05ea, 0x064a ), + ( 0x05eb, 0x064b ), + ( 0x05ec, 0x064c ), + ( 0x05ed, 0x064d ), + ( 0x05ee, 0x064e ), + ( 0x05ef, 0x064f ), + ( 0x05f0, 0x0650 ), + ( 0x05f1, 0x0651 ), + ( 0x05f2, 0x0652 ), + ( 0x06a1, 0x0452 ), + ( 0x06a2, 0x0453 ), + ( 0x06a3, 0x0451 ), + ( 0x06a4, 0x0454 ), + ( 0x06a5, 0x0455 ), + ( 0x06a6, 0x0456 ), + ( 0x06a7, 0x0457 ), + ( 0x06a8, 0x0458 ), + ( 0x06a9, 0x0459 ), + ( 0x06aa, 0x045a ), + ( 0x06ab, 0x045b ), + ( 0x06ac, 0x045c ), + ( 0x06ae, 0x045e ), + ( 0x06af, 0x045f ), + ( 0x06b0, 0x2116 ), + ( 0x06b1, 0x0402 ), + ( 0x06b2, 0x0403 ), + ( 0x06b3, 0x0401 ), + ( 0x06b4, 0x0404 ), + ( 0x06b5, 0x0405 ), + ( 0x06b6, 0x0406 ), + ( 0x06b7, 0x0407 ), + ( 0x06b8, 0x0408 ), + ( 0x06b9, 0x0409 ), + ( 0x06ba, 0x040a ), + ( 0x06bb, 0x040b ), + ( 0x06bc, 0x040c ), + ( 0x06be, 0x040e ), + ( 0x06bf, 0x040f ), + ( 0x06c0, 0x044e ), + ( 0x06c1, 0x0430 ), + ( 0x06c2, 0x0431 ), + ( 0x06c3, 0x0446 ), + ( 0x06c4, 0x0434 ), + ( 0x06c5, 0x0435 ), + ( 0x06c6, 0x0444 ), + ( 0x06c7, 0x0433 ), + ( 0x06c8, 0x0445 ), + ( 0x06c9, 0x0438 ), + ( 0x06ca, 0x0439 ), + ( 0x06cb, 0x043a ), + ( 0x06cc, 0x043b ), + ( 0x06cd, 0x043c ), + ( 0x06ce, 0x043d ), + ( 0x06cf, 0x043e ), + ( 0x06d0, 0x043f ), + ( 0x06d1, 0x044f ), + ( 0x06d2, 0x0440 ), + ( 0x06d3, 0x0441 ), + ( 0x06d4, 0x0442 ), + ( 0x06d5, 0x0443 ), + ( 0x06d6, 0x0436 ), + ( 0x06d7, 0x0432 ), + ( 0x06d8, 0x044c ), + ( 0x06d9, 0x044b ), + ( 0x06da, 0x0437 ), + ( 0x06db, 0x0448 ), + ( 0x06dc, 0x044d ), + ( 0x06dd, 0x0449 ), + ( 0x06de, 0x0447 ), + ( 0x06df, 0x044a ), + ( 0x06e0, 0x042e ), + ( 0x06e1, 0x0410 ), + ( 0x06e2, 0x0411 ), + ( 0x06e3, 0x0426 ), + ( 0x06e4, 0x0414 ), + ( 0x06e5, 0x0415 ), + ( 0x06e6, 0x0424 ), + ( 0x06e7, 0x0413 ), + ( 0x06e8, 0x0425 ), + ( 0x06e9, 0x0418 ), + ( 0x06ea, 0x0419 ), + ( 0x06eb, 0x041a ), + ( 0x06ec, 0x041b ), + ( 0x06ed, 0x041c ), + ( 0x06ee, 0x041d ), + ( 0x06ef, 0x041e ), + ( 0x06f0, 0x041f ), + ( 0x06f1, 0x042f ), + ( 0x06f2, 0x0420 ), + ( 0x06f3, 0x0421 ), + ( 0x06f4, 0x0422 ), + ( 0x06f5, 0x0423 ), + ( 0x06f6, 0x0416 ), + ( 0x06f7, 0x0412 ), + ( 0x06f8, 0x042c ), + ( 0x06f9, 0x042b ), + ( 0x06fa, 0x0417 ), + ( 0x06fb, 0x0428 ), + ( 0x06fc, 0x042d ), + ( 0x06fd, 0x0429 ), + ( 0x06fe, 0x0427 ), + ( 0x06ff, 0x042a ), + ( 0x07a1, 0x0386 ), + ( 0x07a2, 0x0388 ), + ( 0x07a3, 0x0389 ), + ( 0x07a4, 0x038a ), + ( 0x07a5, 0x03aa ), + ( 0x07a7, 0x038c ), + ( 0x07a8, 0x038e ), + ( 0x07a9, 0x03ab ), + ( 0x07ab, 0x038f ), + ( 0x07ae, 0x0385 ), + ( 0x07af, 0x2015 ), + ( 0x07b1, 0x03ac ), + ( 0x07b2, 0x03ad ), + ( 0x07b3, 0x03ae ), + ( 0x07b4, 0x03af ), + ( 0x07b5, 0x03ca ), + ( 0x07b6, 0x0390 ), + ( 0x07b7, 0x03cc ), + ( 0x07b8, 0x03cd ), + ( 0x07b9, 0x03cb ), + ( 0x07ba, 0x03b0 ), + ( 0x07bb, 0x03ce ), + ( 0x07c1, 0x0391 ), + ( 0x07c2, 0x0392 ), + ( 0x07c3, 0x0393 ), + ( 0x07c4, 0x0394 ), + ( 0x07c5, 0x0395 ), + ( 0x07c6, 0x0396 ), + ( 0x07c7, 0x0397 ), + ( 0x07c8, 0x0398 ), + ( 0x07c9, 0x0399 ), + ( 0x07ca, 0x039a ), + ( 0x07cb, 0x039b ), + ( 0x07cc, 0x039c ), + ( 0x07cd, 0x039d ), + ( 0x07ce, 0x039e ), + ( 0x07cf, 0x039f ), + ( 0x07d0, 0x03a0 ), + ( 0x07d1, 0x03a1 ), + ( 0x07d2, 0x03a3 ), + ( 0x07d4, 0x03a4 ), + ( 0x07d5, 0x03a5 ), + ( 0x07d6, 0x03a6 ), + ( 0x07d7, 0x03a7 ), + ( 0x07d8, 0x03a8 ), + ( 0x07d9, 0x03a9 ), + ( 0x07e1, 0x03b1 ), + ( 0x07e2, 0x03b2 ), + ( 0x07e3, 0x03b3 ), + ( 0x07e4, 0x03b4 ), + ( 0x07e5, 0x03b5 ), + ( 0x07e6, 0x03b6 ), + ( 0x07e7, 0x03b7 ), + ( 0x07e8, 0x03b8 ), + ( 0x07e9, 0x03b9 ), + ( 0x07ea, 0x03ba ), + ( 0x07eb, 0x03bb ), + ( 0x07ec, 0x03bc ), + ( 0x07ed, 0x03bd ), + ( 0x07ee, 0x03be ), + ( 0x07ef, 0x03bf ), + ( 0x07f0, 0x03c0 ), + ( 0x07f1, 0x03c1 ), + ( 0x07f2, 0x03c3 ), + ( 0x07f3, 0x03c2 ), + ( 0x07f4, 0x03c4 ), + ( 0x07f5, 0x03c5 ), + ( 0x07f6, 0x03c6 ), + ( 0x07f7, 0x03c7 ), + ( 0x07f8, 0x03c8 ), + ( 0x07f9, 0x03c9 ), + ( 0x08a1, 0x23b7 ), + ( 0x08a2, 0x250c ), + ( 0x08a3, 0x2500 ), + ( 0x08a4, 0x2320 ), + ( 0x08a5, 0x2321 ), + ( 0x08a6, 0x2502 ), + ( 0x08a7, 0x23a1 ), + ( 0x08a8, 0x23a3 ), + ( 0x08a9, 0x23a4 ), + ( 0x08aa, 0x23a6 ), + ( 0x08ab, 0x239b ), + ( 0x08ac, 0x239d ), + ( 0x08ad, 0x239e ), + ( 0x08ae, 0x23a0 ), + ( 0x08af, 0x23a8 ), + ( 0x08b0, 0x23ac ), + ( 0x08bc, 0x2264 ), + ( 0x08bd, 0x2260 ), + ( 0x08be, 0x2265 ), + ( 0x08bf, 0x222b ), + ( 0x08c0, 0x2234 ), + ( 0x08c1, 0x221d ), + ( 0x08c2, 0x221e ), + ( 0x08c5, 0x2207 ), + ( 0x08c8, 0x223c ), + ( 0x08c9, 0x2243 ), + ( 0x08cd, 0x21d4 ), + ( 0x08ce, 0x21d2 ), + ( 0x08cf, 0x2261 ), + ( 0x08d6, 0x221a ), + ( 0x08da, 0x2282 ), + ( 0x08db, 0x2283 ), + ( 0x08dc, 0x2229 ), + ( 0x08dd, 0x222a ), + ( 0x08de, 0x2227 ), + ( 0x08df, 0x2228 ), + ( 0x08ef, 0x2202 ), + ( 0x08f6, 0x0192 ), + ( 0x08fb, 0x2190 ), + ( 0x08fc, 0x2191 ), + ( 0x08fd, 0x2192 ), + ( 0x08fe, 0x2193 ), + ( 0x09e0, 0x25c6 ), + ( 0x09e1, 0x2592 ), + ( 0x09e2, 0x2409 ), + ( 0x09e3, 0x240c ), + ( 0x09e4, 0x240d ), + ( 0x09e5, 0x240a ), + ( 0x09e8, 0x2424 ), + ( 0x09e9, 0x240b ), + ( 0x09ea, 0x2518 ), + ( 0x09eb, 0x2510 ), + ( 0x09ec, 0x250c ), + ( 0x09ed, 0x2514 ), + ( 0x09ee, 0x253c ), + ( 0x09ef, 0x23ba ), + ( 0x09f0, 0x23bb ), + ( 0x09f1, 0x2500 ), + ( 0x09f2, 0x23bc ), + ( 0x09f3, 0x23bd ), + ( 0x09f4, 0x251c ), + ( 0x09f5, 0x2524 ), + ( 0x09f6, 0x2534 ), + ( 0x09f7, 0x252c ), + ( 0x09f8, 0x2502 ), + ( 0x0aa1, 0x2003 ), + ( 0x0aa2, 0x2002 ), + ( 0x0aa3, 0x2004 ), + ( 0x0aa4, 0x2005 ), + ( 0x0aa5, 0x2007 ), + ( 0x0aa6, 0x2008 ), + ( 0x0aa7, 0x2009 ), + ( 0x0aa8, 0x200a ), + ( 0x0aa9, 0x2014 ), + ( 0x0aaa, 0x2013 ), + ( 0x0aae, 0x2026 ), + ( 0x0aaf, 0x2025 ), + ( 0x0ab0, 0x2153 ), + ( 0x0ab1, 0x2154 ), + ( 0x0ab2, 0x2155 ), + ( 0x0ab3, 0x2156 ), + ( 0x0ab4, 0x2157 ), + ( 0x0ab5, 0x2158 ), + ( 0x0ab6, 0x2159 ), + ( 0x0ab7, 0x215a ), + ( 0x0ab8, 0x2105 ), + ( 0x0abb, 0x2012 ), + ( 0x0abc, 0x2329 ), + ( 0x0abe, 0x232a ), + ( 0x0ac3, 0x215b ), + ( 0x0ac4, 0x215c ), + ( 0x0ac5, 0x215d ), + ( 0x0ac6, 0x215e ), + ( 0x0ac9, 0x2122 ), + ( 0x0aca, 0x2613 ), + ( 0x0acc, 0x25c1 ), + ( 0x0acd, 0x25b7 ), + ( 0x0ace, 0x25cb ), + ( 0x0acf, 0x25af ), + ( 0x0ad0, 0x2018 ), + ( 0x0ad1, 0x2019 ), + ( 0x0ad2, 0x201c ), + ( 0x0ad3, 0x201d ), + ( 0x0ad4, 0x211e ), + ( 0x0ad6, 0x2032 ), + ( 0x0ad7, 0x2033 ), + ( 0x0ad9, 0x271d ), + ( 0x0adb, 0x25ac ), + ( 0x0adc, 0x25c0 ), + ( 0x0add, 0x25b6 ), + ( 0x0ade, 0x25cf ), + ( 0x0adf, 0x25ae ), + ( 0x0ae0, 0x25e6 ), + ( 0x0ae1, 0x25ab ), + ( 0x0ae2, 0x25ad ), + ( 0x0ae3, 0x25b3 ), + ( 0x0ae4, 0x25bd ), + ( 0x0ae5, 0x2606 ), + ( 0x0ae6, 0x2022 ), + ( 0x0ae7, 0x25aa ), + ( 0x0ae8, 0x25b2 ), + ( 0x0ae9, 0x25bc ), + ( 0x0aea, 0x261c ), + ( 0x0aeb, 0x261e ), + ( 0x0aec, 0x2663 ), + ( 0x0aed, 0x2666 ), + ( 0x0aee, 0x2665 ), + ( 0x0af0, 0x2720 ), + ( 0x0af1, 0x2020 ), + ( 0x0af2, 0x2021 ), + ( 0x0af3, 0x2713 ), + ( 0x0af4, 0x2717 ), + ( 0x0af5, 0x266f ), + ( 0x0af6, 0x266d ), + ( 0x0af7, 0x2642 ), + ( 0x0af8, 0x2640 ), + ( 0x0af9, 0x260e ), + ( 0x0afa, 0x2315 ), + ( 0x0afb, 0x2117 ), + ( 0x0afc, 0x2038 ), + ( 0x0afd, 0x201a ), + ( 0x0afe, 0x201e ), + ( 0x0ba3, 0x003c ), + ( 0x0ba6, 0x003e ), + ( 0x0ba8, 0x2228 ), + ( 0x0ba9, 0x2227 ), + ( 0x0bc0, 0x00af ), + ( 0x0bc2, 0x22a5 ), + ( 0x0bc3, 0x2229 ), + ( 0x0bc4, 0x230a ), + ( 0x0bc6, 0x005f ), + ( 0x0bca, 0x2218 ), + ( 0x0bcc, 0x2395 ), + ( 0x0bce, 0x22a4 ), + ( 0x0bcf, 0x25cb ), + ( 0x0bd3, 0x2308 ), + ( 0x0bd6, 0x222a ), + ( 0x0bd8, 0x2283 ), + ( 0x0bda, 0x2282 ), + ( 0x0bdc, 0x22a2 ), + ( 0x0bfc, 0x22a3 ), + ( 0x0cdf, 0x2017 ), + ( 0x0ce0, 0x05d0 ), + ( 0x0ce1, 0x05d1 ), + ( 0x0ce2, 0x05d2 ), + ( 0x0ce3, 0x05d3 ), + ( 0x0ce4, 0x05d4 ), + ( 0x0ce5, 0x05d5 ), + ( 0x0ce6, 0x05d6 ), + ( 0x0ce7, 0x05d7 ), + ( 0x0ce8, 0x05d8 ), + ( 0x0ce9, 0x05d9 ), + ( 0x0cea, 0x05da ), + ( 0x0ceb, 0x05db ), + ( 0x0cec, 0x05dc ), + ( 0x0ced, 0x05dd ), + ( 0x0cee, 0x05de ), + ( 0x0cef, 0x05df ), + ( 0x0cf0, 0x05e0 ), + ( 0x0cf1, 0x05e1 ), + ( 0x0cf2, 0x05e2 ), + ( 0x0cf3, 0x05e3 ), + ( 0x0cf4, 0x05e4 ), + ( 0x0cf5, 0x05e5 ), + ( 0x0cf6, 0x05e6 ), + ( 0x0cf7, 0x05e7 ), + ( 0x0cf8, 0x05e8 ), + ( 0x0cf9, 0x05e9 ), + ( 0x0cfa, 0x05ea ), + ( 0x0da1, 0x0e01 ), + ( 0x0da2, 0x0e02 ), + ( 0x0da3, 0x0e03 ), + ( 0x0da4, 0x0e04 ), + ( 0x0da5, 0x0e05 ), + ( 0x0da6, 0x0e06 ), + ( 0x0da7, 0x0e07 ), + ( 0x0da8, 0x0e08 ), + ( 0x0da9, 0x0e09 ), + ( 0x0daa, 0x0e0a ), + ( 0x0dab, 0x0e0b ), + ( 0x0dac, 0x0e0c ), + ( 0x0dad, 0x0e0d ), + ( 0x0dae, 0x0e0e ), + ( 0x0daf, 0x0e0f ), + ( 0x0db0, 0x0e10 ), + ( 0x0db1, 0x0e11 ), + ( 0x0db2, 0x0e12 ), + ( 0x0db3, 0x0e13 ), + ( 0x0db4, 0x0e14 ), + ( 0x0db5, 0x0e15 ), + ( 0x0db6, 0x0e16 ), + ( 0x0db7, 0x0e17 ), + ( 0x0db8, 0x0e18 ), + ( 0x0db9, 0x0e19 ), + ( 0x0dba, 0x0e1a ), + ( 0x0dbb, 0x0e1b ), + ( 0x0dbc, 0x0e1c ), + ( 0x0dbd, 0x0e1d ), + ( 0x0dbe, 0x0e1e ), + ( 0x0dbf, 0x0e1f ), + ( 0x0dc0, 0x0e20 ), + ( 0x0dc1, 0x0e21 ), + ( 0x0dc2, 0x0e22 ), + ( 0x0dc3, 0x0e23 ), + ( 0x0dc4, 0x0e24 ), + ( 0x0dc5, 0x0e25 ), + ( 0x0dc6, 0x0e26 ), + ( 0x0dc7, 0x0e27 ), + ( 0x0dc8, 0x0e28 ), + ( 0x0dc9, 0x0e29 ), + ( 0x0dca, 0x0e2a ), + ( 0x0dcb, 0x0e2b ), + ( 0x0dcc, 0x0e2c ), + ( 0x0dcd, 0x0e2d ), + ( 0x0dce, 0x0e2e ), + ( 0x0dcf, 0x0e2f ), + ( 0x0dd0, 0x0e30 ), + ( 0x0dd1, 0x0e31 ), + ( 0x0dd2, 0x0e32 ), + ( 0x0dd3, 0x0e33 ), + ( 0x0dd4, 0x0e34 ), + ( 0x0dd5, 0x0e35 ), + ( 0x0dd6, 0x0e36 ), + ( 0x0dd7, 0x0e37 ), + ( 0x0dd8, 0x0e38 ), + ( 0x0dd9, 0x0e39 ), + ( 0x0dda, 0x0e3a ), + ( 0x0ddf, 0x0e3f ), + ( 0x0de0, 0x0e40 ), + ( 0x0de1, 0x0e41 ), + ( 0x0de2, 0x0e42 ), + ( 0x0de3, 0x0e43 ), + ( 0x0de4, 0x0e44 ), + ( 0x0de5, 0x0e45 ), + ( 0x0de6, 0x0e46 ), + ( 0x0de7, 0x0e47 ), + ( 0x0de8, 0x0e48 ), + ( 0x0de9, 0x0e49 ), + ( 0x0dea, 0x0e4a ), + ( 0x0deb, 0x0e4b ), + ( 0x0dec, 0x0e4c ), + ( 0x0ded, 0x0e4d ), + ( 0x0df0, 0x0e50 ), + ( 0x0df1, 0x0e51 ), + ( 0x0df2, 0x0e52 ), + ( 0x0df3, 0x0e53 ), + ( 0x0df4, 0x0e54 ), + ( 0x0df5, 0x0e55 ), + ( 0x0df6, 0x0e56 ), + ( 0x0df7, 0x0e57 ), + ( 0x0df8, 0x0e58 ), + ( 0x0df9, 0x0e59 ), + ( 0x0ea1, 0x3131 ), + ( 0x0ea2, 0x3132 ), + ( 0x0ea3, 0x3133 ), + ( 0x0ea4, 0x3134 ), + ( 0x0ea5, 0x3135 ), + ( 0x0ea6, 0x3136 ), + ( 0x0ea7, 0x3137 ), + ( 0x0ea8, 0x3138 ), + ( 0x0ea9, 0x3139 ), + ( 0x0eaa, 0x313a ), + ( 0x0eab, 0x313b ), + ( 0x0eac, 0x313c ), + ( 0x0ead, 0x313d ), + ( 0x0eae, 0x313e ), + ( 0x0eaf, 0x313f ), + ( 0x0eb0, 0x3140 ), + ( 0x0eb1, 0x3141 ), + ( 0x0eb2, 0x3142 ), + ( 0x0eb3, 0x3143 ), + ( 0x0eb4, 0x3144 ), + ( 0x0eb5, 0x3145 ), + ( 0x0eb6, 0x3146 ), + ( 0x0eb7, 0x3147 ), + ( 0x0eb8, 0x3148 ), + ( 0x0eb9, 0x3149 ), + ( 0x0eba, 0x314a ), + ( 0x0ebb, 0x314b ), + ( 0x0ebc, 0x314c ), + ( 0x0ebd, 0x314d ), + ( 0x0ebe, 0x314e ), + ( 0x0ebf, 0x314f ), + ( 0x0ec0, 0x3150 ), + ( 0x0ec1, 0x3151 ), + ( 0x0ec2, 0x3152 ), + ( 0x0ec3, 0x3153 ), + ( 0x0ec4, 0x3154 ), + ( 0x0ec5, 0x3155 ), + ( 0x0ec6, 0x3156 ), + ( 0x0ec7, 0x3157 ), + ( 0x0ec8, 0x3158 ), + ( 0x0ec9, 0x3159 ), + ( 0x0eca, 0x315a ), + ( 0x0ecb, 0x315b ), + ( 0x0ecc, 0x315c ), + ( 0x0ecd, 0x315d ), + ( 0x0ece, 0x315e ), + ( 0x0ecf, 0x315f ), + ( 0x0ed0, 0x3160 ), + ( 0x0ed1, 0x3161 ), + ( 0x0ed2, 0x3162 ), + ( 0x0ed3, 0x3163 ), + ( 0x0ed4, 0x11a8 ), + ( 0x0ed5, 0x11a9 ), + ( 0x0ed6, 0x11aa ), + ( 0x0ed7, 0x11ab ), + ( 0x0ed8, 0x11ac ), + ( 0x0ed9, 0x11ad ), + ( 0x0eda, 0x11ae ), + ( 0x0edb, 0x11af ), + ( 0x0edc, 0x11b0 ), + ( 0x0edd, 0x11b1 ), + ( 0x0ede, 0x11b2 ), + ( 0x0edf, 0x11b3 ), + ( 0x0ee0, 0x11b4 ), + ( 0x0ee1, 0x11b5 ), + ( 0x0ee2, 0x11b6 ), + ( 0x0ee3, 0x11b7 ), + ( 0x0ee4, 0x11b8 ), + ( 0x0ee5, 0x11b9 ), + ( 0x0ee6, 0x11ba ), + ( 0x0ee7, 0x11bb ), + ( 0x0ee8, 0x11bc ), + ( 0x0ee9, 0x11bd ), + ( 0x0eea, 0x11be ), + ( 0x0eeb, 0x11bf ), + ( 0x0eec, 0x11c0 ), + ( 0x0eed, 0x11c1 ), + ( 0x0eee, 0x11c2 ), + ( 0x0eef, 0x316d ), + ( 0x0ef0, 0x3171 ), + ( 0x0ef1, 0x3178 ), + ( 0x0ef2, 0x317f ), + ( 0x0ef3, 0x3181 ), + ( 0x0ef4, 0x3184 ), + ( 0x0ef5, 0x3186 ), + ( 0x0ef6, 0x318d ), + ( 0x0ef7, 0x318e ), + ( 0x0ef8, 0x11eb ), + ( 0x0ef9, 0x11f0 ), + ( 0x0efa, 0x11f9 ), + ( 0x0eff, 0x20a9 ), + ( 0x13a4, 0x20ac ), + ( 0x13bc, 0x0152 ), + ( 0x13bd, 0x0153 ), + ( 0x13be, 0x0178 ), + ( 0x20ac, 0x20ac ), + + // Numeric keypad with numlock on + ( 0xff80 /*XKB_KEY_KP_Space*/, ' ' as u32 ), + ( 0xffaa /*XKB_KEY_KP_Multiply*/, '*' as u32 ), + ( 0xffab /*XKB_KEY_KP_Add*/, '+' as u32 ), + ( 0xffac /*XKB_KEY_KP_Separator*/, ',' as u32 ), + ( 0xffad /*XKB_KEY_KP_Subtract*/, '-' as u32 ), + ( 0xffae /*XKB_KEY_KP_Decimal*/, '.' as u32 ), + ( 0xffaf /*XKB_KEY_KP_Divide*/, '/' as u32 ), + ( 0xffb0 /*XKB_KEY_KP_0*/, 0x0030 ), + ( 0xffb1 /*XKB_KEY_KP_1*/, 0x0031 ), + ( 0xffb2 /*XKB_KEY_KP_2*/, 0x0032 ), + ( 0xffb3 /*XKB_KEY_KP_3*/, 0x0033 ), + ( 0xffb4 /*XKB_KEY_KP_4*/, 0x0034 ), + ( 0xffb5 /*XKB_KEY_KP_5*/, 0x0035 ), + ( 0xffb6 /*XKB_KEY_KP_6*/, 0x0036 ), + ( 0xffb7 /*XKB_KEY_KP_7*/, 0x0037 ), + ( 0xffb8 /*XKB_KEY_KP_8*/, 0x0038 ), + ( 0xffb9 /*XKB_KEY_KP_9*/, 0x0039 ), + ( 0xffbd /*XKB_KEY_KP_Equal*/, '=' as u32 ), +]; + + +fn binary_search(value: u32, min: isize, max: isize) -> Option { + if max >= min { + let mid = (min + max) / 2; + let pair = keysymtab[mid as usize]; + + if pair.0 == value { + Some(pair.1) + } else if pair.0 < value { + binary_search(value, mid + 1, max) + } else { + binary_search(value, min, mid - 1) + } + } else { + // No matching Unicode value found + None + } +} + + +pub fn keysym_to_unicode(keysym: u32) -> Option { + // First check for Latin-1 characters (1:1 mapping) + if (keysym >= 0x0020 && keysym <= 0x007e) || + (keysym >= 0x00a0 && keysym <= 0x00ff) + { + return Some(keysym); + } + + // Also check for directly encoded 24-bit UCS characters + if (keysym & 0xff000000) == 0x01000000 { + return Some(keysym & 0x00ffffff); + } + + // Binary search in table + binary_search(keysym, 0, (LENGTH - 1) as isize) +} + + +#[allow(dead_code)] +pub fn test_it() { + assert_eq!(keysym_to_unicode('1' as u32), Some('1' as u32)); + assert_eq!(keysym_to_unicode('a' as u32), Some('a' as u32)); + + assert_eq!(keysym_to_unicode( 127), None); + assert_eq!(keysym_to_unicode( 0x123), None); + assert_eq!(keysym_to_unicode(0xfffe), None); + + // check table is sorted + for k in 0..LENGTH-1 { + assert!(keysymtab[k+1].0 > keysymtab[k].0); + } + + // check ability to find every value in the table + for i in 0..LENGTH { + let p = keysymtab[i]; + + assert_eq!(keysym_to_unicode(p.0), Some(p.1)); + } +} + diff --git a/src/os/unix/mod.rs b/src/os/unix/mod.rs index 6ce6f08..f3f793c 100644 --- a/src/os/unix/mod.rs +++ b/src/os/unix/mod.rs @@ -1,195 +1,264 @@ -#![cfg(any(target_os="linux", - target_os="freebsd", - target_os="dragonfly", - target_os="netbsd", - target_os="openbsd"))] +#![cfg(any( + target_os = "linux", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "netbsd", + target_os = "openbsd" +))] +// turn off a gazillion warnings about X keysym names +#![allow(non_upper_case_globals)] +extern crate cast; extern crate x11_dl; -use {MouseMode, MouseButton, Scale, Key, KeyRepeat, WindowOptions, InputCallback}; -use key_handler::KeyHandler; use self::x11_dl::keysym::*; +use self::x11_dl::xcursor; +use self::x11_dl::xlib; +use key_handler::KeyHandler; +use {InputCallback, Key, KeyRepeat, MouseButton, MouseMode, Scale, WindowOptions}; + use error::Error; use Result; -use {CursorStyle, MenuItem, MenuItemHandle, MenuHandle, UnixMenu, UnixMenuItem}; +use {CursorStyle, MenuHandle, MenuItem, MenuItemHandle, UnixMenu, UnixMenuItem}; -use std::os::raw::{c_void, c_char, c_uchar}; -use std::ffi::{CString}; -use std::ptr; +use std::ffi::CString; use std::mem; use std::os::raw; -use mouse_handler; -use buffer_helper; -use window_flags; +use std::os::raw::{c_char, c_uint}; +use std::ptr; -#[link(name = "X11")] -#[link(name = "Xcursor")] -extern { - fn mfb_open(name: *const c_char, width: u32, height: u32, flags: u32, scale: i32) -> *mut c_void; - fn mfb_set_title(window: *mut c_void, title: *const c_char); - fn mfb_close(window: *mut c_void); - fn mfb_update(window: *mut c_void); - fn mfb_update_with_buffer(window: *mut c_void, buffer: *const c_uchar); - fn mfb_set_position(window: *mut c_void, x: i32, y: i32); - fn mfb_set_key_callback(window: *mut c_void, target: *mut c_void, - kb: unsafe extern fn(*mut c_void, i32, i32), - cb: unsafe extern fn(*mut c_void, u32)); - fn mfb_set_shared_data(window: *mut c_void, target: *mut SharedData); - fn mfb_should_close(window: *mut c_void) -> i32; - fn mfb_get_screen_size() -> u32; - fn mfb_set_cursor_style(window: *mut c_void, cursor: u32); - fn mfb_get_window_handle(window: *mut c_void) -> *mut c_void; +use buffer_helper; +use mouse_handler; + +// NOTE: the x11-dl crate does not define Button6 or Button7 +const Button6: c_uint = xlib::Button5 + 1; +const Button7: c_uint = xlib::Button5 + 2; + +mod key_mapping; + +struct DisplayInfo { + lib: x11_dl::xlib::Xlib, + display: *mut xlib::Display, + screen: i32, + visual: *mut xlib::Visual, + gc: xlib::GC, + depth: i32, + screen_width: usize, + screen_height: usize, + context: xlib::XContext, + cursor_lib: x11_dl::xcursor::Xcursor, + cursors: [xlib::Cursor; 8], + keyb_ext: bool, + wm_delete_window: xlib::Atom, } -#[derive(Default)] -#[repr(C)] -pub struct SharedData { - pub width: u32, - pub height: u32, - pub scale: f32, - pub mouse_x: f32, - pub mouse_y: f32, - pub scroll_x: f32, - pub scroll_y: f32, - pub state: [u8; 3], +impl DisplayInfo { + fn new() -> Result { + let mut display = Self::setup()?; + + display.check_formats()?; + display.check_extensions()?; + display.init_cursors(); + display.init_atoms(); + + Ok(display) + } + + fn setup() -> Result { + unsafe { + // load the Xlib library + let lib = match xlib::Xlib::open() { + Err(_) => { + return Err(Error::WindowCreate("failed to load Xlib".to_owned())); + } + Ok(v) => v, + }; + + let cursor_lib = match xcursor::Xcursor::open() { + Err(_) => { + return Err(Error::WindowCreate("failed to load Xcursor".to_owned())); + } + Ok(v) => v, + }; + + let display = (lib.XOpenDisplay)(ptr::null()); + + if display.is_null() { + return Err(Error::WindowCreate("XOpenDisplay failed".to_owned())); + } + + let screen = (lib.XDefaultScreen)(display); + let visual = (lib.XDefaultVisual)(display, screen); + let gc = (lib.XDefaultGC)(display, screen); + let depth = (lib.XDefaultDepth)(display, screen); + + let screen_width = cast::usize((lib.XDisplayWidth)(display, screen)) + .map_err(|e| Error::WindowCreate(format!("illegal width: {}", e)))?; + let screen_height = cast::usize((lib.XDisplayHeight)(display, screen)) + .map_err(|e| Error::WindowCreate(format!("illegal height: {}", e)))?; + + // andrewj: using this instead of XUniqueContext(), as the latter + // seems to be erroneously feature guarded in the x11_dl crate. + let context = (lib.XrmUniqueQuark)(); + + Ok(DisplayInfo { + lib, + display, + screen, + visual, + gc, + depth, + screen_width, + screen_height, + context, + cursor_lib, + // the following are determined later... + cursors: [0 as xlib::Cursor; 8], + keyb_ext: false, + wm_delete_window: 0, + }) + } + } + + fn check_formats(&mut self) -> Result<()> { + // We only support 32-bit right now + + let mut conv_depth: i32 = -1; + + unsafe { + let mut count: i32 = -1; + + let formats = (self.lib.XListPixmapFormats)(self.display, &mut count); + + for i in 0..count { + let pix_fmt = *formats.offset(i as isize); + + if pix_fmt.depth == self.depth { + conv_depth = pix_fmt.bits_per_pixel; + } + } + } + + if conv_depth != 32 { + Err(Error::WindowCreate("No 32-bit format available".to_owned())) + } else { + Ok(()) + } + } + + fn check_extensions(&mut self) -> Result<()> { + // require version 1.0 + let mut major: i32 = 1; + let mut minor: i32 = 0; + + // these values are out-only, and are ignored + let mut opcode: i32 = 0; + let mut event: i32 = 0; + let mut error: i32 = 0; + + unsafe { + if (self.lib.XkbQueryExtension)( + self.display, + &mut opcode, + &mut event, + &mut error, + &mut major, + &mut minor, + ) != xlib::False + { + self.keyb_ext = true; + } + } + + Ok(()) + } + + fn init_cursors(&mut self) { + // andrewj: TODO: consider using the XCreateFontCursor() API, since + // some of these names are not working for me (they return zero). + + self.cursors[0] = self.load_cursor("arrow"); + self.cursors[1] = self.load_cursor("xterm"); + self.cursors[2] = self.load_cursor("crosshair"); + self.cursors[3] = self.load_cursor("hand2"); + self.cursors[4] = self.load_cursor("hand2"); + self.cursors[5] = self.load_cursor("sb_h_double_arrow"); + self.cursors[6] = self.load_cursor("sb_v_double_arrow"); + self.cursors[7] = self.load_cursor("diamond_cross"); + } + + fn load_cursor(&mut self, name: &'static str) -> xlib::Cursor { + let name = CString::new(name).expect("static data"); + + unsafe { + (self.cursor_lib.XcursorLibraryLoadCursor)(self.display, name.as_ptr()) + } + } + + fn init_atoms(&mut self) { + self.wm_delete_window = self.intern_atom("WM_DELETE_WINDOW", false); + } + + fn intern_atom(&mut self, name: &'static str, only_if_exists: bool) -> xlib::Atom { + let name = CString::new(name).expect("static data"); + + unsafe { + (self.lib.XInternAtom)( + self.display, + name.as_ptr(), + if only_if_exists { + xlib::True + } else { + xlib::False + }, + ) + } + } +} + +impl Drop for DisplayInfo { + fn drop(&mut self) { + unsafe { + (self.lib.XCloseDisplay)(self.display); + } + } +} + +#[derive(Clone, Copy, Eq, PartialEq)] +enum ProcessEventResult { + Ok, + Termination, } pub struct Window { - window_handle: *mut c_void, - shared_data: SharedData, + d: DisplayInfo, + + handle: xlib::Window, + ximage: *mut xlib::XImage, + draw_buffer: Vec, + + width: u32, // this is the *scaled* size + height: u32, // + + scale: i32, + + mouse_x: f32, + mouse_y: f32, + scroll_x: f32, + scroll_y: f32, + buttons: [u8; 3], + prev_cursor: CursorStyle, + + should_close: bool, // received delete window message from X server + key_handler: KeyHandler, menu_counter: MenuHandle, menus: Vec, } -#[allow(non_upper_case_globals)] -unsafe extern "C" fn key_callback(window: *mut c_void, key: i32, s: i32) { - let win: *mut Window = mem::transmute(window); - - let state = s == 1; - - match key as u32 { - XK_0 => (*win).key_handler.set_key_state(Key::Key0, state), - XK_1 => (*win).key_handler.set_key_state(Key::Key1, state), - XK_2 => (*win).key_handler.set_key_state(Key::Key2, state), - XK_3 => (*win).key_handler.set_key_state(Key::Key3, state), - XK_4 => (*win).key_handler.set_key_state(Key::Key4, state), - XK_5 => (*win).key_handler.set_key_state(Key::Key5, state), - XK_6 => (*win).key_handler.set_key_state(Key::Key6, state), - XK_7 => (*win).key_handler.set_key_state(Key::Key7, state), - XK_8 => (*win).key_handler.set_key_state(Key::Key8, state), - XK_9 => (*win).key_handler.set_key_state(Key::Key9, state), - XK_a => (*win).key_handler.set_key_state(Key::A, state), - XK_b => (*win).key_handler.set_key_state(Key::B, state), - XK_c => (*win).key_handler.set_key_state(Key::C, state), - XK_d => (*win).key_handler.set_key_state(Key::D, state), - XK_e => (*win).key_handler.set_key_state(Key::E, state), - XK_f => (*win).key_handler.set_key_state(Key::F, state), - XK_g => (*win).key_handler.set_key_state(Key::G, state), - XK_h => (*win).key_handler.set_key_state(Key::H, state), - XK_i => (*win).key_handler.set_key_state(Key::I, state), - XK_j => (*win).key_handler.set_key_state(Key::J, state), - XK_k => (*win).key_handler.set_key_state(Key::K, state), - XK_l => (*win).key_handler.set_key_state(Key::L, state), - XK_m => (*win).key_handler.set_key_state(Key::M, state), - XK_n => (*win).key_handler.set_key_state(Key::N, state), - XK_o => (*win).key_handler.set_key_state(Key::O, state), - XK_p => (*win).key_handler.set_key_state(Key::P, state), - XK_q => (*win).key_handler.set_key_state(Key::Q, state), - XK_r => (*win).key_handler.set_key_state(Key::R, state), - XK_s => (*win).key_handler.set_key_state(Key::S, state), - XK_t => (*win).key_handler.set_key_state(Key::T, state), - XK_u => (*win).key_handler.set_key_state(Key::U, state), - XK_v => (*win).key_handler.set_key_state(Key::V, state), - XK_w => (*win).key_handler.set_key_state(Key::W, state), - XK_x => (*win).key_handler.set_key_state(Key::X, state), - XK_y => (*win).key_handler.set_key_state(Key::Y, state), - XK_z => (*win).key_handler.set_key_state(Key::Z, state), - XK_F1 => (*win).key_handler.set_key_state(Key::F1, state), - XK_F2 => (*win).key_handler.set_key_state(Key::F2, state), - XK_F3 => (*win).key_handler.set_key_state(Key::F3, state), - XK_F4 => (*win).key_handler.set_key_state(Key::F4, state), - XK_F5 => (*win).key_handler.set_key_state(Key::F5, state), - XK_F6 => (*win).key_handler.set_key_state(Key::F6, state), - XK_F7 => (*win).key_handler.set_key_state(Key::F7, state), - XK_F8 => (*win).key_handler.set_key_state(Key::F8, state), - XK_F9 => (*win).key_handler.set_key_state(Key::F9, state), - XK_F10 => (*win).key_handler.set_key_state(Key::F10, state), - XK_F11 => (*win).key_handler.set_key_state(Key::F11, state), - XK_F12 => (*win).key_handler.set_key_state(Key::F12, state), - XK_Down => (*win).key_handler.set_key_state(Key::Down, state), - XK_Left => (*win).key_handler.set_key_state(Key::Left, state), - XK_Right => (*win).key_handler.set_key_state(Key::Right, state), - XK_Up => (*win).key_handler.set_key_state(Key::Up, state), - XK_Escape => (*win).key_handler.set_key_state(Key::Escape, state), - XK_apostrophe => (*win).key_handler.set_key_state(Key::Apostrophe, state), - XK_grave => (*win).key_handler.set_key_state(Key::Backquote, state), - XK_backslash => (*win).key_handler.set_key_state(Key::Backslash, state), - XK_comma => (*win).key_handler.set_key_state(Key::Comma, state), - XK_equal => (*win).key_handler.set_key_state(Key::Equal, state), - XK_bracketleft => (*win).key_handler.set_key_state(Key::LeftBracket, state), - XK_minus => (*win).key_handler.set_key_state(Key::Minus, state), - XK_period => (*win).key_handler.set_key_state(Key::Period, state), - XK_braceright => (*win).key_handler.set_key_state(Key::RightBracket, state), - XK_semicolon => (*win).key_handler.set_key_state(Key::Semicolon, state), - XK_slash => (*win).key_handler.set_key_state(Key::Slash, state), - XK_BackSpace => (*win).key_handler.set_key_state(Key::Backspace, state), - XK_Delete => (*win).key_handler.set_key_state(Key::Delete, state), - XK_End => (*win).key_handler.set_key_state(Key::End, state), - XK_Return => (*win).key_handler.set_key_state(Key::Enter, state), - XK_Home => (*win).key_handler.set_key_state(Key::Home, state), - XK_Insert => (*win).key_handler.set_key_state(Key::Insert, state), - XK_Menu => (*win).key_handler.set_key_state(Key::Menu, state), - XK_Page_Down => (*win).key_handler.set_key_state(Key::PageDown, state), - XK_Page_Up => (*win).key_handler.set_key_state(Key::PageUp, state), - XK_Pause => (*win).key_handler.set_key_state(Key::Pause, state), - XK_space => (*win).key_handler.set_key_state(Key::Space, state), - XK_Tab => (*win).key_handler.set_key_state(Key::Tab, state), - XK_Num_Lock => (*win).key_handler.set_key_state(Key::NumLock, state), - XK_Caps_Lock => (*win).key_handler.set_key_state(Key::CapsLock, state), - XK_Scroll_Lock => (*win).key_handler.set_key_state(Key::ScrollLock, state), - XK_Shift_L => (*win).key_handler.set_key_state(Key::LeftShift, state), - XK_Shift_R => (*win).key_handler.set_key_state(Key::RightShift, state), - XK_Control_L => (*win).key_handler.set_key_state(Key::LeftCtrl, state), - XK_Control_R => (*win).key_handler.set_key_state(Key::RightCtrl, state), - XK_KP_0 => (*win).key_handler.set_key_state(Key::NumPad0, state), - XK_KP_1 => (*win).key_handler.set_key_state(Key::NumPad1, state), - XK_KP_2 => (*win).key_handler.set_key_state(Key::NumPad2, state), - XK_KP_3 => (*win).key_handler.set_key_state(Key::NumPad3, state), - XK_KP_4 => (*win).key_handler.set_key_state(Key::NumPad4, state), - XK_KP_5 => (*win).key_handler.set_key_state(Key::NumPad5, state), - XK_KP_6 => (*win).key_handler.set_key_state(Key::NumPad6, state), - XK_KP_7 => (*win).key_handler.set_key_state(Key::NumPad7, state), - XK_KP_8 => (*win).key_handler.set_key_state(Key::NumPad8, state), - XK_KP_9 => (*win).key_handler.set_key_state(Key::NumPad9, state), - XK_KP_Decimal => (*win).key_handler.set_key_state(Key::NumPadDot, state), - XK_KP_Divide => (*win).key_handler.set_key_state(Key::NumPadSlash, state), - XK_KP_Multiply => (*win).key_handler.set_key_state(Key::NumPadAsterisk, state), - XK_KP_Subtract => (*win).key_handler.set_key_state(Key::NumPadMinus, state), - XK_KP_Add => (*win).key_handler.set_key_state(Key::NumPadPlus, state), - XK_KP_Enter => (*win).key_handler.set_key_state(Key::NumPadEnter, state), - XK_Super_L => (*win).key_handler.set_key_state(Key::LeftSuper, state), - XK_Super_R => (*win).key_handler.set_key_state(Key::RightSuper, state), - _ => (), - } -} - -unsafe extern "C" fn char_callback(window: *mut c_void, code_point: u32) { - let win: *mut Window = mem::transmute(window); - - // Taken from GLFW - if code_point < 32 || (code_point > 126 && code_point < 160) { - return; - } - - if let Some(ref mut callback) = (*win).key_handler.key_callback { - callback.add_char(code_point); - } -} - impl Window { pub fn new(name: &str, width: usize, height: usize, opts: WindowOptions) -> Result { - let n = match CString::new(name) { + let name = match CString::new(name) { Err(_) => { println!("Unable to convert {} to c_string", name); return Err(Error::WindowCreate("Unable to set correct name".to_owned())); @@ -197,24 +266,124 @@ impl Window { Ok(n) => n, }; - unsafe { - let scale = Self::get_scale_factor(width, height, opts.scale); - let handle = mfb_open(n.as_ptr(), - width as u32, - height as u32, - window_flags::get_flags(opts), - scale); + // FIXME: this DisplayInfo should be a singleton, hence this code + // is probably no good when using multiple windows. - if handle == ptr::null_mut() { + let d = DisplayInfo::new()?; + + let scale = Self::get_scale_factor(width, height, d.screen_width, d.screen_height, opts.scale); + + let width = width * scale; + let height = height * scale; + + unsafe { + let mut attributes: xlib::XSetWindowAttributes = mem::zeroed(); + + let root = (d.lib.XDefaultRootWindow)(d.display); + + attributes.border_pixel = (d.lib.XBlackPixel)(d.display, d.screen); + attributes.background_pixel = attributes.border_pixel; + + attributes.backing_store = xlib::NotUseful; + + let x = (d.screen_width - width) / 2; + let y = (d.screen_height - height) / 2; + + let handle = (d.lib.XCreateWindow)( + d.display, + root, + x as i32, + y as i32, + width as u32, + height as u32, + 0, /* border_width */ + d.depth, + xlib::InputOutput as u32, /* class */ + d.visual, + xlib::CWBackingStore | xlib::CWBackPixel | xlib::CWBorderPixel, + &mut attributes, + ); + + if handle == 0 { return Err(Error::WindowCreate("Unable to open Window".to_owned())); } + (d.lib.XStoreName)(d.display, handle, name.as_ptr()); + + (d.lib.XSelectInput)( + d.display, + handle, + xlib::StructureNotifyMask + | xlib::KeyPressMask + | xlib::KeyReleaseMask + | xlib::ButtonPressMask + | xlib::ButtonReleaseMask, + ); + + if opts.resize { + let mut size_hints: xlib::XSizeHints = mem::zeroed(); + + size_hints.flags = xlib::PPosition | xlib::PMinSize | xlib::PMaxSize; + size_hints.x = 0; + size_hints.y = 0; + size_hints.min_width = width as i32; + size_hints.max_width = width as i32; + size_hints.min_height = height as i32; + size_hints.max_height = height as i32; + + (d.lib.XSetWMNormalHints)( + d.display, + handle, + &mut size_hints as *mut xlib::XSizeHints, + ); + } + + (d.lib.XClearWindow)(d.display, handle); + (d.lib.XMapRaised)(d.display, handle); + (d.lib.XFlush)(d.display); + + let bytes_per_line = (width as i32) * 4; + + let ximage = (d.lib.XCreateImage)( + d.display, + d.visual, /* TODO: this was CopyFromParent in the C code */ + d.depth as u32, + xlib::ZPixmap, + 0, + ptr::null_mut(), + width as u32, + height as u32, + 32, + bytes_per_line, + ); + + if ximage == ptr::null_mut() { + (d.lib.XDestroyWindow)(d.display, handle); + return Err(Error::WindowCreate( + "Unable to create pixel buffer".to_owned(), + )); + } + + let mut draw_buffer: Vec = Vec::new(); + draw_buffer.resize(width * height, 0); + + (*ximage).data = draw_buffer[..].as_mut_ptr() as *mut c_char; + Ok(Window { - window_handle: handle, - shared_data: SharedData { - scale: scale as f32, - .. SharedData::default() - }, + d, + handle, + ximage, + draw_buffer, + width: width as u32, + height: height as u32, + scale: scale as i32, + mouse_x: 0.0, + mouse_y: 0.0, + scroll_x: 0.0, + scroll_y: 0.0, + buttons: [0, 0, 0], + prev_cursor: CursorStyle::Arrow, + should_close: false, key_handler: KeyHandler::new(), menu_counter: MenuHandle(0), menus: Vec::new(), @@ -223,38 +392,29 @@ impl Window { } pub fn set_title(&mut self, title: &str) { - unsafe { - let t = CString::new(title).unwrap(); - mfb_set_title(self.window_handle, t.as_ptr()); - } - } + match CString::new(title) { + Err(_) => { + println!("Unable to convert {} to c_string", title); + return; + } - unsafe fn set_shared_data(&mut self) { - mfb_set_shared_data(self.window_handle, &mut self.shared_data); + Ok(t) => unsafe { + (self.d.lib.XStoreName)(self.d.display, self.handle, t.as_ptr()); + }, + }; } pub fn update_with_buffer(&mut self, buffer: &[u32]) -> Result<()> { - self.key_handler.update(); + buffer_helper::check_buffer_size( + self.width as usize, + self.height as usize, + self.scale as usize, + buffer, + )?; - unsafe { - Self::set_shared_data(self); - } + unsafe { self.raw_blit_buffer(buffer) }; - let check_res = buffer_helper::check_buffer_size(self.shared_data.width as usize, - self.shared_data.height as usize, - self.shared_data.scale as usize, - buffer); - if check_res.is_err() { - return check_res; - } - - unsafe { - mfb_update_with_buffer(self.window_handle, buffer.as_ptr() as *const u8); - mfb_set_key_callback(self.window_handle, - mem::transmute(self), - key_callback, - char_callback); - } + self.update(); Ok(()) } @@ -262,58 +422,60 @@ impl Window { pub fn update(&mut self) { self.key_handler.update(); + // clear before processing new events + self.scroll_x = 0.0; + self.scroll_y = 0.0; + unsafe { - Self::set_shared_data(self); - mfb_update(self.window_handle); - mfb_set_key_callback(self.window_handle, - mem::transmute(self), - key_callback, - char_callback); + self.raw_get_mouse_pos(); + self.raw_process_events(); } } #[inline] pub fn get_window_handle(&self) -> *mut raw::c_void { - unsafe { mfb_get_window_handle(self.window_handle) as *mut raw::c_void } + unsafe { mem::transmute(self.handle as usize) } } #[inline] pub fn set_position(&mut self, x: isize, y: isize) { - unsafe { mfb_set_position(self.window_handle, x as i32, y as i32) } + unsafe { + (self.d.lib.XMoveWindow)(self.d.display, self.handle, x as i32, y as i32); + (self.d.lib.XFlush)(self.d.display); + } } #[inline] pub fn get_size(&self) -> (usize, usize) { - (self.shared_data.width as usize, self.shared_data.height as usize) + (self.width as usize, self.height as usize) } pub fn get_mouse_pos(&self, mode: MouseMode) -> Option<(f32, f32)> { - let s = self.shared_data.scale as f32; - let w = self.shared_data.width as f32; - let h = self.shared_data.height as f32; + let s = self.scale as f32; + let w = self.width as f32; + let h = self.height as f32; - mouse_handler::get_pos(mode, self.shared_data.mouse_x, self.shared_data.mouse_y, s, w, h) + mouse_handler::get_pos(mode, self.mouse_x, self.mouse_y, s, w, h) } pub fn get_unscaled_mouse_pos(&self, mode: MouseMode) -> Option<(f32, f32)> { - let w = self.shared_data.width as f32; - let h = self.shared_data.height as f32; + let w = self.width as f32; + let h = self.height as f32; - mouse_handler::get_pos(mode, self.shared_data.mouse_x, self.shared_data.mouse_y, 1.0, w, h) + mouse_handler::get_pos(mode, self.mouse_x, self.mouse_y, 1.0, w, h) } pub fn get_mouse_down(&self, button: MouseButton) -> bool { - match button { - MouseButton::Left => self.shared_data.state[0] > 0, - MouseButton::Middle => self.shared_data.state[1] > 0, - MouseButton::Right => self.shared_data.state[2] > 0, - } + match button { + MouseButton::Left => self.buttons[0] > 0, + MouseButton::Middle => self.buttons[1] > 0, + MouseButton::Right => self.buttons[2] > 0, + } } pub fn get_scroll_wheel(&self) -> Option<(f32, f32)> { - if self.shared_data.scroll_x.abs() > 0.0 || - self.shared_data.scroll_y.abs() > 0.0 { - Some((self.shared_data.scroll_x, self.shared_data.scroll_y)) + if self.scroll_x.abs() > 0.0 || self.scroll_y.abs() > 0.0 { + Some((self.scroll_x, self.scroll_y)) } else { None } @@ -321,8 +483,16 @@ impl Window { #[inline] pub fn set_cursor_style(&mut self, cursor: CursorStyle) { - unsafe { - mfb_set_cursor_style(self.window_handle, cursor as u32); + if self.prev_cursor != cursor { + unsafe { + (self.d.lib.XDefineCursor)( + self.d.display, + self.handle, + self.d.cursors[cursor as usize], + ); + } + + self.prev_cursor = cursor; } } @@ -362,13 +532,13 @@ impl Window { } #[inline] - pub fn set_input_callback(&mut self, callback: Box) { + pub fn set_input_callback(&mut self, callback: Box) { self.key_handler.set_input_callback(callback) } #[inline] pub fn is_open(&self) -> bool { - unsafe { mfb_should_close(self.window_handle) == 1 } + !self.should_close } #[inline] @@ -377,8 +547,8 @@ impl Window { true } - unsafe fn get_scale_factor(width: usize, height: usize, scale: Scale) -> i32 { - let factor: i32 = match scale { + fn get_scale_factor(width: usize, height: usize, screen_width: usize, screen_height: usize, scale: Scale) -> usize { + match scale { Scale::X1 => 1, Scale::X2 => 2, Scale::X4 => 4, @@ -386,19 +556,13 @@ impl Window { Scale::X16 => 16, Scale::X32 => 32, Scale::FitScreen => { - let wh: u32 = mfb_get_screen_size(); - let screen_x = (wh >> 16) as i32; - let screen_y = (wh & 0xffff) as i32; - - println!("{} - {}", screen_x, screen_y); - - let mut scale = 1i32; + let mut scale = 1; loop { - let w = width as i32 * (scale + 1); - let h = height as i32 * (scale + 1); + let w = width * (scale + 1); + let h = height * (scale + 1); - if w >= screen_x || h >= screen_y { + if w >= screen_width || h >= screen_height { break; } @@ -406,24 +570,22 @@ impl Window { } if scale >= 32 { - 32 + 32 } else { - scale - } + scale + } } - }; - - return factor; + } } fn next_menu_handle(&mut self) -> MenuHandle { - let handle = self.menu_counter; - self.menu_counter.0 += 1; - handle + let handle = self.menu_counter; + self.menu_counter.0 += 1; + handle } pub fn add_menu(&mut self, menu: &Menu) -> MenuHandle { - let handle = self.next_menu_handle(); + let handle = self.next_menu_handle(); let mut menu = menu.internal.clone(); menu.handle = handle; self.menus.push(menu); @@ -441,68 +603,459 @@ impl Window { pub fn is_menu_pressed(&mut self) -> Option { None } -} -pub struct Menu { - pub internal: UnixMenu, -} + //////////////////////////////////// -impl Menu { - pub fn new(name: &str) -> Result { - Ok(Menu { - internal: UnixMenu { - handle: MenuHandle(0), - item_counter: MenuItemHandle(0), - name: name.to_owned(), - items: Vec::new(), - } - }) + unsafe fn raw_blit_buffer(&mut self, buffer: &[u32]) { + match self.scale { + 1 => { + // input buffer may be larger than necessary, so get a slice of correct size + let src_buf = &buffer[0..self.draw_buffer.len()]; + + self.draw_buffer[..].copy_from_slice(src_buf); + } + + 2 => { + self.scale_2x(buffer); + } + + 4 => { + self.scale_4x(buffer); + } + + _ => { + panic!("bad scale for raw_blit_buffer()"); + } + } + + (self.d.lib.XPutImage)( + self.d.display, + self.handle, + self.d.gc, + self.ximage, + 0, + 0, + 0, + 0, + self.width, + self.height, + ); + (self.d.lib.XFlush)(self.d.display); } - pub fn add_sub_menu(&mut self, name: &str, sub_menu: &Menu) { - let handle = self.next_item_handle(); - self.internal.items.push(UnixMenuItem { - label: name.to_owned(), - handle: handle, - sub_menu: Some(Box::new(sub_menu.internal.clone())), - id: 0, - enabled: true, - key: Key::Unknown, - modifier: 0, - }); + unsafe fn scale_2x(&mut self, buffer: &[u32]) { + let w = self.width as usize; + + let bw = (self.width as usize) / 2; + let bh = (self.height as usize) / 2; + + for y in 0..bh { + let src = &buffer[y * bw..y * bw + bw]; + + for dy in 0..2 { + let dest = &mut self.draw_buffer[(y * 2 + dy) * w..(y * 2 + dy) * w + w]; + + for x in 0..bw { + dest[x * 2] = src[x]; + dest[x * 2 + 1] = src[x]; + } + } + } } - fn next_item_handle(&mut self) -> MenuItemHandle { - let handle = self.internal.item_counter; - self.internal.item_counter.0 += 1; - handle + unsafe fn scale_4x(&mut self, buffer: &[u32]) { + let w = self.width as usize; + + let bw = (self.width as usize) / 4; + let bh = (self.height as usize) / 4; + + for y in 0..bh { + let src = &buffer[y * bw..y * bw + bw]; + + for dy in 0..4 { + let dest = &mut self.draw_buffer[(y * 4 + dy) * w..(y * 4 + dy) * w + w]; + + for x in 0..bw { + dest[x * 4] = src[x]; + dest[x * 4 + 1] = src[x]; + dest[x * 4 + 2] = src[x]; + dest[x * 4 + 3] = src[x]; + } + } + } } - pub fn add_menu_item(&mut self, item: &MenuItem) -> MenuItemHandle { - let item_handle = self.next_item_handle(); - self.internal.items.push(UnixMenuItem { - sub_menu: None, - handle: self.internal.item_counter, - id: item.id, - label: item.label.clone(), - enabled: item.enabled, - key: item.key, - modifier: item.modifier, - }); - item_handle + unsafe fn raw_get_mouse_pos(&mut self) { + let mut root: xlib::Window = 0; + let mut root_x: i32 = 0; + let mut root_y: i32 = 0; + + let mut child: xlib::Window = 0; + let mut child_x: i32 = 0; + let mut child_y: i32 = 0; + + let mut mask: u32 = 0; + + if (self.d.lib.XQueryPointer)( + self.d.display, + self.handle, + &mut root, + &mut child, + &mut root_x, + &mut root_y, + &mut child_x, + &mut child_y, + &mut mask, + ) != xlib::False + { + self.mouse_x = child_x as f32; + self.mouse_y = child_y as f32; + } } - pub fn remove_item(&mut self, handle: &MenuItemHandle) { - self.internal.items.retain(|ref item| item.handle.0 != handle.0); + unsafe fn raw_process_events(&mut self) { + let count = (self.d.lib.XPending)(self.d.display); + + for _ in 0..count { + let mut event: xlib::XEvent = mem::zeroed(); + + (self.d.lib.XNextEvent)(self.d.display, &mut event); + + // Don't process any more messages if we hit a termination event + if self.raw_process_one_event(event) == ProcessEventResult::Termination { + return; + } + } + } + + unsafe fn raw_process_one_event(&mut self, ev: xlib::XEvent) -> ProcessEventResult { + // FIXME: we cannot handle multiple windows here! + if ev.any.window != self.handle { + return ProcessEventResult::Ok; + } + + match ev.type_ { + xlib::ClientMessage => { + // TODO : check for message_type == wm_protocols, as per x11-rs example + if ev.client_message.format == 32 /* i.e. longs */ && + ev.client_message.data.get_long(0) as xlib::Atom == self.d.wm_delete_window + { + self.should_close = true; + return ProcessEventResult::Termination; + } + } + + xlib::KeyPress => { + self.process_key(ev, true /* is_down */); + } + + xlib::KeyRelease => { + self.process_key(ev, false /* is_down */); + } + + xlib::ButtonPress => { + self.process_button(ev, true /* is_down */); + } + + xlib::ButtonRelease => { + self.process_button(ev, false /* is_down */); + } + + xlib::ConfigureNotify => { + // TODO : pass this onto the application + self.width = ev.configure.width as u32; + self.height = ev.configure.height as u32; + } + + _ => {} + } + + ProcessEventResult::Ok + } + + fn process_key(&mut self, mut ev: xlib::XEvent, is_down: bool) { + // NOTE: need "mut" on ev due to dumbness in the X API + + // handle special keys... + + if self.d.keyb_ext { + let sym: xlib::KeySym = unsafe { + (self.d.lib.XkbKeycodeToKeysym)( + self.d.display, + ev.key.keycode as xlib::KeyCode, + 0, /* group */ + 1, /* level */ + ) + }; + + match sym as u32 { + XK_KP_0 | XK_KP_1 | XK_KP_2 | XK_KP_3 | XK_KP_4 | XK_KP_5 | XK_KP_6 | XK_KP_7 + | XK_KP_8 | XK_KP_9 | XK_KP_Separator | XK_KP_Decimal | XK_KP_Equal + | XK_KP_Enter => { + self.update_key_state(sym, is_down); + return; + } + + _ => {} + } + } + + // normal keys... + + let sym: xlib::KeySym = unsafe { + (self.d.lib.XLookupKeysym)(&mut ev.key, 0 /* index */) + }; + + if sym == xlib::NoSymbol as xlib::KeySym { + return; + } + + self.update_key_state(sym, is_down); + + // unicode callback... + + if !is_down { + return; + } + + if let Some(code_point) = key_mapping::keysym_to_unicode(sym as u32) { + // Taken from GLFW + if code_point < 32 || (code_point > 126 && code_point < 160) { + return; + } + + if let Some(ref mut callback) = self.key_handler.key_callback { + callback.add_char(code_point); + } + } + } + + unsafe fn process_button(&mut self, ev: xlib::XEvent, is_down: bool) { + match ev.button.button { + xlib::Button1 => { + self.buttons[0] = if is_down { 1 } else { 0 }; + return; + } + xlib::Button2 => { + self.buttons[1] = if is_down { 1 } else { 0 }; + return; + } + xlib::Button3 => { + self.buttons[2] = if is_down { 1 } else { 0 }; + return; + } + + _ => {} + } + + // in X, the mouse wheel is usually mapped to Button4/5 + + let scroll: (i32, i32) = match ev.button.button { + xlib::Button4 => (0, 10), + xlib::Button5 => (0, -10), + + Button6 => (10, 0), + Button7 => (-10, 0), + + _ => { + return; + } + }; + + self.scroll_x += scroll.0 as f32 * 0.1; + self.scroll_y += scroll.1 as f32 * 0.1; + } + + fn update_key_state(&mut self, sym: xlib::KeySym, is_down: bool) { + if sym > u32::max_value() as xlib::KeySym { + return; + } + + let key = match sym as u32 { + XK_0 => Key::Key0, + XK_1 => Key::Key1, + XK_2 => Key::Key2, + XK_3 => Key::Key3, + XK_4 => Key::Key4, + XK_5 => Key::Key5, + XK_6 => Key::Key6, + XK_7 => Key::Key7, + XK_8 => Key::Key8, + XK_9 => Key::Key9, + + XK_a => Key::A, + XK_b => Key::B, + XK_c => Key::C, + XK_d => Key::D, + XK_e => Key::E, + XK_f => Key::F, + XK_g => Key::G, + XK_h => Key::H, + XK_i => Key::I, + XK_j => Key::J, + XK_k => Key::K, + XK_l => Key::L, + XK_m => Key::M, + XK_n => Key::N, + XK_o => Key::O, + XK_p => Key::P, + XK_q => Key::Q, + XK_r => Key::R, + XK_s => Key::S, + XK_t => Key::T, + XK_u => Key::U, + XK_v => Key::V, + XK_w => Key::W, + XK_x => Key::X, + XK_y => Key::Y, + XK_z => Key::Z, + + XK_apostrophe => Key::Apostrophe, + XK_grave => Key::Backquote, + XK_backslash => Key::Backslash, + XK_comma => Key::Comma, + XK_equal => Key::Equal, + XK_bracketleft => Key::LeftBracket, + XK_minus => Key::Minus, + XK_period => Key::Period, + XK_braceright => Key::RightBracket, + XK_semicolon => Key::Semicolon, + XK_slash => Key::Slash, + XK_space => Key::Space, + + XK_F1 => Key::F1, + XK_F2 => Key::F2, + XK_F3 => Key::F3, + XK_F4 => Key::F4, + XK_F5 => Key::F5, + XK_F6 => Key::F6, + XK_F7 => Key::F7, + XK_F8 => Key::F8, + XK_F9 => Key::F9, + XK_F10 => Key::F10, + XK_F11 => Key::F11, + XK_F12 => Key::F12, + + XK_Down => Key::Down, + XK_Left => Key::Left, + XK_Right => Key::Right, + XK_Up => Key::Up, + XK_Escape => Key::Escape, + XK_BackSpace => Key::Backspace, + XK_Delete => Key::Delete, + XK_End => Key::End, + XK_Return => Key::Enter, + XK_Home => Key::Home, + XK_Insert => Key::Insert, + XK_Menu => Key::Menu, + XK_Page_Down => Key::PageDown, + XK_Page_Up => Key::PageUp, + XK_Pause => Key::Pause, + XK_Tab => Key::Tab, + XK_Num_Lock => Key::NumLock, + XK_Caps_Lock => Key::CapsLock, + XK_Scroll_Lock => Key::ScrollLock, + XK_Shift_L => Key::LeftShift, + XK_Shift_R => Key::RightShift, + XK_Control_L => Key::LeftCtrl, + XK_Control_R => Key::RightCtrl, + XK_Super_L => Key::LeftSuper, + XK_Super_R => Key::RightSuper, + + XK_KP_0 => Key::NumPad0, + XK_KP_1 => Key::NumPad1, + XK_KP_2 => Key::NumPad2, + XK_KP_3 => Key::NumPad3, + XK_KP_4 => Key::NumPad4, + XK_KP_5 => Key::NumPad5, + XK_KP_6 => Key::NumPad6, + XK_KP_7 => Key::NumPad7, + XK_KP_8 => Key::NumPad8, + XK_KP_9 => Key::NumPad9, + XK_KP_Decimal => Key::NumPadDot, + XK_KP_Divide => Key::NumPadSlash, + XK_KP_Multiply => Key::NumPadAsterisk, + XK_KP_Subtract => Key::NumPadMinus, + XK_KP_Add => Key::NumPadPlus, + XK_KP_Enter => Key::NumPadEnter, + + _ => { + // ignore other keys + return; + } + }; + + self.key_handler.set_key_state(key, is_down); } } - impl Drop for Window { fn drop(&mut self) { unsafe { - mfb_close(self.window_handle); + (*self.ximage).data = ptr::null_mut(); + + // TODO [ andrewj: right now DisplayInfo is not shared, so doing this is + // probably pointless ] + // XSaveContext(s_display, info->window, s_context, (XPointer)0); + + (self.d.lib.XDestroyImage)(self.ximage); + (self.d.lib.XDestroyWindow)(self.d.display, self.handle); } } } +pub struct Menu { + pub internal: UnixMenu, +} + +impl Menu { + pub fn new(name: &str) -> Result { + Ok(Menu { + internal: UnixMenu { + handle: MenuHandle(0), + item_counter: MenuItemHandle(0), + name: name.to_owned(), + items: Vec::new(), + }, + }) + } + + pub fn add_sub_menu(&mut self, name: &str, sub_menu: &Menu) { + let handle = self.next_item_handle(); + self.internal.items.push(UnixMenuItem { + label: name.to_owned(), + handle: handle, + sub_menu: Some(Box::new(sub_menu.internal.clone())), + id: 0, + enabled: true, + key: Key::Unknown, + modifier: 0, + }); + } + + fn next_item_handle(&mut self) -> MenuItemHandle { + let handle = self.internal.item_counter; + self.internal.item_counter.0 += 1; + handle + } + + pub fn add_menu_item(&mut self, item: &MenuItem) -> MenuItemHandle { + let item_handle = self.next_item_handle(); + self.internal.items.push(UnixMenuItem { + sub_menu: None, + handle: self.internal.item_counter, + id: item.id, + label: item.label.clone(), + enabled: item.enabled, + key: item.key, + modifier: item.modifier, + }); + item_handle + } + + pub fn remove_item(&mut self, handle: &MenuItemHandle) { + self.internal + .items + .retain(|ref item| item.handle.0 != handle.0); + } +}