diff --git a/src/native/x11/X11MiniFB.c b/src/native/x11/X11MiniFB.c index 2cb8abb..333b7d7 100644 --- a/src/native/x11/X11MiniFB.c +++ b/src/native/x11/X11MiniFB.c @@ -10,13 +10,13 @@ #define KEY_FUNCTION 0xFF #define KEY_ESC 0x1B +void mfb_close(void* window_info); + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int s_window_count = 0; static Display* s_display; static int s_screen; -static int s_width; -static int s_height; static GC s_gc; static int s_depth; static int s_setup_done = 0; @@ -24,6 +24,7 @@ static Visual* s_visual; static int s_screen_width; static int s_screen_height; static XContext s_context; +static Atom s_wm_delete_window; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -36,6 +37,7 @@ typedef struct WindowInfo { int scale; int width; int height; + int update; } WindowInfo; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -84,6 +86,9 @@ static int setup_display() { 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; printf("setup done\n"); @@ -105,6 +110,9 @@ void* mfb_open(const char* title, int width, int height, int scale) return 0; } + width *= scale; + height *= scale; + Window defaultRootWindow = DefaultRootWindow(s_display); windowAttributes.border_pixel = BlackPixel(s_display, s_screen); @@ -154,6 +162,9 @@ void* mfb_open(const char* title, int width, int height, int 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); @@ -179,13 +190,22 @@ static WindowInfo* find_handle(Window handle) /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -static void process_event(XEvent* event) { +static int process_event(XEvent* event) { KeySym sym; WindowInfo* info = find_handle(event->xany.window); if (!info) - return; + 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; + } + } if ((event->type == KeyPress) || (event->type == KeyRelease) && info->key_callback) { int sym = XLookupKeysym(&event->xkey, 0); @@ -196,6 +216,8 @@ static void process_event(XEvent* event) { info->key_callback(info->rust_data, sym, 0); } } + + return 1; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -212,7 +234,10 @@ static int process_events() { XEvent event; XNextEvent(s_display, &event); - process_event(&event); + + // Don't process any more messages if event is 0 + if (process_event(&event) == 0) + return 0; } return 0; @@ -220,16 +245,79 @@ static int process_events() /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +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); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void mfb_update(void* window_info, void* buffer) { WindowInfo* info = (WindowInfo*)window_info; int width = info->width; int height = info->height; + int scale = info->scale; - memcpy(info->draw_buffer, buffer, width * height * 4); + if (info->update) { + 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; + } - XPutImage(s_display, info->window, s_gc, info->ximage, 0, 0, 0, 0, width, height); - XFlush(s_display); + case 4: { + scale_4x(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); + } process_events(); } @@ -240,18 +328,18 @@ 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); - - s_window_count--; - - // Only close display when there are no windows left - - if (s_window_count == 0) { - XCloseDisplay(s_display); - } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -263,3 +351,17 @@ void mfb_set_key_callback(void* window, void* rust_data, void (*key_callback)(vo win->rust_data = rust_data; } +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +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; +} + diff --git a/src/os/unix/mod.rs b/src/os/unix/mod.rs index eadbd69..87ea6b6 100644 --- a/src/os/unix/mod.rs +++ b/src/os/unix/mod.rs @@ -17,8 +17,8 @@ extern { 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; + fn mfb_should_close(window: *mut c_void) -> i32; + fn mfb_get_screen_size() -> u32; } pub struct Window { @@ -85,6 +85,7 @@ unsafe extern "C" fn key_callback(window: *mut c_void, key: i32, s: i32) { 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), @@ -100,7 +101,6 @@ unsafe extern "C" fn key_callback(window: *mut c_void, key: i32, s: i32) { 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_Escape => (*win).key_handler.set_key_state(Key::Escape, 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), @@ -203,20 +203,44 @@ impl Window { #[inline] pub fn is_open(&self) -> bool { - true - //unsafe { mfb_should_close(self.window_handle) == 0 } + unsafe { mfb_should_close(self.window_handle) == 1 } } - unsafe fn get_scale_factor(_: usize, _: usize, scale: Scale) -> i32 { + unsafe fn get_scale_factor(width: usize, height: 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 + 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; + + loop { + let w = width as i32 * (scale + 1); + let h = height as i32 * (scale + 1); + + if w > screen_x || h > screen_y { + break; + } + + scale *= 2; + } + + if scale >= 4 { + 4 + } else { + scale + } + } + + _ => { + println!("Scale above 4 not support currently, defaults to 4"); + 4 } };