diff --git a/src/lib.rs b/src/lib.rs index d38eaef..4fd858d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -167,6 +167,8 @@ mod key_handler; use self::os::macos as imp; #[cfg(target_os = "windows")] use self::os::windows as imp; +#[cfg(unix)] +use self::os::unix as imp; /// /// Window used for displaying a 32-bit RGB buffer. Here is a small example on how to use it: diff --git a/src/native/x11/X11MiniFB.c b/src/native/x11/X11MiniFB.c index e55face..015876b 100644 --- a/src/native/x11/X11MiniFB.c +++ b/src/native/x11/X11MiniFB.c @@ -1,5 +1,9 @@ +#include #include #include +#include +#include +#include /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -8,39 +12,55 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +static int s_window_count = 0; static Display* s_display; static int s_screen; static int s_width; static int s_height; -static Window s_window; static GC s_gc; -static XImage *s_ximage; +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 XContext s_context; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -int mfb_open(const char* title, int width, int height) -{ +typedef struct WindowInfo { + Window window; + XImage* ximage; + void* draw_buffer; + int scale; + int width; + int height; +} WindowInfo; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static int setup_display() { int depth, i, formatCount, convDepth = -1; XPixmapFormatValues* formats; - XSetWindowAttributes windowAttributes; - XSizeHints sizeHints; - Visual* visual; + + if (s_setup_done) { + return 1; + } s_display = XOpenDisplay(0); - if (!s_display) - return -1; - + if (!s_display) { + printf("Unable to open X11 display\n"); + return 0; + } + + s_context = XUniqueContext(); s_screen = DefaultScreen(s_display); - visual = DefaultVisual(s_display, s_screen); + s_visual = DefaultVisual(s_display, s_screen); formats = XListPixmapFormats(s_display, &formatCount); depth = DefaultDepth(s_display, s_screen); - Window defaultRootWindow = DefaultRootWindow(s_display); - for (i = 0; i < formatCount; ++i) - { - if (depth == formats[i].depth) - { + for (i = 0; i < formatCount; ++i) { + if (depth == formats[i].depth) { convDepth = formats[i].bits_per_pixel; break; } @@ -49,28 +69,57 @@ int mfb_open(const char* title, int width, int height) XFree(formats); // We only support 32-bit right now - if (convDepth != 32) - { + if (convDepth != 32) { + printf("Unable to find 32-bit format for X11 display\n"); XCloseDisplay(s_display); - return -1; + return 0; } - int screenWidth = DisplayWidth(s_display, s_screen); - int screenHeight = DisplayHeight(s_display, s_screen); + 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); + + s_setup_done = 1; + + printf("setup done\n"); + + return 1; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void* mfb_open(const char* title, int width, int height, int scale) +{ + XSetWindowAttributes windowAttributes; + XSizeHints sizeHints; + XImage* image; + Window window; + WindowInfo* window_info; + + if (!setup_display()) { + return 0; + } + + 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; - s_window = XCreateWindow(s_display, defaultRootWindow, (screenWidth - width) / 2, - (screenHeight - height) / 2, width, height, 0, depth, InputOutput, - visual, CWBackPixel | CWBorderPixel | CWBackingStore, + 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 (!s_window) + if (!window) { + printf("Unable to create X11 Window\n"); return 0; + } - XSelectInput(s_display, s_window, KeyPressMask | KeyReleaseMask); - XStoreName(s_display, s_window, title); + //XSelectInput(s_display, s_window, KeyPressMask | KeyReleaseMask); + XStoreName(s_display, window, title); sizeHints.flags = PPosition | PMinSize | PMaxSize; sizeHints.x = 0; @@ -80,68 +129,104 @@ int mfb_open(const char* title, int width, int height) sizeHints.min_height = height; sizeHints.max_height = height; - XSetWMNormalHints(s_display, s_window, &sizeHints); - XClearWindow(s_display, s_window); - XMapRaised(s_display, s_window); + XSetWMNormalHints(s_display, window, &sizeHints); + XClearWindow(s_display, window); + XMapRaised(s_display, window); XFlush(s_display); - s_gc = DefaultGC(s_display, s_screen); + image = XCreateImage(s_display, CopyFromParent, s_depth, ZPixmap, 0, NULL, width, height, 32, width * 4); - s_ximage = XCreateImage(s_display, CopyFromParent, depth, ZPixmap, 0, NULL, width, height, 32, width * 4); + if (!image) { + XDestroyWindow(s_display, window); + printf("Unable to create XImage\n"); + return 0; + } - s_width = width; - s_height = height; + window_info = (WindowInfo*)malloc(sizeof(WindowInfo)); + 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); - return 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; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -static int processEvents() +static void process_event(XEvent* event) { + KeySym sym; + + if (event->type != KeyPress) + return; + + sym = XLookupKeysym(&event->xkey, 0); + + if ((sym >> 8) != KEY_FUNCTION) + return; + + if ((sym & 0xFF) == KEY_ESC) + return; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static int process_events() { + int count; XEvent event; KeySym sym; - if (!XPending(s_display)) - return; - - XNextEvent(s_display, &event); - - if (event.type != KeyPress) - return 0; - - sym = XLookupKeysym(&event.xkey, 0); - - if ((sym >> 8) != KEY_FUNCTION) - return 0; - - if ((sym & 0xFF) == KEY_ESC) - return -1; + count = XPending(s_display); + while (count--) + { + XEvent event; + XNextEvent(s_display, &event); + process_event(&event); + } return 0; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -int mfb_update(void* buffer) +void mfb_update(void* window_info, void* buffer) { - s_ximage->data = (char*)buffer; + WindowInfo* info = (WindowInfo*)window_info; + int width = info->width; + int height = info->height; - XPutImage(s_display, s_window, s_gc, s_ximage, 0, 0, 0, 0, s_width, s_height); + memcpy(info->draw_buffer, buffer, width * height * 4); + + XPutImage(s_display, info->window, s_gc, info->ximage, 0, 0, 0, 0, width, height); XFlush(s_display); - if (processEvents() < 0) - return -1; - - return 0; + process_events(); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void mfb_close (void) +void mfb_close(void* window_info) { - s_ximage->data = NULL; - XDestroyImage(s_ximage); - XDestroyWindow(s_display, s_window); - XCloseDisplay(s_display); + WindowInfo* info = (WindowInfo*)window_info; + + info->ximage->data = NULL; + + XDestroyImage(info->ximage); + XDestroyWindow(s_display, info->window); + + s_window_count--; + + // Only close display when there are no windows left + + if (s_window_count == 0) { + XCloseDisplay(s_display); + } } diff --git a/src/os/mod.rs b/src/os/mod.rs index 0669925..aaacd16 100644 --- a/src/os/mod.rs +++ b/src/os/mod.rs @@ -2,3 +2,5 @@ pub mod macos; #[cfg(target_os = "windows")] pub mod windows; +#[cfg(unix)] +pub mod unix; diff --git a/src/os/unix/mod.rs b/src/os/unix/mod.rs new file mode 100644 index 0000000..a651782 --- /dev/null +++ b/src/os/unix/mod.rs @@ -0,0 +1,119 @@ +#![cfg(unix)] + +use {Scale, Key, KeyRepeat}; +use key_handler::KeyHandler; + +use libc::{c_void, c_char, c_uchar}; +use std::ffi::{CString}; +use std::ptr; +//use std::mem; + +#[link(name = "X11")] +extern { + fn mfb_open(name: *const c_char, width: u32, height: u32, scale: i32) -> *mut c_void; + fn mfb_close(window: *mut c_void); + fn mfb_update(window: *mut c_void, buffer: *const c_uchar); + //fn mfb_set_key_callback(window: *mut c_void, target: *mut c_void, cb: unsafe extern fn(*mut c_void, i32, i32)); + //fn mfb_should_close(window: *mut c_void) -> i32; + //fn mfb_get_screen_size() -> u32; +} + +pub struct Window { + window_handle: *mut c_void, + key_handler: KeyHandler, +} + +impl Window { + pub fn new(name: &str, width: usize, height: usize, scale: Scale) -> Result { + let n = match CString::new(name) { + Err(_) => { + println!("Unable to convert {} to c_string", name); + return Err("Unable to set correct name"); + } + Ok(n) => n, + }; + + unsafe { + let handle = mfb_open(n.as_ptr(), width as u32, height as u32, Self::get_scale_factor(width, height, scale)); + + if handle == ptr::null_mut() { + return Err("Unable to open Window"); + } + + Ok(Window { + window_handle: handle, + key_handler: KeyHandler::new(), + }) + } + } + + pub fn update(&mut self, buffer: &[u32]) { + self.key_handler.update(); + + unsafe { + mfb_update(self.window_handle, buffer.as_ptr() as *const u8); + //mfb_set_key_callback(self.window_handle, mem::transmute(self), key_callback); + } + } + + #[inline] + pub fn get_keys(&self) -> Option> { + self.key_handler.get_keys() + } + + #[inline] + pub fn get_keys_pressed(&self, repeat: KeyRepeat) -> Option> { + self.key_handler.get_keys_pressed(repeat) + } + + #[inline] + pub fn is_key_down(&self, key: Key) -> bool { + self.key_handler.is_key_down(key) + } + + #[inline] + pub fn set_key_repeat_delay(&mut self, delay: f32) { + self.key_handler.set_key_repeat_delay(delay) + } + + #[inline] + pub fn set_key_repeat_rate(&mut self, rate: f32) { + self.key_handler.set_key_repeat_rate(rate) + } + + #[inline] + pub fn is_key_pressed(&self, key: Key, repeat: KeyRepeat) -> bool { + self.key_handler.is_key_pressed(key, repeat) + } + + #[inline] + pub fn is_open(&self) -> bool { + true + //unsafe { mfb_should_close(self.window_handle) == 0 } + } + + unsafe fn get_scale_factor(_: usize, _: usize, scale: Scale) -> i32 { + let factor: i32 = match scale { + Scale::X1 => 1, + Scale::X2 => 2, + Scale::X4 => 4, + Scale::X8 => 8, + Scale::X16 => 16, + Scale::X32 => 32, + Scale::FitScreen => { + 1 + } + }; + + return factor; + } +} + +impl Drop for Window { + fn drop(&mut self) { + unsafe { + mfb_close(self.window_handle); + } + } +} +