From 8b6148cf97de3df22060144f64e812f64e4dc71d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Aragon=C3=A9s?= <9335325+Darky-Lucera@users.noreply.github.com> Date: Sat, 8 Jun 2019 20:11:02 +0200 Subject: [PATCH] Add input events (#18) * MacOSX with input events and mfb_set_viewport * Windows with input events and mfb_set_viewport * Minor changes in macosx and windows * X11 with input and mfb_set_viewport * Minor changes on X11 * Fix bug in X11 resize event * Fix bug in windows resize * added scale image to X11 * Added stretch_image with & without bilinear interpolation * moved MiniFB_internal.h * Added wayland events * minor changes * unify a bit the window structs * More work on wayland * Added cmake file * minor fix on windows * modified test * Fix on wayland. Unset wayland as default for linux * Use stdbool instead of our own macro eBool Remove prefix e from all enums * Remove _ex sufix in common files * merge X11MiniFB_ex.c into X11MiniFB.c * Merge WaylandMiniFB_ex.c into WaylandMiniFB.c * Add user_data to callbacks * Merge WinMiniFB_ex.c into WinMiniFB.c * Some minor changes on Windows * Fix bug on Wayland * Added mfb_get_key_name * keyboard_default on all platforms --- .gitignore | 6 + CMakeLists.txt | 75 +++ include/MiniFB.h | 90 ++- include/MiniFB_cpp.h | 124 ++++ include/MiniFB_enums.h | 163 ++++++ src/MiniFB_common.c | 421 ++++++++++++++ src/MiniFB_cpp.cpp | 38 ++ src/MiniFB_internal.c | 75 +++ src/MiniFB_internal.h | 28 + src/macosx/MacMiniFB.m | 769 ++++++++++++++++--------- src/macosx/OSXWindow.h | 26 +- src/macosx/OSXWindow.m | 431 +++++++++----- src/macosx/OSXWindowData.h | 22 + src/macosx/OSXWindowFrameView.h | 74 +-- src/macosx/OSXWindowFrameView.m | 474 +++++++++++----- src/wayland/WaylandMiniFB.c | 972 ++++++++++++++++++++++++-------- src/wayland/WaylandWindowData.h | 57 ++ src/windows/WinMiniFB.c | 617 ++++++++++++++++---- src/windows/WinWindowData.h | 30 + src/x11/X11MiniFB.c | 780 ++++++++++++++++++++----- src/x11/X11WindowData.h | 34 ++ tests/noise.c | 169 +++++- 22 files changed, 4322 insertions(+), 1153 deletions(-) create mode 100644 CMakeLists.txt create mode 100755 include/MiniFB_cpp.h create mode 100755 include/MiniFB_enums.h create mode 100755 src/MiniFB_common.c create mode 100755 src/MiniFB_cpp.cpp create mode 100644 src/MiniFB_internal.c create mode 100755 src/MiniFB_internal.h create mode 100644 src/macosx/OSXWindowData.h create mode 100644 src/wayland/WaylandWindowData.h create mode 100644 src/windows/WinWindowData.h create mode 100644 src/x11/X11WindowData.h diff --git a/.gitignore b/.gitignore index 9379cf7..0836cf7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,8 @@ .tundra2.* t2-output +/.vscode +/win +/macosx +/linux +/build +/.history diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b286d7a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,75 @@ +cmake_minimum_required(VERSION 2.8.9) +project (noise) + +include_directories(include src) + +file(GLOB SrcLib "src/*.c" + "src/*.cpp" + "include/*.h") +file(GLOB SrcWindows "src/windows/*.c" + "src/windows/*.h") +file(GLOB SrcMacOSX "src/macosx/*.c" + "src/macosx/*.cpp" + "src/macosx/*.m" + "src/macosx/*.mm" + "src/macosx/*.h") +file(GLOB SrcWayland "src/wayland/*.c") +file(GLOB SrcX11 "src/x11/*.c") + +if (NOT MSVC) + set (CMAKE_C_FLAGS "-g -Wall -Wextra -Wno-switch -Wno-unused-function") + set (CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++11") + set (CMAKE_OBJC_FLAGS "${CMAKE_C_FLAGS}") + set (CMAKE_OBJCXX_FLAGS "${CMAKE_CXX_FLAGS}") +endif() + +if (APPLE) + OPTION(USE_METAL_API "Build the project using metal API code" ON) +elseif (UNIX) + OPTION(USE_WAYLAND_API "Build the project using wayland API code" OFF) +endif() + +if (MSVC) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + add_definitions(-D_WIN32_WINNT=0x0600) + list (APPEND SrcLib ${SrcWindows}) +elseif (MINGW) + add_definitions(-D_WIN32_WINNT=0x0600) + list(APPEND SrcLib ${SrcWindows}) +elseif (APPLE) + if(USE_METAL_API) + add_definitions(-DUSE_METAL_API) + endif() + list(APPEND SrcLib ${SrcMacOSX}) +elseif (UNIX) + if(USE_WAYLAND_API) + list(APPEND SrcLib ${SrcWayland}) + else() + list(APPEND SrcLib ${SrcX11}) + endif() +endif() + +add_library(minifb STATIC + ${SrcLib} +) + +add_executable(noise + tests/noise.c +) + +target_link_libraries(noise minifb) + +if (MSVC) +elseif (MINGW) +elseif (APPLE) + target_link_libraries(noise "-framework Cocoa") + target_link_libraries(noise "-framework QuartzCore") + target_link_libraries(noise "-framework Metal") + target_link_libraries(noise "-framework MetalKit") +elseif (UNIX) + if(USE_WAYLAND_API) + target_link_libraries(noise -lwayland-client -lwayland-cursor) + else() + target_link_libraries(noise -lX11) + endif() +endif() diff --git a/include/MiniFB.h b/include/MiniFB.h index eb1a34e..6f302b0 100644 --- a/include/MiniFB.h +++ b/include/MiniFB.h @@ -1,30 +1,60 @@ -#ifndef _MINIFB_H_ -#define _MINIFB_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -#define MFB_RGB(r, g, b) (((unsigned int)r) << 16) | (((unsigned int)g) << 8) | b - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// Create a window that is used to display the buffer sent into the mfb_update function, returns 0 if fails -int mfb_open(const char* name, int width, int height); - -// Update the display. Input buffer is assumed to be a 32-bit buffer of the size given in the open call -// Will return -1 when ESC key is pressed (later on will return keycode and -1 on other close signal) -int mfb_update(void* buffer); - -// Close the window -void mfb_close(); - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -#ifdef __cplusplus -} -#endif - -#endif +#ifndef _MINIFB_H_ +#define _MINIFB_H_ + +#include "MiniFB_enums.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define MFB_RGB(r, g, b) (((unsigned int)r) << 16) | (((unsigned int)g) << 8) | b + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Create a window that is used to display the buffer sent into the mfb_update function, returns 0 if fails +int mfb_open(const char* name, int width, int height); +int mfb_open_ex(const char* name, int width, int height, int flags); + +// Update the display. Input buffer is assumed to be a 32-bit buffer of the size given in the open call +// Will return -1 when ESC key is pressed (later on will return keycode and -1 on other close signal) +int mfb_update(void* buffer); + +// Close the window +void mfb_close(); + +// Set user data +void mfb_set_user_data(void *user_data); + +// Set viewport (useful when resize) +bool mfb_set_viewport(unsigned offset_x, unsigned offset_y, unsigned width, unsigned height); + +// Event callbacks +typedef void(*mfb_active_func)(void *user_data, bool isActive); +typedef void(*mfb_resize_func)(void *user_data, int width, int height); +typedef void(*mfb_keyboard_func)(void *user_data, Key key, KeyMod mod, bool isPressed); +typedef void(*mfb_char_input_func)(void *user_data, unsigned int code); +typedef void(*mfb_mouse_btn_func)(void *user_data, MouseButton button, KeyMod mod, bool isPressed); +typedef void(*mfb_mouse_move_func)(void *user_data, int x, int y); +typedef void(*mfb_mouse_scroll_func)(void *user_data, KeyMod mod, float deltaX, float deltaY); + +void mfb_active_callback(mfb_active_func callback); +void mfb_resize_callback(mfb_resize_func callback); +void mfb_keyboard_callback(mfb_keyboard_func callback); +void mfb_char_input_callback(mfb_char_input_func callback); +void mfb_mouse_button_callback(mfb_mouse_btn_func callback); +void mfb_mouse_move_callback(mfb_mouse_move_func callback); +void mfb_mouse_scroll_callback(mfb_mouse_scroll_func callback); + +const char *mfb_get_key_name(Key key); + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +} + +#include "MiniFB_cpp.h" +#endif + +#endif diff --git a/include/MiniFB_cpp.h b/include/MiniFB_cpp.h new file mode 100755 index 0000000..f665a0b --- /dev/null +++ b/include/MiniFB_cpp.h @@ -0,0 +1,124 @@ +#pragma once + +#if defined(__cplusplus) + +#include +#include "MiniFB.h" + +template +void mfb_active_callback(T *obj, void (T::*method)(void *, bool)); + +template +void mfb_resize_callback(T *obj, void (T::*method)(void *, int, int)); + +template +void mfb_keyboard_callback(T *obj, void (T::*method)(void *, Key, KeyMod, bool)); + +template +void mfb_char_input_callback(T *obj, void (T::*method)(void *, unsigned int)); + +template +void mfb_mouse_button_callback(T *obj, void (T::*method)(void *, MouseButton, KeyMod, bool)); + +template +void mfb_mouse_move_callback(T *obj, void (T::*method)(void *, int, int)); + +template +void mfb_mouse_scroll_callback(T *obj, void (T::*method)(void *, KeyMod, float, float)); + +//------------------------------------- +// To avoid clumsy hands +//------------------------------------- +class Stub { + template + friend void mfb_active_callback(T *obj, void (T::*method)(void *, bool)); + template + friend void mfb_resize_callback(T *obj, void (T::*method)(void *, int, int)); + template + friend void mfb_mouse_button_callback(T *obj, void (T::*method)(void *, MouseButton, KeyMod, bool)); + template + friend void mfb_keyboard_callback(T *obj, void (T::*method)(void *, Key, KeyMod, bool)); + template + friend void mfb_char_input_callback(T *obj, void (T::*method)(void *, unsigned int)); + template + friend void mfb_mouse_button_callback(T *obj, void (T::*method)(void *, MouseButton, KeyMod, bool)); + template + friend void mfb_mouse_move_callback(T *obj, void (T::*method)(void *, int, int)); + template + friend void mfb_mouse_scroll_callback(T *obj, void (T::*method)(void *, KeyMod, float, float)); + + static void active_stub(void *user_data, bool isActive); + static void resize_stub(void *user_data, int width, int height); + static void keyboard_stub(void *user_data, Key key, KeyMod mod, bool isPressed); + static void char_input_stub(void *user_data, unsigned int); + static void mouse_btn_stub(void *user_data, MouseButton button, KeyMod mod, bool isPressed); + static void mouse_move_stub(void *user_data, int x, int y); + static void scroll_stub(void *user_data, KeyMod mod, float deltaX, float deltaY); + + static std::function m_active; + static std::function m_resize; + static std::function m_keyboard; + static std::function m_char_input; + static std::function m_mouse_btn; + static std::function m_mouse_move; + static std::function m_scroll; +}; + +//------------------------------------- +template +inline void mfb_active_callback(T *obj, void (T::*method)(void *user_data, bool)) { + using namespace std::placeholders; + + Stub::m_active = std::bind(method, obj, _1, _2); + mfb_active_callback(Stub::active_stub); +} + +template +inline void mfb_resize_callback(T *obj, void (T::*method)(void *user_data, int, int)) { + using namespace std::placeholders; + + Stub::m_resize = std::bind(method, obj, _1, _2, _3); + mfb_resize_callback(Stub::resize_stub); +} + +template +inline void mfb_keyboard_callback(T *obj, void (T::*method)(void *user_data, Key, KeyMod, bool)) { + using namespace std::placeholders; + + Stub::m_keyboard = std::bind(method, obj, _1, _2, _3, _4); + mfb_keyboard_callback(Stub::keyboard_stub); +} + +template +inline void mfb_char_input_callback(T *obj, void (T::*method)(void *user_data, unsigned int)) { + using namespace std::placeholders; + + Stub::m_char_input = std::bind(method, obj, _1, _2); + mfb_char_input_callback(Stub::char_input_stub); +} + +template +inline void mfb_mouse_button_callback(T *obj, void (T::*method)(void *user_data, MouseButton, KeyMod, bool)) { + using namespace std::placeholders; + + Stub::m_mouse_btn = std::bind(method, obj, _1, _2, _3, _4); + mfb_mouse_button_callback(Stub::mouse_btn_stub); +} + +template +inline void mfb_mouse_move_callback(T *obj, void (T::*method)(void *user_data, int, int)) { + using namespace std::placeholders; + + Stub::m_mouse_move = std::bind(method, obj, _1, _2, _3); + mfb_mouse_move_callback(Stub::mouse_move_stub); +} + +template +inline void mfb_mouse_scroll_callback(T *obj, void (T::*method)(void *user_data, KeyMod, float, float)) { + using namespace std::placeholders; + + Stub::m_scroll = std::bind(method, obj, _1, _2, _3, _4); + mfb_mouse_scroll_callback(Stub::scroll_stub); +} + +#endif diff --git a/include/MiniFB_enums.h b/include/MiniFB_enums.h new file mode 100755 index 0000000..cc7b6fd --- /dev/null +++ b/include/MiniFB_enums.h @@ -0,0 +1,163 @@ +#pragma once + +#include + +typedef enum { + MOUSE_BTN_0, // No mouse button + MOUSE_BTN_1, + MOUSE_BTN_2, + MOUSE_BTN_3, + MOUSE_BTN_4, + MOUSE_BTN_5, + MOUSE_BTN_6, + MOUSE_BTN_7, + MOUSE_BTN_8 +} MouseButton; +#define MOUSE_LAST MOUSE_BTN_8 +#define MOUSE_LEFT MOUSE_BTN_0 +#define MOUSE_RIGHT MOUSE_BTN_1 +#define MOUSE_MIDDLE MOUSE_BTN_2 + +typedef enum { + KB_KEY_UNKNOWN = -1, + + KB_KEY_SPACE = 32, + KB_KEY_APOSTROPHE = 39, + KB_KEY_COMMA = 44, + KB_KEY_MINUS = 45, + KB_KEY_PERIOD = 46, + KB_KEY_SLASH = 47, + KB_KEY_0 = 48, + KB_KEY_1 = 49, + KB_KEY_2 = 50, + KB_KEY_3 = 51, + KB_KEY_4 = 52, + KB_KEY_5 = 53, + KB_KEY_6 = 54, + KB_KEY_7 = 55, + KB_KEY_8 = 56, + KB_KEY_9 = 57, + KB_KEY_SEMICOLON = 59, + KB_KEY_EQUAL = 61, + KB_KEY_A = 65, + KB_KEY_B = 66, + KB_KEY_C = 67, + KB_KEY_D = 68, + KB_KEY_E = 69, + KB_KEY_F = 70, + KB_KEY_G = 71, + KB_KEY_H = 72, + KB_KEY_I = 73, + KB_KEY_J = 74, + KB_KEY_K = 75, + KB_KEY_L = 76, + KB_KEY_M = 77, + KB_KEY_N = 78, + KB_KEY_O = 79, + KB_KEY_P = 80, + KB_KEY_Q = 81, + KB_KEY_R = 82, + KB_KEY_S = 83, + KB_KEY_T = 84, + KB_KEY_U = 85, + KB_KEY_V = 86, + KB_KEY_W = 87, + KB_KEY_X = 88, + KB_KEY_Y = 89, + KB_KEY_Z = 90, + KB_KEY_LEFT_BRACKET = 91, + KB_KEY_BACKSLASH = 92, + KB_KEY_RIGHT_BRACKET = 93, + KB_KEY_GRAVE_ACCENT = 96, + KB_KEY_WORLD_1 = 161, + KB_KEY_WORLD_2 = 162, + + KB_KEY_ESCAPE = 256, + KB_KEY_ENTER = 257, + KB_KEY_TAB = 258, + KB_KEY_BACKSPACE = 259, + KB_KEY_INSERT = 260, + KB_KEY_DELETE = 261, + KB_KEY_RIGHT = 262, + KB_KEY_LEFT = 263, + KB_KEY_DOWN = 264, + KB_KEY_UP = 265, + KB_KEY_PAGE_UP = 266, + KB_KEY_PAGE_DOWN = 267, + KB_KEY_HOME = 268, + KB_KEY_END = 269, + KB_KEY_CAPS_LOCK = 280, + KB_KEY_SCROLL_LOCK = 281, + KB_KEY_NUM_LOCK = 282, + KB_KEY_PRINT_SCREEN = 283, + KB_KEY_PAUSE = 284, + KB_KEY_F1 = 290, + KB_KEY_F2 = 291, + KB_KEY_F3 = 292, + KB_KEY_F4 = 293, + KB_KEY_F5 = 294, + KB_KEY_F6 = 295, + KB_KEY_F7 = 296, + KB_KEY_F8 = 297, + KB_KEY_F9 = 298, + KB_KEY_F10 = 299, + KB_KEY_F11 = 300, + KB_KEY_F12 = 301, + KB_KEY_F13 = 302, + KB_KEY_F14 = 303, + KB_KEY_F15 = 304, + KB_KEY_F16 = 305, + KB_KEY_F17 = 306, + KB_KEY_F18 = 307, + KB_KEY_F19 = 308, + KB_KEY_F20 = 309, + KB_KEY_F21 = 310, + KB_KEY_F22 = 311, + KB_KEY_F23 = 312, + KB_KEY_F24 = 313, + KB_KEY_F25 = 314, + KB_KEY_KP_0 = 320, + KB_KEY_KP_1 = 321, + KB_KEY_KP_2 = 322, + KB_KEY_KP_3 = 323, + KB_KEY_KP_4 = 324, + KB_KEY_KP_5 = 325, + KB_KEY_KP_6 = 326, + KB_KEY_KP_7 = 327, + KB_KEY_KP_8 = 328, + KB_KEY_KP_9 = 329, + KB_KEY_KP_DECIMAL = 330, + KB_KEY_KP_DIVIDE = 331, + KB_KEY_KP_MULTIPLY = 332, + KB_KEY_KP_SUBTRACT = 333, + KB_KEY_KP_ADD = 334, + KB_KEY_KP_ENTER = 335, + KB_KEY_KP_EQUAL = 336, + KB_KEY_LEFT_SHIFT = 340, + KB_KEY_LEFT_CONTROL = 341, + KB_KEY_LEFT_ALT = 342, + KB_KEY_LEFT_SUPER = 343, + KB_KEY_RIGHT_SHIFT = 344, + KB_KEY_RIGHT_CONTROL = 345, + KB_KEY_RIGHT_ALT = 346, + KB_KEY_RIGHT_SUPER = 347, + KB_KEY_MENU = 348 +} Key; +#define KB_KEY_LAST KB_KEY_MENU + +typedef enum { + KB_MOD_SHIFT = 0x0001, + KB_MOD_CONTROL = 0x0002, + KB_MOD_ALT = 0x0004, + KB_MOD_SUPER = 0x0008, + KB_MOD_CAPS_LOCK = 0x0010, + KB_MOD_NUM_LOCK = 0x0020 +} KeyMod; + +typedef enum { + WF_RESIZABLE = 0x01, + WF_FULLSCREEN = 0x02, + WF_FULLSCREEN_DESKTOP = 0x04, + WF_BORDERLESS = 0x08, + WF_ALWAYS_ON_TOP = 0x10, +} WindowFlags; diff --git a/src/MiniFB_common.c b/src/MiniFB_common.c new file mode 100755 index 0000000..3e24254 --- /dev/null +++ b/src/MiniFB_common.c @@ -0,0 +1,421 @@ +#include "MiniFB.h" + +//------------------------------------- +mfb_active_func g_active_func = 0x0; +mfb_resize_func g_resize_func = 0x0; +mfb_keyboard_func g_keyboard_func = 0x0; +mfb_char_input_func g_char_input_func = 0x0; +mfb_mouse_btn_func g_mouse_btn_func = 0x0; +mfb_mouse_move_func g_mouse_move_func = 0x0; +mfb_mouse_scroll_func g_mouse_wheel_func = 0x0; + +void *g_user_data = 0x0; + +//------------------------------------- +void mfb_active_callback(mfb_active_func callback) { + g_active_func = callback; +} + +//------------------------------------- +void mfb_resize_callback(mfb_resize_func callback) { + g_resize_func = callback; +} + +//------------------------------------- +void mfb_keyboard_callback(mfb_keyboard_func callback) { + g_keyboard_func = callback; +} + +//------------------------------------- +void mfb_char_input_callback(mfb_char_input_func callback) { + g_char_input_func = callback; +} + +//------------------------------------- +void mfb_mouse_button_callback(mfb_mouse_btn_func callback) { + g_mouse_btn_func = callback; +} + +//------------------------------------- +void mfb_mouse_move_callback(mfb_mouse_move_func callback) { + g_mouse_move_func = callback; +} + +//------------------------------------- +void mfb_mouse_scroll_callback(mfb_mouse_scroll_func callback) { + g_mouse_wheel_func = callback; +} + +//------------------------------------- +void mfb_set_user_data(void *user_data) { + g_user_data = user_data; +} + +//------------------------------------- +const char *mfb_get_key_name(Key key) { + + switch (key) + { + case KB_KEY_SPACE: + return "Space"; + + case KB_KEY_APOSTROPHE: + return "Apostrophe"; + + case KB_KEY_COMMA: + return "Comma"; + + case KB_KEY_MINUS: + return "Minus"; + + case KB_KEY_PERIOD: + return "Period"; + + case KB_KEY_SLASH: + return "Slash"; + + case KB_KEY_0: + return "0"; + + case KB_KEY_1: + return "1"; + + case KB_KEY_2: + return "2"; + + case KB_KEY_3: + return "3"; + + case KB_KEY_4: + return "4"; + + case KB_KEY_5: + return "5"; + + case KB_KEY_6: + return "6"; + + case KB_KEY_7: + return "7"; + + case KB_KEY_8: + return "8"; + + case KB_KEY_9: + return "9"; + + case KB_KEY_SEMICOLON: + return "Semicolon"; + + case KB_KEY_EQUAL: + return "Equal"; + + case KB_KEY_A: + return "A"; + + case KB_KEY_B: + return "B"; + + case KB_KEY_C: + return "C"; + + case KB_KEY_D: + return "D"; + + case KB_KEY_E: + return "E"; + + case KB_KEY_F: + return "F"; + + case KB_KEY_G: + return "G"; + + case KB_KEY_H: + return "H"; + + case KB_KEY_I: + return "I"; + + case KB_KEY_J: + return "J"; + + case KB_KEY_K: + return "K"; + + case KB_KEY_L: + return "L"; + + case KB_KEY_M: + return "M"; + + case KB_KEY_N: + return "N"; + + case KB_KEY_O: + return "O"; + + case KB_KEY_P: + return "P"; + + case KB_KEY_Q: + return "Q"; + + case KB_KEY_R: + return "R"; + + case KB_KEY_S: + return "S"; + + case KB_KEY_T: + return "T"; + + case KB_KEY_U: + return "U"; + + case KB_KEY_V: + return "V"; + + case KB_KEY_W: + return "W"; + + case KB_KEY_X: + return "X"; + + case KB_KEY_Y: + return "Y"; + + case KB_KEY_Z: + return "Z"; + + case KB_KEY_LEFT_BRACKET: + return "Left_Bracket"; + + case KB_KEY_BACKSLASH: + return "Backslash"; + + case KB_KEY_RIGHT_BRACKET: + return "Right_Bracket"; + + case KB_KEY_GRAVE_ACCENT: + return "Grave_Accent"; + + case KB_KEY_WORLD_1: + return "World_1"; + + case KB_KEY_WORLD_2: + return "World_2"; + + case KB_KEY_ESCAPE: + return "Escape"; + + case KB_KEY_ENTER: + return "Enter"; + + case KB_KEY_TAB: + return "Tab"; + + case KB_KEY_BACKSPACE: + return "Backspace"; + + case KB_KEY_INSERT: + return "Insert"; + + case KB_KEY_DELETE: + return "Delete"; + + case KB_KEY_RIGHT: + return "Right"; + + case KB_KEY_LEFT: + return "Left"; + + case KB_KEY_DOWN: + return "Down"; + + case KB_KEY_UP: + return "Up"; + + case KB_KEY_PAGE_UP: + return "Page_Up"; + + case KB_KEY_PAGE_DOWN: + return "Page_Down"; + + case KB_KEY_HOME: + return "Home"; + + case KB_KEY_END: + return "End"; + + case KB_KEY_CAPS_LOCK: + return "Caps_Lock"; + + case KB_KEY_SCROLL_LOCK: + return "Scroll_Lock"; + + case KB_KEY_NUM_LOCK: + return "Num_Lock"; + + case KB_KEY_PRINT_SCREEN: + return "Print_Screen"; + + case KB_KEY_PAUSE: + return "Pause"; + + case KB_KEY_F1: + return "F1"; + + case KB_KEY_F2: + return "F2"; + + case KB_KEY_F3: + return "F3"; + + case KB_KEY_F4: + return "F4"; + + case KB_KEY_F5: + return "F5"; + + case KB_KEY_F6: + return "F6"; + + case KB_KEY_F7: + return "F7"; + + case KB_KEY_F8: + return "F8"; + + case KB_KEY_F9: + return "F9"; + + case KB_KEY_F10: + return "F10"; + + case KB_KEY_F11: + return "F11"; + + case KB_KEY_F12: + return "F12"; + + case KB_KEY_F13: + return "F13"; + + case KB_KEY_F14: + return "F14"; + + case KB_KEY_F15: + return "F15"; + + case KB_KEY_F16: + return "F16"; + + case KB_KEY_F17: + return "F17"; + + case KB_KEY_F18: + return "F18"; + + case KB_KEY_F19: + return "F19"; + + case KB_KEY_F20: + return "F20"; + + case KB_KEY_F21: + return "F21"; + + case KB_KEY_F22: + return "F22"; + + case KB_KEY_F23: + return "F23"; + + case KB_KEY_F24: + return "F24"; + + case KB_KEY_F25: + return "F25"; + + case KB_KEY_KP_0: + return "KP_0"; + + case KB_KEY_KP_1: + return "KP_1"; + + case KB_KEY_KP_2: + return "KP_2"; + + case KB_KEY_KP_3: + return "KP_3"; + + case KB_KEY_KP_4: + return "KP_4"; + + case KB_KEY_KP_5: + return "KP_5"; + + case KB_KEY_KP_6: + return "KP_6"; + + case KB_KEY_KP_7: + return "KP_7"; + + case KB_KEY_KP_8: + return "KP_8"; + + case KB_KEY_KP_9: + return "KP_9"; + + case KB_KEY_KP_DECIMAL: + return "KP_Decimal"; + + case KB_KEY_KP_DIVIDE: + return "KP_Divide"; + + case KB_KEY_KP_MULTIPLY: + return "KP_Multiply"; + + case KB_KEY_KP_SUBTRACT: + return "KP_Subtract"; + + case KB_KEY_KP_ADD: + return "KP_Add"; + + case KB_KEY_KP_ENTER: + return "KP_Enter"; + + case KB_KEY_KP_EQUAL: + return "KP_Equal"; + + case KB_KEY_LEFT_SHIFT: + return "Left_Shift"; + + case KB_KEY_LEFT_CONTROL: + return "Left_Control"; + + case KB_KEY_LEFT_ALT: + return "Left_Alt"; + + case KB_KEY_LEFT_SUPER: + return "Left_Super"; + + case KB_KEY_RIGHT_SHIFT: + return "Right_Shift"; + + case KB_KEY_RIGHT_CONTROL: + return "Right_Control"; + + case KB_KEY_RIGHT_ALT: + return "Right_Alt"; + + case KB_KEY_RIGHT_SUPER: + return "Right_Super"; + + case KB_KEY_MENU: + return "Menu"; + + } + return "Unknown"; +} \ No newline at end of file diff --git a/src/MiniFB_cpp.cpp b/src/MiniFB_cpp.cpp new file mode 100755 index 0000000..eb38303 --- /dev/null +++ b/src/MiniFB_cpp.cpp @@ -0,0 +1,38 @@ +#include +#include + +std::function Stub::m_active; +std::function Stub::m_resize; +std::function Stub::m_keyboard; +std::function Stub::m_char_input; +std::function Stub::m_mouse_btn; +std::function Stub::m_mouse_move; +std::function Stub::m_scroll; + +void Stub::active_stub(void *user_data, bool isActive) { + m_active(user_data, isActive); +} + +void Stub::resize_stub(void *user_data, int width, int height) { + m_resize(user_data, width, height); +} + +void Stub::keyboard_stub(void *user_data, Key key, KeyMod mod, bool isPressed) { + m_keyboard(user_data, key, mod, isPressed); +} + +void Stub::char_input_stub(void *user_data, unsigned int code) { + m_char_input(user_data, code); +} + +void Stub::mouse_btn_stub(void *user_data, MouseButton button, KeyMod mod, bool isPressed) { + m_mouse_btn(user_data, button, mod, isPressed); +} + +void Stub::mouse_move_stub(void *user_data, int x, int y) { + m_mouse_move(user_data, x, y); +} + +void Stub::scroll_stub(void *user_data, KeyMod mod, float deltaX, float deltaY) { + m_scroll(user_data, mod, deltaX, deltaY); +} diff --git a/src/MiniFB_internal.c b/src/MiniFB_internal.c new file mode 100644 index 0000000..5b74a3b --- /dev/null +++ b/src/MiniFB_internal.c @@ -0,0 +1,75 @@ +#include "MiniFB_internal.h" +#include + +//#define kUseBilinearInterpolation + +#if defined(kUseBilinearInterpolation) +static uint32_t interpolate(uint32_t *srcImage, uint32_t x, uint32_t y, uint32_t srcOffsetX, uint32_t srcOffsetY, uint32_t srcWidth, uint32_t srcHeight, uint32_t srcPitch) { + uint32_t incX = x + 1 < srcWidth ? 1 : 0; + uint32_t incY = y + 1 < srcHeight ? srcPitch : 0; + uint8_t *p00 = (uint8_t *) &srcImage[(srcOffsetX >> 16)]; + uint8_t *p01 = (uint8_t *) &srcImage[(srcOffsetX >> 16) + incX]; + uint8_t *p10 = (uint8_t *) &srcImage[(srcOffsetX >> 16) + incY]; + uint8_t *p11 = (uint8_t *) &srcImage[(srcOffsetX >> 16) + incY + incX]; + + uint32_t wx2 = srcOffsetX & 0xffff; + uint32_t wy2 = srcOffsetY & 0xffff; + uint32_t wx1 = 0x10000 - wx2; + uint32_t wy1 = 0x10000 - wy2; + + uint32_t w1 = ((uint64_t) wx1 * wy1) >> 16; + uint32_t w2 = ((uint64_t) wx2 * wy1) >> 16; + uint32_t w3 = ((uint64_t) wx1 * wy2) >> 16; + uint32_t w4 = ((uint64_t) wx2 * wy2) >> 16; + + // If you don't have uint64_t + //uint32_t b = (((p00[0] * wx1 + p01[0] * wx2) >> 16) * wy1 + ((p10[0] * wx1 + p11[0] * wx2) >> 16) * wy2) >> 16; + //uint32_t g = (((p00[1] * wx1 + p01[1] * wx2) >> 16) * wy1 + ((p10[1] * wx1 + p11[1] * wx2) >> 16) * wy2) >> 16; + //uint32_t r = (((p00[2] * wx1 + p01[2] * wx2) >> 16) * wy1 + ((p10[2] * wx1 + p11[2] * wx2) >> 16) * wy2) >> 16; + //uint32_t a = (((p00[3] * wx1 + p01[3] * wx2) >> 16) * wy1 + ((p10[3] * wx1 + p11[3] * wx2) >> 16) * wy2) >> 16; + + uint32_t b = ((p00[0] * w1 + p01[0] * w2) + (p10[0] * w3 + p11[0] * w4)) >> 16; + uint32_t g = ((p00[1] * w1 + p01[1] * w2) + (p10[1] * w3 + p11[1] * w4)) >> 16; + uint32_t r = ((p00[2] * w1 + p01[2] * w2) + (p10[2] * w3 + p11[2] * w4)) >> 16; + uint32_t a = ((p00[3] * w1 + p01[3] * w2) + (p10[3] * w3 + p11[3] * w4)) >> 16; + + return (a << 24) + (r << 16) + (g << 8) + b; +} +#endif + +// Only for 32 bits images +void stretch_image(uint32_t *srcImage, uint32_t srcX, uint32_t srcY, uint32_t srcWidth, uint32_t srcHeight, uint32_t srcPitch, + uint32_t *dstImage, uint32_t dstX, uint32_t dstY, uint32_t dstWidth, uint32_t dstHeight, uint32_t dstPitch) { + + uint32_t x, y; + uint32_t srcOffsetX, srcOffsetY; + + if(srcImage == 0x0 || dstImage == 0x0) + return; + + srcImage += srcX + srcY * srcPitch; + dstImage += dstX + dstY * dstPitch; + + const uint32_t deltaX = (srcWidth << 16) / dstWidth; + const uint32_t deltaY = (srcHeight << 16) / dstHeight; + + srcOffsetY = 0; + for(y=0; y> 16]; +#endif + srcOffsetX += deltaX; + } + + srcOffsetY += deltaY; + if(srcOffsetY >= 0x10000) { + srcImage += (srcOffsetY >> 16) * srcPitch; + srcOffsetY &= 0xffff; + } + dstImage += dstPitch; + } +} \ No newline at end of file diff --git a/src/MiniFB_internal.h b/src/MiniFB_internal.h new file mode 100755 index 0000000..f7b2010 --- /dev/null +++ b/src/MiniFB_internal.h @@ -0,0 +1,28 @@ +#pragma once + +#include "MiniFB.h" + +extern void *g_user_data; + +#define kCall(f, ...) if((f)) (f)(g_user_data, __VA_ARGS__); +#define kUnused(var) (void) var; + +#if defined(__cplusplus) +extern "C" { +#endif + + short int keycodes[512]; + void init_keycodes(); + void keyboard_default(void *user_data, Key key, KeyMod mod, bool isPressed); + + extern mfb_active_func g_active_func; + extern mfb_resize_func g_resize_func; + extern mfb_keyboard_func g_keyboard_func; + extern mfb_char_input_func g_char_input_func; + extern mfb_mouse_btn_func g_mouse_btn_func; + extern mfb_mouse_move_func g_mouse_move_func; + extern mfb_mouse_scroll_func g_mouse_wheel_func; + +#if defined(__cplusplus) +} +#endif diff --git a/src/macosx/MacMiniFB.m b/src/macosx/MacMiniFB.m index 4947d08..a990b70 100644 --- a/src/macosx/MacMiniFB.m +++ b/src/macosx/MacMiniFB.m @@ -1,273 +1,496 @@ -#include "OSXWindow.h" -#include "OSXWindowFrameView.h" -#include -#if defined(USE_METAL_API) -#include -#include -#endif -#include -#include "MiniFB.h" - -#if defined(USE_METAL_API) -extern id g_metal_device; -extern id g_command_queue; -extern id g_library; -extern id g_pipeline_state; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -NSString* g_shadersSrc = @ -" #include \n" - "using namespace metal;\n" - - "struct VertexOutput {\n" - "float4 pos [[position]];\n" - "float2 texcoord;\n" - "};\n" - - "vertex VertexOutput vertFunc(\n" - "unsigned int vID[[vertex_id]])\n" - "{\n" - "VertexOutput out;\n" - - "out.pos.x = (float)(vID / 2) * 4.0 - 1.0;\n" - "out.pos.y = (float)(vID % 2) * 4.0 - 1.0;\n" - "out.pos.z = 0.0;\n" - "out.pos.w = 1.0;\n" - - "out.texcoord.x = (float)(vID / 2) * 2.0;\n" - "out.texcoord.y = 1.0 - (float)(vID % 2) * 2.0;\n" - - "return out;\n" - "}\n" - - "fragment float4 fragFunc(VertexOutput input [[stage_in]],\n" - "texture2d colorTexture [[ texture(0) ]])\n" - "{\n" - "constexpr sampler textureSampler(mag_filter::nearest, min_filter::nearest);\n" - - // Sample the texture to obtain a color - "const half4 colorSample = colorTexture.sample(textureSampler, input.texcoord);\n" - - // We return the color of the texture - "return float4(colorSample);\n" - //"return half4(input.texcoord.x, input.texcoord.y, 0.0, 1.0);\n" - "}\n"; - -#endif - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -#if !defined(USE_METAL_API) -void* g_updateBuffer = 0; -int g_width = 0; -int g_height = 0; -#endif -static OSXWindow *s_window; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -#if defined(USE_METAL_API) -static bool create_shaders() { - // Error - NSError* nsError = NULL; - NSError** nsErrorPtr = &nsError; - - id library = [g_metal_device newLibraryWithSource:g_shadersSrc - options:[[MTLCompileOptions alloc] init] - error:nsErrorPtr]; - - // Error update - if (nsError || !library) { - NSLog(@"Unable to create shaders %@", nsError); - return false; - } - - g_library = library; - NSLog(@"Names %@", [g_library functionNames]); - - id vertex_shader_func = [g_library newFunctionWithName:@"vertFunc"]; - id fragment_shader_func = [g_library newFunctionWithName:@"fragFunc"]; - - if (!vertex_shader_func) { - printf("Unable to get vertFunc!\n"); - return false; - } - - if (!fragment_shader_func) { - printf("Unable to get fragFunc!\n"); - return false; - } - - // Create a reusable pipeline state - MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; - pipelineStateDescriptor.label = @"MyPipeline"; - pipelineStateDescriptor.vertexFunction = vertex_shader_func; - pipelineStateDescriptor.fragmentFunction = fragment_shader_func; - pipelineStateDescriptor.colorAttachments[0].pixelFormat = 80; //bgra8Unorm; - - NSError *error = NULL; - g_pipeline_state = [g_metal_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error]; - if (!g_pipeline_state) - { - NSLog(@"Failed to created pipeline state, error %@", error); - } - - return true; -} -#endif - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int mfb_open(const char* name, int width, int height) -{ - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - -#if !defined(USE_METAL_API) - g_width = width; - g_height = height; -#endif - [NSApplication sharedApplication]; - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - -#if defined(USE_METAL_API) - g_metal_device = MTLCreateSystemDefaultDevice(); - - if (!g_metal_device) { - printf("Your device/OS doesn't support Metal."); - return -1; - } - - if (!create_shaders()) { - return -2; - } -#endif - - NSWindowStyleMask styles = NSWindowStyleMaskResizable | NSWindowStyleMaskClosable | NSWindowStyleMaskTitled; - - NSRect rectangle = NSMakeRect(0, 0, width, height); - s_window = [[OSXWindow alloc] initWithContentRect:rectangle styleMask:styles backing:NSBackingStoreBuffered defer:NO]; - - if (!s_window) - return -3; - -#if defined(USE_METAL_API) - s_window->draw_buffer = malloc(width * height * 4); - - if (!s_window->draw_buffer) - return -4; - - // Setup command queue - g_command_queue = [g_metal_device newCommandQueue]; - - WindowViewController* viewController = [WindowViewController new]; - - MTLTextureDescriptor* textureDescriptor = [[MTLTextureDescriptor alloc] init]; - - // Indicate that each pixel has a blue, green, red, and alpha channel, where each channel is - // an 8-bit unsigned normalized value (i.e. 0 maps to 0.0 and 255 maps to 1.0) - textureDescriptor.pixelFormat = MTLPixelFormatBGRA8Unorm; - - // Set the pixel dimensions of the texture - textureDescriptor.width = width; - textureDescriptor.height = height; - - // Create the texture from the device by using the descriptor - - for (int i = 0; i < MaxBuffersInFlight; ++i) { - viewController->m_texture_buffers[i] = [g_metal_device newTextureWithDescriptor:textureDescriptor]; - } - - // Used for syncing the CPU and GPU - viewController->m_semaphore = dispatch_semaphore_create(MaxBuffersInFlight); - viewController->m_draw_buffer = s_window->draw_buffer; - viewController->m_width = width; - viewController->m_height = height; - - MTKView* view = [[MTKView alloc] initWithFrame:rectangle]; - view.device = g_metal_device; - view.delegate = viewController; - view.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; - [s_window.contentView addSubview:view]; - - s_window->width = width; - s_window->height = height; - - //[s_window updateSize]; -#endif - - [s_window setTitle:[NSString stringWithUTF8String:name]]; - [s_window setReleasedWhenClosed:NO]; - [s_window performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) withObject:nil waitUntilDone:YES]; - - [s_window center]; - - [NSApp activateIgnoringOtherApps:YES]; - -#if defined(USE_METAL_API) - [NSApp finishLaunching]; -#endif - - [pool drain]; - - return 1; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void mfb_close() -{ - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - - if (s_window) - [s_window close]; - - [pool drain]; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -static int updateEvents() -{ - int state = 0; - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; - if (event) - { - switch ([event type]) - { - case NSEventTypeKeyDown: - case NSEventTypeKeyUp: - { - state = -1; - break; - } - - default : - { - [NSApp sendEvent:event]; - break; - } - } - } - [pool release]; - - if (s_window->closed) - state = -1; - - return state; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int mfb_update(void* buffer) -{ -#if defined(USE_METAL_API) - memcpy(s_window->draw_buffer, buffer, s_window->width * s_window->height * 4); -#else - g_updateBuffer = buffer; -#endif - int state = updateEvents(); - [[s_window contentView] setNeedsDisplay:YES]; - return state; -} +#include "OSXWindow.h" +#include "OSXWindowFrameView.h" +#include "OSXWindowData.h" +#include +#include +#include +#include +#if defined(USE_METAL_API) +#include +#include +#endif +#include + +SWindowData g_window_data = { 0 }; + +void init_keycodes(); + +#if defined(USE_METAL_API) +extern id g_metal_device; +extern id g_command_queue; +extern id g_library; +extern id g_pipeline_state; + +Vertex gVertices[4] = { + {-1.0, -1.0, 0, 1}, + {-1.0, 1.0, 0, 1}, + { 1.0, -1.0, 0, 1}, + { 1.0, 1.0, 0, 1}, +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +NSString* g_shadersSrc = @ +" #include \n" + "using namespace metal;\n" + + "struct VertexOutput {\n" + "float4 pos [[position]];\n" + "float2 texcoord;\n" + "};\n" + + "vertex VertexOutput vertFunc(unsigned int vID[[vertex_id]])\n" + "{\n" + "VertexOutput out;\n" + + "out.pos.x = (float)(vID / 2) * 4.0 - 1.0;\n" + "out.pos.y = (float)(vID % 2) * 4.0 - 1.0;\n" + "out.pos.z = 0.0;\n" + "out.pos.w = 1.0;\n" + + "out.texcoord.x = (float)(vID / 2) * 2.0;\n" + "out.texcoord.y = 1.0 - (float)(vID % 2) * 2.0;\n" + + "return out;\n" + "}\n" + + "struct Vertex\n" + "{\n" + "float4 position [[position]];\n" + "};\n" + + "vertex VertexOutput vertFunc2(unsigned int vID[[vertex_id]], device Vertex *pos [[buffer(0)]])\n" + "{\n" + "VertexOutput out;\n" + + "out.pos = pos[vID].position;\n" + + "out.texcoord.x = (float)(vID / 2);\n" + "out.texcoord.y = 1.0 - (float)(vID % 2);\n" + + "return out;\n" + "}\n" + + "fragment float4 fragFunc(VertexOutput input [[stage_in]],\n" + "texture2d colorTexture [[ texture(0) ]])\n" + "{\n" + "constexpr sampler textureSampler(mag_filter::nearest, min_filter::nearest);\n" + + // Sample the texture to obtain a color + "const half4 colorSample = colorTexture.sample(textureSampler, input.texcoord);\n" + + // We return the color of the texture + "return float4(colorSample);\n" + //"return float4(input.texcoord.x, input.texcoord.y, 0.0, 1.0);\n" + "}\n"; + +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#if defined(USE_METAL_API) +static bool create_shaders() { + // Error + NSError* nsError = NULL; + NSError** nsErrorPtr = &nsError; + + id library = [g_metal_device newLibraryWithSource:g_shadersSrc + options:[[MTLCompileOptions alloc] init] + error:nsErrorPtr]; + + // Error update + if (nsError || !library) { + NSLog(@"Unable to create shaders %@", nsError); + return false; + } + + g_library = library; + NSLog(@"Names %@", [g_library functionNames]); + + id vertex_shader_func = [g_library newFunctionWithName:@"vertFunc2"]; + id fragment_shader_func = [g_library newFunctionWithName:@"fragFunc"]; + + if (!vertex_shader_func) { + printf("Unable to get vertFunc!\n"); + return false; + } + + if (!fragment_shader_func) { + printf("Unable to get fragFunc!\n"); + return false; + } + + // Create a reusable pipeline state + MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; + pipelineStateDescriptor.label = @"MyPipeline"; + pipelineStateDescriptor.vertexFunction = vertex_shader_func; + pipelineStateDescriptor.fragmentFunction = fragment_shader_func; + pipelineStateDescriptor.colorAttachments[0].pixelFormat = 80; //bgra8Unorm; + + NSError *error = NULL; + g_pipeline_state = [g_metal_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error]; + if (!g_pipeline_state) + { + NSLog(@"Failed to created pipeline state, error %@", error); + } + + return true; +} +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int mfb_open(const char* name, int width, int height) +{ + return mfb_open_ex(name, width, height, 0); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int mfb_open_ex(const char* name, int width, int height, int flags) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + init_keycodes(); + + g_window_data.window_width = width; + g_window_data.window_height = height; + + g_window_data.dst_width = width; + g_window_data.dst_height = height; + + g_window_data.buffer_width = width; + g_window_data.buffer_height = height; + + [NSApplication sharedApplication]; + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + +#if defined(USE_METAL_API) + g_metal_device = MTLCreateSystemDefaultDevice(); + + if (!g_metal_device) { + printf("Your device/OS doesn't support Metal."); + return -1; + } + + if (!create_shaders()) { + return -2; + } +#endif + + NSWindowStyleMask styles = NSWindowStyleMaskClosable | NSWindowStyleMaskTitled; + + if (flags & WF_BORDERLESS) + styles |= NSWindowStyleMaskBorderless; + + if (flags & WF_RESIZABLE) + styles |= NSWindowStyleMaskResizable; + + NSRect rectangle = NSMakeRect(0, 0, width, height); + g_window_data.window = [[OSXWindow alloc] initWithContentRect:rectangle styleMask:styles backing:NSBackingStoreBuffered defer:NO]; + if (!g_window_data.window) + return -3; + +#if defined(USE_METAL_API) + g_window_data.draw_buffer = malloc(width * height * 4); + + if (!g_window_data.draw_buffer) + return -4; + + // Setup command queue + g_command_queue = [g_metal_device newCommandQueue]; + + WindowViewController* viewController = [WindowViewController new]; + + MTLTextureDescriptor* textureDescriptor = [[MTLTextureDescriptor alloc] init]; + + // Indicate that each pixel has a blue, green, red, and alpha channel, where each channel is + // an 8-bit unsigned normalized value (i.e. 0 maps to 0.0 and 255 maps to 1.0) + textureDescriptor.pixelFormat = MTLPixelFormatBGRA8Unorm; + + // Set the pixel dimensions of the texture + textureDescriptor.width = width; + textureDescriptor.height = height; + + // Create the texture from the device by using the descriptor + + for (int i = 0; i < MaxBuffersInFlight; ++i) { + viewController->m_texture_buffers[i] = [g_metal_device newTextureWithDescriptor:textureDescriptor]; + } + + // Used for syncing the CPU and GPU + viewController->m_semaphore = dispatch_semaphore_create(MaxBuffersInFlight); + viewController->m_draw_buffer = g_window_data.draw_buffer; + viewController->m_width = width; + viewController->m_height = height; + + MTKView* view = [[MTKView alloc] initWithFrame:rectangle]; + view.device = g_metal_device; + view.delegate = viewController; + view.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; + [g_window_data.window.contentView addSubview:view]; + + g_window_data.buffer_width = width; + g_window_data.buffer_height = height; + + //[g_window_data.window updateSize]; +#endif + + [g_window_data.window setTitle:[NSString stringWithUTF8String:name]]; + [g_window_data.window setReleasedWhenClosed:NO]; + [g_window_data.window performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) withObject:nil waitUntilDone:YES]; + [g_window_data.window setAcceptsMouseMovedEvents:YES]; + + [g_window_data.window center]; + + [NSApp activateIgnoringOtherApps:YES]; + +#if defined(USE_METAL_API) + [NSApp finishLaunching]; +#endif + + if (g_keyboard_func == 0x0) { + mfb_keyboard_callback(keyboard_default); + } + +#if defined(USE_METAL_API) + NSLog(@"Window created using Metal API"); +#else + NSLog(@"Window created using Cocoa API"); +#endif + + [pool drain]; + + return 1; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void keyboard_default(void *user_data, Key key, KeyMod mod, bool isPressed) { + kUnused(user_data); + kUnused(mod); + kUnused(isPressed); + if (key == KB_KEY_ESCAPE) + g_window_data.close = true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void mfb_close() +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + g_window_data.close = true; + if (g_window_data.window) + [g_window_data.window close]; + + [pool drain]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static int update_events() +{ + NSEvent* event; + + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + do + { + event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; + if (event) { + [NSApp sendEvent:event]; + } + } + while ((g_window_data.close == false) && event); + + [pool release]; + + if(g_window_data.close == true) + return -1; + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int mfb_update(void* buffer) +{ + if(buffer == 0x0) + return -2; + +#if defined(USE_METAL_API) + memcpy(g_window_data.draw_buffer, buffer, g_window_data.buffer_width * g_window_data.buffer_height * 4); +#else + g_window_data.draw_buffer = buffer; +#endif + + int state = update_events(); + if(g_window_data.close == false) + [[g_window_data.window contentView] setNeedsDisplay:YES]; + + return state; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool mfb_set_viewport(unsigned offset_x, unsigned offset_y, unsigned width, unsigned height) +{ + if(offset_x + width > g_window_data.window_width) { + return false; + } + if(offset_y + height > g_window_data.window_height) { + return false; + } + + g_window_data.dst_offset_x = offset_x; + g_window_data.dst_offset_y = offset_y; + g_window_data.dst_width = width; + g_window_data.dst_height = height; + +#if defined(USE_METAL_API) + float x1 = ((float) offset_x / g_window_data.window_width) * 2.0f - 1.0f; + float x2 = (((float) offset_x + width) / g_window_data.window_width) * 2.0f - 1.0f; + float y1 = ((float) offset_y / g_window_data.window_height) * 2.0f - 1.0f; + float y2 = (((float) offset_y + height) / g_window_data.window_height) * 2.0f - 1.0f; + + gVertices[0].x = x1; + gVertices[0].y = y1; + + gVertices[1].x = x1; + gVertices[1].y = y2; + + gVertices[2].x = x2; + gVertices[2].y = y1; + + gVertices[3].x = x2; + gVertices[3].y = y2; +#endif + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +extern short int keycodes[512]; + +void init_keycodes() +{ + // Clear keys + for (unsigned int i = 0; i < sizeof(keycodes) / sizeof(keycodes[0]); ++i) + keycodes[i] = 0; + + keycodes[0x1D] = KB_KEY_0; + keycodes[0x12] = KB_KEY_1; + keycodes[0x13] = KB_KEY_2; + keycodes[0x14] = KB_KEY_3; + keycodes[0x15] = KB_KEY_4; + keycodes[0x17] = KB_KEY_5; + keycodes[0x16] = KB_KEY_6; + keycodes[0x1A] = KB_KEY_7; + keycodes[0x1C] = KB_KEY_8; + keycodes[0x19] = KB_KEY_9; + keycodes[0x00] = KB_KEY_A; + keycodes[0x0B] = KB_KEY_B; + keycodes[0x08] = KB_KEY_C; + keycodes[0x02] = KB_KEY_D; + keycodes[0x0E] = KB_KEY_E; + keycodes[0x03] = KB_KEY_F; + keycodes[0x05] = KB_KEY_G; + keycodes[0x04] = KB_KEY_H; + keycodes[0x22] = KB_KEY_I; + keycodes[0x26] = KB_KEY_J; + keycodes[0x28] = KB_KEY_K; + keycodes[0x25] = KB_KEY_L; + keycodes[0x2E] = KB_KEY_M; + keycodes[0x2D] = KB_KEY_N; + keycodes[0x1F] = KB_KEY_O; + keycodes[0x23] = KB_KEY_P; + keycodes[0x0C] = KB_KEY_Q; + keycodes[0x0F] = KB_KEY_R; + keycodes[0x01] = KB_KEY_S; + keycodes[0x11] = KB_KEY_T; + keycodes[0x20] = KB_KEY_U; + keycodes[0x09] = KB_KEY_V; + keycodes[0x0D] = KB_KEY_W; + keycodes[0x07] = KB_KEY_X; + keycodes[0x10] = KB_KEY_Y; + keycodes[0x06] = KB_KEY_Z; + + keycodes[0x27] = KB_KEY_APOSTROPHE; + keycodes[0x2A] = KB_KEY_BACKSLASH; + keycodes[0x2B] = KB_KEY_COMMA; + keycodes[0x18] = KB_KEY_EQUAL; + keycodes[0x32] = KB_KEY_GRAVE_ACCENT; + keycodes[0x21] = KB_KEY_LEFT_BRACKET; + keycodes[0x1B] = KB_KEY_MINUS; + keycodes[0x2F] = KB_KEY_PERIOD; + keycodes[0x1E] = KB_KEY_RIGHT_BRACKET; + keycodes[0x29] = KB_KEY_SEMICOLON; + keycodes[0x2C] = KB_KEY_SLASH; + keycodes[0x0A] = KB_KEY_WORLD_1; + + keycodes[0x33] = KB_KEY_BACKSPACE; + keycodes[0x39] = KB_KEY_CAPS_LOCK; + keycodes[0x75] = KB_KEY_DELETE; + keycodes[0x7D] = KB_KEY_DOWN; + keycodes[0x77] = KB_KEY_END; + keycodes[0x24] = KB_KEY_ENTER; + keycodes[0x35] = KB_KEY_ESCAPE; + keycodes[0x7A] = KB_KEY_F1; + keycodes[0x78] = KB_KEY_F2; + keycodes[0x63] = KB_KEY_F3; + keycodes[0x76] = KB_KEY_F4; + keycodes[0x60] = KB_KEY_F5; + keycodes[0x61] = KB_KEY_F6; + keycodes[0x62] = KB_KEY_F7; + keycodes[0x64] = KB_KEY_F8; + keycodes[0x65] = KB_KEY_F9; + keycodes[0x6D] = KB_KEY_F10; + keycodes[0x67] = KB_KEY_F11; + keycodes[0x6F] = KB_KEY_F12; + keycodes[0x69] = KB_KEY_F13; + keycodes[0x6B] = KB_KEY_F14; + keycodes[0x71] = KB_KEY_F15; + keycodes[0x6A] = KB_KEY_F16; + keycodes[0x40] = KB_KEY_F17; + keycodes[0x4F] = KB_KEY_F18; + keycodes[0x50] = KB_KEY_F19; + keycodes[0x5A] = KB_KEY_F20; + keycodes[0x73] = KB_KEY_HOME; + keycodes[0x72] = KB_KEY_INSERT; + keycodes[0x7B] = KB_KEY_LEFT; + keycodes[0x3A] = KB_KEY_LEFT_ALT; + keycodes[0x3B] = KB_KEY_LEFT_CONTROL; + keycodes[0x38] = KB_KEY_LEFT_SHIFT; + keycodes[0x37] = KB_KEY_LEFT_SUPER; + keycodes[0x6E] = KB_KEY_MENU; + keycodes[0x47] = KB_KEY_NUM_LOCK; + keycodes[0x79] = KB_KEY_PAGE_DOWN; + keycodes[0x74] = KB_KEY_PAGE_UP; + keycodes[0x7C] = KB_KEY_RIGHT; + keycodes[0x3D] = KB_KEY_RIGHT_ALT; + keycodes[0x3E] = KB_KEY_RIGHT_CONTROL; + keycodes[0x3C] = KB_KEY_RIGHT_SHIFT; + keycodes[0x36] = KB_KEY_RIGHT_SUPER; + keycodes[0x31] = KB_KEY_SPACE; + keycodes[0x30] = KB_KEY_TAB; + keycodes[0x7E] = KB_KEY_UP; + + keycodes[0x52] = KB_KEY_KP_0; + keycodes[0x53] = KB_KEY_KP_1; + keycodes[0x54] = KB_KEY_KP_2; + keycodes[0x55] = KB_KEY_KP_3; + keycodes[0x56] = KB_KEY_KP_4; + keycodes[0x57] = KB_KEY_KP_5; + keycodes[0x58] = KB_KEY_KP_6; + keycodes[0x59] = KB_KEY_KP_7; + keycodes[0x5B] = KB_KEY_KP_8; + keycodes[0x5C] = KB_KEY_KP_9; + keycodes[0x45] = KB_KEY_KP_ADD; + keycodes[0x41] = KB_KEY_KP_DECIMAL; + keycodes[0x4B] = KB_KEY_KP_DIVIDE; + keycodes[0x4C] = KB_KEY_KP_ENTER; + keycodes[0x51] = KB_KEY_KP_EQUAL; + keycodes[0x43] = KB_KEY_KP_MULTIPLY; + keycodes[0x4E] = KB_KEY_KP_SUBTRACT; +} diff --git a/src/macosx/OSXWindow.h b/src/macosx/OSXWindow.h index 28bf33f..61a92e7 100644 --- a/src/macosx/OSXWindow.h +++ b/src/macosx/OSXWindow.h @@ -1,16 +1,10 @@ -#import - -// @class OSXWindowFrameView; - -@interface OSXWindow : NSWindow -{ - NSView* childContentView; - @public bool closed; -#if defined(USE_METAL_API) - @public int width; - @public int height; - @public void* draw_buffer; -#endif -} - -@end +#import + +// @class OSXWindowFrameView; + +@interface OSXWindow : NSWindow +{ + NSView *childContentView; +} + +@end diff --git a/src/macosx/OSXWindow.m b/src/macosx/OSXWindow.m index cd3d63c..9b1a4ab 100644 --- a/src/macosx/OSXWindow.m +++ b/src/macosx/OSXWindow.m @@ -1,155 +1,276 @@ -#import "OSXWindow.h" -#import "OSXWindowFrameView.h" - -@implementation OSXWindow - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -#if !defined(USE_METAL_API) -- (id)initWithContentRect:(NSRect)contentRect - styleMask:(NSWindowStyleMask)windowStyle - backing:(NSBackingStoreType)bufferingType - defer:(BOOL)deferCreation -{ - self = [super - initWithContentRect:contentRect - styleMask:windowStyle - backing:bufferingType - defer:deferCreation]; - if (self) - { - [self setOpaque:YES]; - [self setBackgroundColor:[NSColor clearColor]]; - - [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(mainWindowChanged:) - name:NSWindowDidBecomeMainNotification - object:self]; - - [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(mainWindowChanged:) - name:NSWindowDidResignMainNotification - object:self]; - - [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(willClose) - name:NSWindowWillCloseNotification - object:self]; - - closed = false; - } - return self; -} -#endif - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] - removeObserver:self]; - [super dealloc]; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (void)setContentSize:(NSSize)newSize -{ - NSSize sizeDelta = newSize; - NSSize childBoundsSize = [childContentView bounds].size; - sizeDelta.width -= childBoundsSize.width; - sizeDelta.height -= childBoundsSize.height; - - OSXWindowFrameView *frameView = [super contentView]; - NSSize newFrameSize = [frameView bounds].size; - newFrameSize.width += sizeDelta.width; - newFrameSize.height += sizeDelta.height; - - [super setContentSize:newFrameSize]; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (void)mainWindowChanged:(NSNotification *)aNotification -{ - (void)aNotification; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (void)setContentView:(NSView *)aView -{ - if ([childContentView isEqualTo:aView]) - { - return; - } - - NSRect bounds = [self frame]; - bounds.origin = NSZeroPoint; - - OSXWindowFrameView *frameView = [super contentView]; - if (!frameView) - { - frameView = [[[OSXWindowFrameView alloc] initWithFrame:bounds] autorelease]; - - [super setContentView:frameView]; - } - - if (childContentView) - { - [childContentView removeFromSuperview]; - } - childContentView = aView; - [childContentView setFrame:[self contentRectForFrameRect:bounds]]; - [childContentView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - [frameView addSubview:childContentView]; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (NSView *)contentView -{ - return childContentView; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (BOOL)canBecomeKeyWindow -{ - return YES; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (BOOL)canBecomeMainWindow -{ - return YES; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (NSRect)contentRectForFrameRect:(NSRect)windowFrame -{ - windowFrame.origin = NSZeroPoint; - return NSInsetRect(windowFrame, 0, 0); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -+ (NSRect)frameRectForContentRect:(NSRect)windowContentRect styleMask:(NSWindowStyleMask)windowStyle -{ - (void)windowStyle; - return NSInsetRect(windowContentRect, 0, 0); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (void)willClose -{ - closed = true; -} - -@end +#import "OSXWindow.h" +#import "OSXWindowFrameView.h" +#include "OSXWindowData.h" +#include +#include + +extern SWindowData g_window_data; +extern short int g_keycodes[512]; + +bool gActive = false; + +@implementation OSXWindow + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (id)initWithContentRect:(NSRect)contentRect + styleMask:(NSWindowStyleMask)windowStyle + backing:(NSBackingStoreType)bufferingType + defer:(BOOL)deferCreation +{ + self = [super + initWithContentRect:contentRect + styleMask:windowStyle + backing:bufferingType + defer:deferCreation]; + + if (self) + { + [self setOpaque:YES]; + [self setBackgroundColor:[NSColor clearColor]]; + + self.delegate = self; + } + return self; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] + removeObserver:self]; + [super dealloc]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)setContentSize:(NSSize)newSize +{ + NSSize sizeDelta = newSize; + NSSize childBoundsSize = [childContentView bounds].size; + sizeDelta.width -= childBoundsSize.width; + sizeDelta.height -= childBoundsSize.height; + + OSXWindowFrameView *frameView = [super contentView]; + NSSize newFrameSize = [frameView bounds].size; + newFrameSize.width += sizeDelta.width; + newFrameSize.height += sizeDelta.height; + + [super setContentSize:newFrameSize]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +-(void)flagsChanged:(NSEvent *)event +{ + const uint32_t flags = [event modifierFlags]; + uint32_t mod_keys = 0, mod_keys_aux = 0; + + //NSEventModifierFlagHelp = 1 << 22, + //NSEventModifierFlagFunction = 1 << 23, + if(flags & NSEventModifierFlagCapsLock) { + mod_keys |= KB_MOD_CAPS_LOCK; + } + if(flags & NSEventModifierFlagShift) { + mod_keys |= KB_MOD_SHIFT; + } + if(flags & NSEventModifierFlagControl) { + mod_keys |= KB_MOD_CONTROL; + } + if(flags & NSEventModifierFlagOption) { + mod_keys |= KB_MOD_ALT; + } + if(flags & NSEventModifierFlagCommand) { + mod_keys |= KB_MOD_SUPER; + } + if(flags & NSEventModifierFlagNumericPad) { + mod_keys |= KB_MOD_NUM_LOCK; + } + + if(mod_keys != g_window_data.mod_keys) { + short int keyCode = keycodes[[event keyCode] & 0x1ff]; + if(keyCode != KB_KEY_UNKNOWN) { + mod_keys_aux = mod_keys ^ g_window_data.mod_keys; + if(mod_keys_aux & KB_MOD_CAPS_LOCK) { + kCall(g_keyboard_func, keyCode, mod_keys, (mod_keys & KB_MOD_CAPS_LOCK) != 0); + } + if(mod_keys_aux & KB_MOD_SHIFT) { + kCall(g_keyboard_func, keyCode, mod_keys, (mod_keys & KB_MOD_SHIFT) != 0); + } + if(mod_keys_aux & KB_MOD_CONTROL) { + kCall(g_keyboard_func, keyCode, mod_keys, (mod_keys & KB_MOD_CONTROL) != 0); + } + if(mod_keys_aux & KB_MOD_ALT) { + kCall(g_keyboard_func, keyCode, mod_keys, (mod_keys & KB_MOD_ALT) != 0); + } + if(mod_keys_aux & KB_MOD_SUPER) { + kCall(g_keyboard_func, keyCode, mod_keys, (mod_keys & KB_MOD_SUPER) != 0); + } + if(mod_keys_aux & KB_MOD_NUM_LOCK) { + kCall(g_keyboard_func, keyCode, mod_keys, (mod_keys & KB_MOD_NUM_LOCK) != 0); + } + } + } + g_window_data.mod_keys = mod_keys; + //NSLog(@"KeyCode: %d (%x) - %x", [event keyCode], [event keyCode], flags); + + [super flagsChanged:event]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)keyDown:(NSEvent *)event +{ + short int keyCode = keycodes[[event keyCode] & 0x1ff]; + kCall(g_keyboard_func, keyCode, g_window_data.mod_keys, true); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)keyUp:(NSEvent *)event +{ + short int keyCode = keycodes[[event keyCode] & 0x1ff]; + kCall(g_keyboard_func, keyCode, g_window_data.mod_keys, false); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)insertText:(id)string replacementRange:(NSRange)replacementRange +{ + NSString *characters; + NSUInteger length; + + kUnused(replacementRange); + + if ([string isKindOfClass:[NSAttributedString class]]) + characters = [string string]; + else + characters = (NSString*) string; + + length = [characters length]; + for (NSUInteger i = 0; i < length; i++) + { + const unichar code = [characters characterAtIndex:i]; + if ((code & 0xff00) == 0xf700) + continue; + + kCall(g_char_input_func, code); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)mainWindowChanged:(NSNotification *)notification +{ + kUnused(notification); + + if(gActive == true) { + gActive = false; + kCall(g_active_func, false); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)setContentView:(NSView *)aView +{ + if ([childContentView isEqualTo:aView]) + { + return; + } + NSRect bounds = [self frame]; + bounds.origin = NSZeroPoint; + + OSXWindowFrameView *frameView = [super contentView]; + if (!frameView) + { + frameView = [[[OSXWindowFrameView alloc] initWithFrame:bounds] autorelease]; + + [super setContentView:frameView]; + } + + if (childContentView) + { + [childContentView removeFromSuperview]; + } + childContentView = aView; + [childContentView setFrame:[self contentRectForFrameRect:bounds]]; + [childContentView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [frameView addSubview:childContentView]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (NSView *)contentView +{ + return childContentView; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (BOOL)canBecomeKeyWindow +{ + return YES; +} + +- (void)windowDidBecomeKey:(NSNotification *)notification +{ + kUnused(notification); + kCall(g_active_func, true); +} + +- (void)windowDidResignKey:(NSNotification *)notification +{ + kUnused(notification); + kCall(g_active_func, false); +} + +- (void)windowWillClose:(NSNotification *)notification { + kUnused(notification); + g_window_data.close = true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (BOOL)canBecomeMainWindow +{ + return YES; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (NSRect)contentRectForFrameRect:(NSRect)windowFrame +{ + windowFrame.origin = NSZeroPoint; + return NSInsetRect(windowFrame, 0, 0); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ++ (NSRect)frameRectForContentRect:(NSRect)windowContentRect styleMask:(NSWindowStyleMask)windowStyle +{ + kUnused(windowStyle); + return NSInsetRect(windowContentRect, 0, 0); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)willClose +{ + g_window_data.close = true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)windowDidResize:(NSNotification *)notification { + kUnused(notification); + CGSize size = [self contentRectForFrameRect:[self frame]].size; + + g_window_data.window_width = size.width; + g_window_data.window_height = size.height; + + kCall(g_resize_func, size.width, size.height); +} + +@end diff --git a/src/macosx/OSXWindowData.h b/src/macosx/OSXWindowData.h new file mode 100644 index 0000000..b0ebc0b --- /dev/null +++ b/src/macosx/OSXWindowData.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +@class OSXWindow; + +typedef struct { + OSXWindow *window; + uint32_t window_width; + uint32_t window_height; + + uint32_t dst_offset_x; + uint32_t dst_offset_y; + uint32_t dst_width; + uint32_t dst_height; + + void *draw_buffer; + uint32_t buffer_width; + uint32_t buffer_height; + uint32_t mod_keys; + bool close; +} SWindowData; diff --git a/src/macosx/OSXWindowFrameView.h b/src/macosx/OSXWindowFrameView.h index 92bb70f..93e6173 100644 --- a/src/macosx/OSXWindowFrameView.h +++ b/src/macosx/OSXWindowFrameView.h @@ -1,35 +1,39 @@ -#import - -#if defined(USE_METAL_API) -#import - -// Number of textures in flight (tripple buffered) -static const int MaxBuffersInFlight = 3; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -@interface WindowViewController : NSViewController -{ - @public id m_texture_buffers[MaxBuffersInFlight]; - @public int m_current_buffer; - @public void* m_draw_buffer; - @public int m_width; - @public int m_height; - // Used for syncing with CPU/GPU - @public dispatch_semaphore_t m_semaphore; -} - -@end -#endif - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -@interface OSXWindowFrameView : NSView -{ -#if defined(USE_METAL_API) - @private NSTrackingArea* trackingArea; -#endif -} - -@end - +#import + +#if defined(USE_METAL_API) +#import + +typedef struct Vertex { + float x, y, z, w; +} Vertex; + +// Number of textures in flight (tripple buffered) +static const int MaxBuffersInFlight = 3; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@interface WindowViewController : NSViewController +{ + @public id m_texture_buffers[MaxBuffersInFlight]; + @public int m_current_buffer; + @public void* m_draw_buffer; + @public int m_width; + @public int m_height; + // Used for syncing with CPU/GPU + @public dispatch_semaphore_t m_semaphore; +} + +@end +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@interface OSXWindowFrameView : NSView +{ +#if defined(USE_METAL_API) + @private NSTrackingArea* trackingArea; +#endif +} + +@end + diff --git a/src/macosx/OSXWindowFrameView.m b/src/macosx/OSXWindowFrameView.m index 2ff218e..fc278ec 100644 --- a/src/macosx/OSXWindowFrameView.m +++ b/src/macosx/OSXWindowFrameView.m @@ -1,155 +1,319 @@ -#import "OSXWindowFrameView.h" - -#if defined(USE_METAL_API) -#import - -id g_metal_device; -id g_command_queue; -id g_library; -id g_pipeline_state; - -@implementation WindowViewController - --(void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size -{ - (void)view; - (void)size; - // resize -} - --(void)drawInMTKView:(nonnull MTKView *)view -{ - // Wait to ensure only MaxBuffersInFlight number of frames are getting proccessed - // by any stage in the Metal pipeline (App, Metal, Drivers, GPU, etc) - dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER); - - // Iterate through our Metal buffers, and cycle back to the first when we've written to MaxBuffersInFlight - m_current_buffer = (m_current_buffer + 1) % MaxBuffersInFlight; - - // Calculate the number of bytes per row of our image. - NSUInteger bytesPerRow = 4 * m_width; - MTLRegion region = { { 0, 0, 0 }, { m_width, m_height, 1 } }; - - // Copy the bytes from our data object into the texture - [m_texture_buffers[m_current_buffer] replaceRegion:region - mipmapLevel:0 withBytes:m_draw_buffer bytesPerRow:bytesPerRow]; - - // Create a new command buffer for each render pass to the current drawable - id commandBuffer = [g_command_queue commandBuffer]; - commandBuffer.label = @"minifb_command_buffer"; - - // Add completion hander which signals _inFlightSemaphore when Metal and the GPU has fully - // finished processing the commands we're encoding this frame. This indicates when the - // dynamic buffers filled with our vertices, that we're writing to this frame, will no longer - // be needed by Metal and the GPU, meaning we can overwrite the buffer contents without - // corrupting the rendering. - __block dispatch_semaphore_t block_sema = m_semaphore; - [commandBuffer addCompletedHandler:^(id buffer) - { - (void)buffer; - dispatch_semaphore_signal(block_sema); - }]; - - MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor; - - if (renderPassDescriptor != nil) - { - renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(1.0, 0.0, 0.0, 1.0); - - // Create a render command encoder so we can render into something - id renderEncoder = - [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; - renderEncoder.label = @"minifb_command_encoder"; - - // Set render command encoder state - [renderEncoder setRenderPipelineState:g_pipeline_state]; - - [renderEncoder setFragmentTexture:m_texture_buffers[m_current_buffer] atIndex:0]; - - // Draw the vertices of our quads - [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle - vertexStart:0 - vertexCount:3]; - - // We're done encoding commands - [renderEncoder endEncoding]; - - // Schedule a present once the framebuffer is complete using the current drawable - [commandBuffer presentDrawable:view.currentDrawable]; - } - - // Finalize rendering here & push the command buffer to the GPU - [commandBuffer commit]; -} -@end -#endif - -@implementation OSXWindowFrameView - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -#if defined(USE_METAL_API) --(void)updateTrackingAreas -{ - if(trackingArea != nil) { - [self removeTrackingArea:trackingArea]; - [trackingArea release]; - } - - int opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways); - trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds] - options:opts - owner:self - userInfo:nil]; - [self addTrackingArea:trackingArea]; -} -#else -extern void* g_updateBuffer; -extern int g_width; -extern int g_height; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (NSRect)resizeRect -{ - const CGFloat resizeBoxSize = 16.0; - const CGFloat contentViewPadding = 5.5; - - NSRect contentViewRect = [[self window] contentRectForFrameRect:[[self window] frame]]; - NSRect resizeRect = NSMakeRect( - NSMaxX(contentViewRect) + contentViewPadding, - NSMinY(contentViewRect) - resizeBoxSize - contentViewPadding, - resizeBoxSize, - resizeBoxSize); - - return resizeRect; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (void)drawRect:(NSRect)rect -{ - (void)rect; - - if (!g_updateBuffer) - return; - - CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; - - CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); - CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, g_updateBuffer, g_width * g_height * 4, NULL); - - CGImageRef img = CGImageCreate(g_width, g_height, 8, 32, g_width * 4, space, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little, - provider, NULL, false, kCGRenderingIntentDefault); - - CGColorSpaceRelease(space); - CGDataProviderRelease(provider); - - CGContextDrawImage(context, CGRectMake(0, 0, g_width, g_height), img); - - CGImageRelease(img); -} -#endif - -@end - +#import "OSXWindowFrameView.h" +#import "OSXWindow.h" +#include "OSXWindowData.h" +#include + +extern SWindowData g_window_data; + +#if defined(USE_METAL_API) +#import + +id g_metal_device; +id g_command_queue; +id g_library; +id g_pipeline_state; + +extern Vertex gVertices[4]; + +@implementation WindowViewController + +-(void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size +{ + (void)view; + (void)size; + // resize +} + +-(void)drawInMTKView:(nonnull MTKView *)view +{ + // Wait to ensure only MaxBuffersInFlight number of frames are getting proccessed + // by any stage in the Metal pipeline (App, Metal, Drivers, GPU, etc) + dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER); + + // Iterate through our Metal buffers, and cycle back to the first when we've written to MaxBuffersInFlight + m_current_buffer = (m_current_buffer + 1) % MaxBuffersInFlight; + + // Calculate the number of bytes per row of our image. + NSUInteger bytesPerRow = 4 * m_width; + MTLRegion region = { { 0, 0, 0 }, { m_width, m_height, 1 } }; + + // Copy the bytes from our data object into the texture + [m_texture_buffers[m_current_buffer] replaceRegion:region + mipmapLevel:0 withBytes:m_draw_buffer bytesPerRow:bytesPerRow]; + + // Create a new command buffer for each render pass to the current drawable + id commandBuffer = [g_command_queue commandBuffer]; + commandBuffer.label = @"minifb_command_buffer"; + + // Add completion hander which signals _inFlightSemaphore when Metal and the GPU has fully + // finished processing the commands we're encoding this frame. This indicates when the + // dynamic buffers filled with our vertices, that we're writing to this frame, will no longer + // be needed by Metal and the GPU, meaning we can overwrite the buffer contents without + // corrupting the rendering. + __block dispatch_semaphore_t block_sema = m_semaphore; + [commandBuffer addCompletedHandler:^(id buffer) + { + (void)buffer; + dispatch_semaphore_signal(block_sema); + }]; + + MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor; + + if (renderPassDescriptor != nil) + { + renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0); + + // Create a render command encoder so we can render into something + id renderEncoder = + [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; + renderEncoder.label = @"minifb_command_encoder"; + + // Set render command encoder state + [renderEncoder setRenderPipelineState:g_pipeline_state]; + + [renderEncoder setVertexBytes:gVertices + length:sizeof(gVertices) + atIndex:0]; + + [renderEncoder setFragmentTexture:m_texture_buffers[m_current_buffer] atIndex:0]; + + // Draw the vertices of our quads + // [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle + // vertexStart:0 + // vertexCount:3]; + [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip + vertexStart:0 + vertexCount:4]; + + // We're done encoding commands + [renderEncoder endEncoding]; + + // Schedule a present once the framebuffer is complete using the current drawable + [commandBuffer presentDrawable:view.currentDrawable]; + } + + // Finalize rendering here & push the command buffer to the GPU + [commandBuffer commit]; +} +@end +#endif + +@implementation OSXWindowFrameView + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#if defined(USE_METAL_API) +-(void)updateTrackingAreas +{ + if(trackingArea != nil) { + [self removeTrackingArea:trackingArea]; + [trackingArea release]; + } + + int opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways); + trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds] + options:opts + owner:self + userInfo:nil]; + [self addTrackingArea:trackingArea]; +} +#else + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (NSRect)resizeRect +{ + const CGFloat resizeBoxSize = 16.0; + const CGFloat contentViewPadding = 5.5; + + NSRect contentViewRect = [[self window] contentRectForFrameRect:[[self window] frame]]; + NSRect resizeRect = NSMakeRect( + NSMaxX(contentViewRect) + contentViewPadding, + NSMinY(contentViewRect) - resizeBoxSize - contentViewPadding, + resizeBoxSize, + resizeBoxSize); + + return resizeRect; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)drawRect:(NSRect)rect +{ + (void)rect; + + if (!g_window_data.window || !g_window_data.draw_buffer) + return; + + CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; + + CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); + CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, g_window_data.draw_buffer, g_window_data.buffer_width * g_window_data.buffer_height * 4, NULL); + + CGImageRef img = CGImageCreate(g_window_data.buffer_width, g_window_data.buffer_height, 8, 32, g_window_data.buffer_width * 4, space, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little, + provider, NULL, false, kCGRenderingIntentDefault); + + const CGFloat components[] = {0.0f, 0.0f, 0.0f, 1.0f}; + const CGColorRef black = CGColorCreate(space, components); + + CGColorSpaceRelease(space); + CGDataProviderRelease(provider); + + if(g_window_data.dst_offset_x != 0 || g_window_data.dst_offset_y != 0 || g_window_data.dst_width != g_window_data.window_width || g_window_data.dst_height != g_window_data.window_height) { + CGContextSetFillColorWithColor(context, black); + CGContextFillRect(context, CGRectMake(0, 0, g_window_data.window_width, g_window_data.window_height)); + } + + CGContextDrawImage(context, CGRectMake(g_window_data.dst_offset_x, g_window_data.dst_offset_y, g_window_data.dst_width, g_window_data.dst_height), img); + + CGImageRelease(img); +} +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (BOOL)acceptsFirstMouse:(NSEvent *)event +{ + (void)event; + return YES; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)mouseDown:(NSEvent*)event +{ + (void)event; + kCall(g_mouse_btn_func, MOUSE_BTN_1, g_window_data.mod_keys, true); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)mouseUp:(NSEvent*)event +{ + (void)event; + kCall(g_mouse_btn_func, MOUSE_BTN_1, g_window_data.mod_keys, false); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)rightMouseDown:(NSEvent*)event +{ + (void)event; + kCall(g_mouse_btn_func, MOUSE_BTN_2, g_window_data.mod_keys, true); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)rightMouseUp:(NSEvent*)event +{ + (void)event; + kCall(g_mouse_btn_func, MOUSE_BTN_1, g_window_data.mod_keys, false); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)otherMouseDown:(NSEvent *)event +{ + (void)event; + kCall(g_mouse_btn_func, [event buttonNumber], g_window_data.mod_keys, true); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)otherMouseUp:(NSEvent *)event +{ + (void)event; + kCall(g_mouse_btn_func, [event buttonNumber], g_window_data.mod_keys, false); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)scrollWheel:(NSEvent *)event +{ + kCall(g_mouse_wheel_func, g_window_data.mod_keys, [event deltaX], [event deltaY]); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)mouseDragged:(NSEvent *)event +{ + [self mouseMoved:event]; +} + +- (void)rightMouseDragged:(NSEvent *)event +{ + [self mouseMoved:event]; +} + +- (void)otherMouseDragged:(NSEvent *)event +{ + [self mouseMoved:event]; +} + +- (void)mouseMoved:(NSEvent *)event +{ + NSPoint point = [event locationInWindow]; + //NSPoint localPoint = [self convertPoint:point fromView:nil]; + kCall(g_mouse_move_func, point.x, point.y); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)mouseExited:(NSEvent *)event +{ + (void)event; + //printf("mouse exit\n"); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)mouseEntered:(NSEvent *)event +{ + (void)event; + //printf("mouse enter\n"); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (BOOL)canBecomeKeyView +{ + return YES; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (NSView *)nextValidKeyView +{ + return self; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (NSView *)previousValidKeyView +{ + return self; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)viewDidMoveToWindow +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [super dealloc]; +} + +@end + diff --git a/src/wayland/WaylandMiniFB.c b/src/wayland/WaylandMiniFB.c index cbd7bf4..73db7b5 100644 --- a/src/wayland/WaylandMiniFB.c +++ b/src/wayland/WaylandMiniFB.c @@ -1,6 +1,10 @@ #include +#include "MiniFB_internal.h" +#include "MiniFB_enums.h" +#include "WaylandWindowData.h" #include +#include #include #include @@ -10,276 +14,802 @@ #include #include +#include #include -static struct wl +SWindowData g_window_data = { 0 }; + +static void +destroy(void) { - struct wl_display *display; - struct wl_registry *registry; - struct wl_compositor *compositor; - struct wl_shell *shell; - struct wl_seat *seat; - struct wl_keyboard *keyboard; - struct wl_shm *shm; - struct wl_shm_pool *shm_pool; - struct wl_surface *surface; - struct wl_shell_surface *shell_surface; + if (! g_window_data.display) + return; - uint32_t seat_version; - uint32_t shm_format; - uint32_t width; - uint32_t height; - uint32_t stride; - uint32_t *shm_ptr; - struct wl_buffer *buffer; - int should_close; -} wl; +#define KILL(NAME) \ + do \ + { \ + if (g_window_data.NAME) \ + wl_##NAME##_destroy(g_window_data.NAME); \ + } while (0); \ + g_window_data.NAME = 0x0; -static void destroy(void) -{ - if (! wl.display) - return; - -#define KILL(NAME) \ - do \ - { \ - if (wl.NAME) \ - wl_##NAME##_destroy(wl.NAME); \ - } while (0) - KILL(shell_surface); - KILL(shell); - KILL(surface); - KILL(buffer); - KILL(shm_pool); - KILL(shm); - KILL(compositor); - KILL(keyboard); - KILL(seat); - KILL(registry); + KILL(shell_surface); + KILL(shell); + KILL(surface); + //KILL(buffer); + if(g_window_data.draw_buffer) { + wl_buffer_destroy(g_window_data.draw_buffer); + g_window_data.draw_buffer = 0x0; + } + KILL(shm_pool); + KILL(shm); + KILL(compositor); + KILL(keyboard); + KILL(seat); + KILL(registry); #undef KILL - wl_display_disconnect(wl.display); - memset(&wl, 0, sizeof(wl)); + wl_display_disconnect(g_window_data.display); + memset(&g_window_data, 0, sizeof(SWindowData)); } -static void nop() {} - -#define NO_FUNC (void (*)()) nop - -static void keyboard_key(void *data, struct wl_keyboard *wl_keyboard, - uint32_t serial, uint32_t time, uint32_t key, - uint32_t state) +// This event provides a file descriptor to the client which can be memory-mapped +// to provide a keyboard mapping description. +// format: keymap format +// fd: keymap file descriptor +// size: keymap size, in bytes +static void +keyboard_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size) { - if (state == WL_KEYBOARD_KEY_STATE_RELEASED && key == KEY_ESC) - { - wl.should_close = 1; - } + kUnused(data); + kUnused(keyboard); + kUnused(format); + kUnused(fd); + kUnused(size); +} + +// Notification that this seat's keyboard focus is on a certain surface. +// serial: serial number of the enter event +// surface: surface gaining keyboard focus +// keys: the currently pressed keys +static void +keyboard_enter(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) +{ + kUnused(data); + kUnused(keyboard); + kUnused(serial); + kUnused(surface); + kUnused(keys); + kCall(g_active_func, true); +} + +// The leave notification is sent before the enter notification for the new focus. +// serial: serial number of the leave event +// surface: surface that lost keyboard focus +static void +keyboard_leave(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface) +{ + kUnused(data); + kUnused(keyboard); + kUnused(serial); + kUnused(surface); + kCall(g_active_func, false); +} + +// A key was pressed or released. The time argument is a timestamp with +// millisecond granularity, with an undefined base. +// serial: serial number of the key event +// time: timestamp with millisecond granularity +// key: key that produced the event +// state: physical state of the key +static void +keyboard_key(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) +{ + kUnused(data); + kUnused(keyboard); + kUnused(serial); + kUnused(time); + if(key < 512) { + Key kb_key = (Key) keycodes[key]; + bool is_pressed = (bool) (state == WL_KEYBOARD_KEY_STATE_PRESSED); + switch (kb_key) + { + case KB_KEY_LEFT_SHIFT: + case KB_KEY_RIGHT_SHIFT: + if(is_pressed) + g_window_data.mod_keys |= KB_MOD_SHIFT; + else + g_window_data.mod_keys &= ~KB_MOD_SHIFT; + break; + + case KB_KEY_LEFT_CONTROL: + case KB_KEY_RIGHT_CONTROL: + if(is_pressed) + g_window_data.mod_keys |= KB_MOD_CONTROL; + else + g_window_data.mod_keys &= ~KB_MOD_CONTROL; + break; + + case KB_KEY_LEFT_ALT: + case KB_KEY_RIGHT_ALT: + if(is_pressed) + g_window_data.mod_keys |= KB_MOD_ALT; + else + g_window_data.mod_keys &= ~KB_MOD_ALT; + break; + + case KB_KEY_LEFT_SUPER: + case KB_KEY_RIGHT_SUPER: + if(is_pressed) + g_window_data.mod_keys |= KB_MOD_SUPER; + else + g_window_data.mod_keys &= ~KB_MOD_SUPER; + break; + } + + kCall(g_keyboard_func, kb_key, (KeyMod)g_window_data.mod_keys, is_pressed); + } +} + +// Notifies clients that the modifier and/or group state has changed, +// and it should update its local state. +// serial: serial number of the modifiers event +// mods_depressed: depressed modifiers +// mods_latched: latched modifiers +// mods_locked: locked modifiers +// group: keyboard layout +static void +keyboard_modifiers(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) +{ + kUnused(data); + kUnused(keyboard); + kUnused(serial); + kUnused(mods_depressed); + kUnused(mods_latched); + kUnused(mods_locked); + kUnused(group); + // it is not easy to identify them here :( +} + +// Informs the client about the keyboard's repeat rate and delay. +// rate: the rate of repeating keys in characters per second +// delay: delay in milliseconds since key down until repeating starts +static void +keyboard_repeat_info(void *data, struct wl_keyboard *keyboard, int32_t rate, int32_t delay) +{ + kUnused(data); + kUnused(keyboard); + kUnused(rate); + kUnused(delay); } static const struct wl_keyboard_listener keyboard_listener = { - .keymap = NO_FUNC, - .enter = NO_FUNC, - .leave = NO_FUNC, - .key = keyboard_key, - .modifiers = NO_FUNC, - .repeat_info = NO_FUNC, + .keymap = keyboard_keymap, + .enter = keyboard_enter, + .leave = keyboard_leave, + .key = keyboard_key, + .modifiers = keyboard_modifiers, + .repeat_info = NULL, }; -static void seat_capabilities(void *data, struct wl_seat *seat, - enum wl_seat_capability caps) +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Notification that this seat's pointer is focused on a certain surface. +// +// When a seat's focus enters a surface, the pointer image is +// undefined and a client should respond to this event by setting +// an appropriate pointer image with the set_cursor request. +// +// serial: serial number of the enter event +// surface: surface entered by the pointer +// sx: surface-local x coordinate +// sy: surface-local y coordinate +static void +pointer_enter(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t sx, wl_fixed_t sy) { - if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !wl.keyboard) - { - wl.keyboard = wl_seat_get_keyboard(seat); - wl_keyboard_add_listener(wl.keyboard, &keyboard_listener, NULL); - } - else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && wl.keyboard) - { - wl_keyboard_destroy(wl.keyboard); - wl.keyboard = NULL; - } + kUnused(data); + //kUnused(pointer); + //kUnused(serial); + kUnused(surface); + kUnused(sx); + kUnused(sy); + struct wl_buffer *buffer; + struct wl_cursor_image *image; + + image = g_window_data.default_cursor->images[0]; + buffer = wl_cursor_image_get_buffer(image); + + wl_pointer_set_cursor(pointer, serial, g_window_data.cursor_surface, image->hotspot_x, image->hotspot_y); + wl_surface_attach(g_window_data.cursor_surface, buffer, 0, 0); + wl_surface_damage(g_window_data.cursor_surface, 0, 0, image->width, image->height); + wl_surface_commit(g_window_data.cursor_surface); + //fprintf(stderr, "Pointer entered surface %p at %d %d\n", surface, sx, sy); +} + +// Notification that this seat's pointer is no longer focused on a certain surface. +// +// The leave notification is sent before the enter notification for the new focus. +// +// serial: serial number of the leave event +// surface: surface left by the pointer +static void +pointer_leave(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface) +{ + kUnused(data); + kUnused(pointer); + kUnused(serial); + kUnused(surface); + //fprintf(stderr, "Pointer left surface %p\n", surface); +} + +// Notification of pointer location change. +// +// The arguments sx and sy are the location relative to the focused surface. +// +// time: timestamp with millisecond granularity +// sx: surface-local x coordinate +// sy: surface-local y coordinate +static void +pointer_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy) +{ + kUnused(data); + kUnused(pointer); + kUnused(time); + //printf("Pointer moved at %f %f\n", sx / 256.0f, sy / 256.0f); + kCall(g_mouse_move_func, sx >> 24, sy >> 24); +} + +// Mouse button click and release notifications. +// +// The location of the click is given by the last motion or enter +// event. The time argument is a timestamp with millisecond +// granularity, with an undefined base. +// +// The button is a button code as defined in the Linux kernel's +// linux/input-event-codes.h header file, e.g. BTN_LEFT. +// +// Any 16-bit button code value is reserved for future additions to +// the kernel's event code list. All other button codes above +// 0xFFFF are currently undefined but may be used in future +// versions of this protocol. +// +// serial: serial number of the button event +// time: timestamp with millisecond granularity +// button: button that produced the event +// state: physical state of the button +static void +pointer_button(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) +{ + kUnused(data); + kUnused(pointer); + kUnused(serial); + kUnused(time); + //printf("Pointer button '%d'(%d)\n", button, state); + kCall(g_mouse_btn_func, button - BTN_MOUSE + 1, g_window_data.mod_keys, state == 1); +} + +// Scroll and other axis notifications. +// +// For scroll events (vertical and horizontal scroll axes), the +// value parameter is the length of a vector along the specified +// axis in a coordinate space identical to those of motion events, +// representing a relative movement along the specified axis. +// +// For devices that support movements non-parallel to axes multiple +// axis events will be emitted. +// +// When applicable, for example for touch pads, the server can +// choose to emit scroll events where the motion vector is +// equivalent to a motion event vector. +// +// When applicable, a client can transform its content relative to +// the scroll distance. +// +// time: timestamp with millisecond granularity +// axis: axis type +// value: length of vector in surface-local coordinate space +static void +pointer_axis(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value) +{ + kUnused(data); + kUnused(pointer); + kUnused(time); + kUnused(axis); + //printf("Pointer handle axis: axis: %d (0x%x)\n", axis, value); + if(axis == 0) { + kCall(g_mouse_wheel_func, g_window_data.mod_keys, 0.0f, -(value / 256.0f)); + } + else if(axis == 1) { + kCall(g_mouse_wheel_func, g_window_data.mod_keys, -(value / 256.0f), 0.0f); + } +} + +static void +frame(void *data, struct wl_pointer *pointer) { + kUnused(data); + kUnused(pointer); +} + +static void +axis_source(void *data, struct wl_pointer *pointer, uint32_t axis_source) { + kUnused(data); + kUnused(pointer); + kUnused(axis_source); +} + +static void +axis_stop(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis) { + kUnused(data); + kUnused(pointer); + kUnused(time); + kUnused(axis); +} + +static void +axis_discrete(void *data, struct wl_pointer *pointer, uint32_t axis, int32_t discrete) { + kUnused(data); + kUnused(pointer); + kUnused(axis); + kUnused(discrete); +} + +static const struct wl_pointer_listener pointer_listener = { + .enter = pointer_enter, + .leave = pointer_leave, + .motion = pointer_motion, + .button = pointer_button, + .axis = pointer_axis, + .frame = NULL, + .axis_source = NULL, + .axis_stop = NULL, + .axis_discrete = NULL, +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static void +seat_capabilities(void *data, struct wl_seat *seat, enum wl_seat_capability caps) +{ + kUnused(data); + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !g_window_data.keyboard) + { + g_window_data.keyboard = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(g_window_data.keyboard, &keyboard_listener, NULL); + } + else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && g_window_data.keyboard) + { + wl_keyboard_destroy(g_window_data.keyboard); + g_window_data.keyboard = NULL; + } + + if ((caps & WL_SEAT_CAPABILITY_POINTER) && !g_window_data.pointer) + { + g_window_data.pointer = wl_seat_get_pointer(seat); + wl_pointer_add_listener(g_window_data.pointer, &pointer_listener, NULL); + } + else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && g_window_data.pointer) + { + wl_pointer_destroy(g_window_data.pointer); + g_window_data.pointer = NULL; + } +} + +static void +seat_name(void *data, struct wl_seat *seat, const char *name) { + kUnused(data); + kUnused(seat); + printf("Seat '%s'n", name); } static const struct wl_seat_listener seat_listener = { - .capabilities = seat_capabilities, .name = NO_FUNC, + .capabilities = seat_capabilities, + .name = NULL, }; -static void shm_format(void *data, struct wl_shm *shm, uint32_t format) -{ - if (wl.shm_format == -1u) - { - switch (format) - { - // We could do RGBA, but that would not be what is expected from minifb... - /* case WL_SHM_FORMAT_ARGB8888: */ - case WL_SHM_FORMAT_XRGB8888: - wl.shm_format = format; - break; +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - default: - break; - } - } +// pixel format description +// +// Informs the client about a valid pixel format that can be used +// for buffers. Known formats include argb8888 and xrgb8888. +// +// format: buffer pixel format +static void +shm_format(void *data, struct wl_shm *shm, uint32_t format) +{ + kUnused(data); + kUnused(shm); + if (g_window_data.shm_format == -1u) + { + switch (format) + { + // We could do RGBA, but that would not be what is expected from minifb... + // case WL_SHM_FORMAT_ARGB8888: + case WL_SHM_FORMAT_XRGB8888: + g_window_data.shm_format = format; + break; + + default: + break; + } + } } -static const struct wl_shm_listener shm_listener = {.format = shm_format}; +static const struct wl_shm_listener shm_listener = { + .format = shm_format +}; -static void registry_global(void *data, struct wl_registry *registry, - uint32_t id, char const *iface, uint32_t version) +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static void +registry_global(void *data, struct wl_registry *registry, uint32_t id, char const *iface, uint32_t version) { - if (strcmp(iface, "wl_compositor") == 0) - { - wl.compositor = - wl_registry_bind(registry, id, &wl_compositor_interface, 1); - } - else if (strcmp(iface, "wl_shm") == 0) - { - wl.shm = wl_registry_bind(registry, id, &wl_shm_interface, 1); - if (wl.shm) - wl_shm_add_listener(wl.shm, &shm_listener, NULL); - } - else if (strcmp(iface, "wl_shell") == 0) - { - wl.shell = wl_registry_bind(registry, id, &wl_shell_interface, 1); - } - else if (strcmp(iface, "wl_seat") == 0) - { - wl.seat = wl_registry_bind(registry, id, &wl_seat_interface, 1); - if (wl.seat) - { - wl_seat_add_listener(wl.seat, &seat_listener, NULL); - } - } + kUnused(data); + kUnused(version); + if (strcmp(iface, "wl_compositor") == 0) + { + g_window_data.compositor = (struct wl_compositor *) wl_registry_bind(registry, id, &wl_compositor_interface, 1); + } + else if (strcmp(iface, "wl_shm") == 0) + { + g_window_data.shm = (struct wl_shm *) wl_registry_bind(registry, id, &wl_shm_interface, 1); + if (g_window_data.shm) { + wl_shm_add_listener(g_window_data.shm, &shm_listener, NULL); + g_window_data.cursor_theme = wl_cursor_theme_load(NULL, 32, g_window_data.shm); + g_window_data.default_cursor = wl_cursor_theme_get_cursor(g_window_data.cursor_theme, "left_ptr"); + } + } + else if (strcmp(iface, "wl_shell") == 0) + { + g_window_data.shell = (struct wl_shell *) wl_registry_bind(registry, id, &wl_shell_interface, 1); + } + else if (strcmp(iface, "wl_seat") == 0) + { + g_window_data.seat = (struct wl_seat *) wl_registry_bind(registry, id, &wl_seat_interface, 1); + if (g_window_data.seat) + { + wl_seat_add_listener(g_window_data.seat, &seat_listener, NULL); + } + } } static const struct wl_registry_listener registry_listener = { - .global = registry_global, .global_remove = NO_FUNC, + .global = registry_global, + .global_remove = NULL, }; -int mfb_open(const char *title, int width, int height) +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int +mfb_open_ex(const char* title, int width, int height, int flags) { + // TODO: Not yet + kUnused(flags); + return mfb_open(title, width, height); +} + +int +mfb_open(const char *title, int width, int height) { - int fd = -1; + int fd = -1; - wl.display = wl_display_connect(NULL); - if (!wl.display) - return -1; - wl.registry = wl_display_get_registry(wl.display); - wl_registry_add_listener(wl.registry, ®istry_listener, NULL); - if (wl_display_roundtrip(wl.display) == -1 || - wl_display_roundtrip(wl.display) == -1) - { - return -1; - } + g_window_data.shm_format = -1u; - // did not get a format we want... meh - if (wl.shm_format == -1) - goto out; - if (!wl.compositor) - goto out; + g_window_data.display = wl_display_connect(NULL); + if (!g_window_data.display) + return -1; + g_window_data.registry = wl_display_get_registry(g_window_data.display); + wl_registry_add_listener(g_window_data.registry, ®istry_listener, NULL); - char const *xdg_rt_dir = getenv("XDG_RUNTIME_DIR"); - char shmfile[PATH_MAX]; - int ret = snprintf(shmfile, sizeof(shmfile), "%s/WaylandMiniFB-SHM-XXXXXX", - xdg_rt_dir); - if (ret >= sizeof(shmfile)) - goto out; + init_keycodes(); - fd = mkstemp(shmfile); - if (fd == -1) - goto out; - unlink(shmfile); + if (wl_display_dispatch(g_window_data.display) == -1 || wl_display_roundtrip(g_window_data.display) == -1) + { + return -1; + } - uint32_t length = sizeof(uint32_t) * width * height; + // did not get a format we want... meh + if (g_window_data.shm_format == -1u) + goto out; + if (!g_window_data.compositor) + goto out; - if (ftruncate(fd, length) == -1) - goto out; + char const *xdg_rt_dir = getenv("XDG_RUNTIME_DIR"); + char shmfile[PATH_MAX]; + uint32_t ret = snprintf(shmfile, sizeof(shmfile), "%s/WaylandMiniFB-SHM-XXXXXX", xdg_rt_dir); + if (ret >= sizeof(shmfile)) + goto out; - wl.shm_ptr = mmap(NULL, length, PROT_WRITE, MAP_SHARED, fd, 0); - if (wl.shm_ptr == MAP_FAILED) - goto out; + fd = mkstemp(shmfile); + if (fd == -1) + goto out; + unlink(shmfile); - wl.width = width; - wl.height = height; - wl.stride = width * sizeof(uint32_t); - wl.shm_pool = wl_shm_create_pool(wl.shm, fd, length); - wl.buffer = wl_shm_pool_create_buffer(wl.shm_pool, 0, wl.width, wl.height, - wl.stride, wl.shm_format); + uint32_t length = sizeof(uint32_t) * width * height; - close(fd); - fd = -1; + if (ftruncate(fd, length) == -1) + goto out; - wl.surface = wl_compositor_create_surface(wl.compositor); - if (!wl.surface) - goto out; + g_window_data.shm_ptr = (uint32_t *) mmap(NULL, length, PROT_WRITE, MAP_SHARED, fd, 0); + if (g_window_data.shm_ptr == MAP_FAILED) + goto out; - // There should always be a shell, right? - if (wl.shell) - { - wl.shell_surface = wl_shell_get_shell_surface(wl.shell, wl.surface); - if (!wl.shell_surface) - goto out; + g_window_data.window_width = width; + g_window_data.window_height = height; + g_window_data.buffer_width = width; + g_window_data.buffer_height = height; + g_window_data.buffer_stride = width * sizeof(uint32_t); + g_window_data.dst_offset_x = 0; + g_window_data.dst_offset_y = 0; + g_window_data.dst_width = width; + g_window_data.dst_height = height; - wl_shell_surface_set_title(wl.shell_surface, title); - wl_shell_surface_set_toplevel(wl.shell_surface); - } + g_window_data.shm_pool = wl_shm_create_pool(g_window_data.shm, fd, length); + g_window_data.draw_buffer = wl_shm_pool_create_buffer(g_window_data.shm_pool, 0, + g_window_data.buffer_width, g_window_data.buffer_height, + g_window_data.buffer_stride, g_window_data.shm_format); - wl_surface_attach(wl.surface, wl.buffer, 0, 0); - wl_surface_damage(wl.surface, 0, 0, width, height); - wl_surface_commit(wl.surface); + close(fd); + fd = -1; - return 1; + g_window_data.surface = wl_compositor_create_surface(g_window_data.compositor); + if (!g_window_data.surface) + goto out; + + g_window_data.cursor_surface = wl_compositor_create_surface(g_window_data.compositor); + + // There should always be a shell, right? + if (g_window_data.shell) + { + g_window_data.shell_surface = wl_shell_get_shell_surface(g_window_data.shell, g_window_data.surface); + if (!g_window_data.shell_surface) + goto out; + + wl_shell_surface_set_title(g_window_data.shell_surface, title); + wl_shell_surface_set_toplevel(g_window_data.shell_surface); + } + + wl_surface_attach(g_window_data.surface, g_window_data.draw_buffer, g_window_data.dst_offset_x, g_window_data.dst_offset_y); + wl_surface_damage(g_window_data.surface, g_window_data.dst_offset_x, g_window_data.dst_offset_y, g_window_data.dst_width, g_window_data.dst_height); + wl_surface_commit(g_window_data.surface); + + if (g_keyboard_func == 0x0) { + mfb_keyboard_callback(keyboard_default); + } + + printf("Window created using Wayland API\n"); + + return 1; out: - close(fd); - destroy(); - return 0; -} - -static void frame_done(void *data, struct wl_callback *callback, - uint32_t cookie) -{ - wl_callback_destroy(callback); - *(uint32_t *)data = 1; -} - -static const struct wl_callback_listener frame_listener = { - .done = frame_done, -}; - -int mfb_update(void *buffer) -{ - uint32_t done = 0; - - if (!wl.display || wl_display_get_error(wl.display) != 0) - return -1; - - if (wl.should_close) - return -1; - - // update shm buffer - memcpy(wl.shm_ptr, buffer, wl.stride * wl.height); - - wl_surface_attach(wl.surface, wl.buffer, 0, 0); - wl_surface_damage(wl.surface, 0, 0, wl.width, wl.height); - - struct wl_callback *frame = wl_surface_frame(wl.surface); - if (!frame) - return -1; - - wl_callback_add_listener(frame, &frame_listener, &done); - - wl_surface_commit(wl.surface); - - while (!done) - if (wl_display_dispatch(wl.display) == -1) - { - wl_callback_destroy(frame); - return -1; - } - - return 0; + close(fd); + destroy(); + return 0; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void mfb_close(void) { destroy(); } +void keyboard_default(void *user_data, Key key, KeyMod mod, bool isPressed) { + kUnused(user_data); + kUnused(mod); + kUnused(isPressed); + if (key == KB_KEY_ESCAPE) + g_window_data.close = true; +} + +// done event +// +// Notify the client when the related request is done. +// +// callback_data: request-specific data for the callback +static void +frame_done(void *data, struct wl_callback *callback, uint32_t cookie) +{ + kUnused(cookie); + wl_callback_destroy(callback); + *(uint32_t *)data = 1; +} + +static const struct wl_callback_listener frame_listener = { + .done = frame_done, +}; + +int +mfb_update(void *buffer) +{ + uint32_t done = 0; + + if (!g_window_data.display || wl_display_get_error(g_window_data.display) != 0) + return -1; + + if (g_window_data.close == true) + return -1; + + // update shm buffer + memcpy(g_window_data.shm_ptr, buffer, g_window_data.buffer_stride * g_window_data.buffer_height); + + wl_surface_attach(g_window_data.surface, g_window_data.draw_buffer, g_window_data.dst_offset_x, g_window_data.dst_offset_y); + wl_surface_damage(g_window_data.surface, g_window_data.dst_offset_x, g_window_data.dst_offset_y, g_window_data.dst_width, g_window_data.dst_height); + + struct wl_callback *frame = wl_surface_frame(g_window_data.surface); + if (!frame) + return -1; + + wl_callback_add_listener(frame, &frame_listener, &done); + + wl_surface_commit(g_window_data.surface); + + while (!done && g_window_data.close == false) { + if (wl_display_dispatch(g_window_data.display) == -1 || wl_display_roundtrip(g_window_data.display) == -1) + { + wl_callback_destroy(frame); + return -1; + } + } + if(g_window_data.close == true) { + destroy(); + } + + //static int counter = 0; + //printf("update!: %d\n", counter++); + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void +mfb_close(void) { + g_window_data.close = true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +extern short int keycodes[512]; + +void +init_keycodes(void) +{ + // Clear keys + for (size_t i = 0; i < sizeof(keycodes) / sizeof(keycodes[0]); ++i) + keycodes[i] = 0; + + keycodes[KEY_GRAVE] = KB_KEY_GRAVE_ACCENT; + keycodes[KEY_1] = KB_KEY_1; + keycodes[KEY_2] = KB_KEY_2; + keycodes[KEY_3] = KB_KEY_3; + keycodes[KEY_4] = KB_KEY_4; + keycodes[KEY_5] = KB_KEY_5; + keycodes[KEY_6] = KB_KEY_6; + keycodes[KEY_7] = KB_KEY_7; + keycodes[KEY_8] = KB_KEY_8; + keycodes[KEY_9] = KB_KEY_9; + keycodes[KEY_0] = KB_KEY_0; + keycodes[KEY_SPACE] = KB_KEY_SPACE; + keycodes[KEY_MINUS] = KB_KEY_MINUS; + keycodes[KEY_EQUAL] = KB_KEY_EQUAL; + keycodes[KEY_Q] = KB_KEY_Q; + keycodes[KEY_W] = KB_KEY_W; + keycodes[KEY_E] = KB_KEY_E; + keycodes[KEY_R] = KB_KEY_R; + keycodes[KEY_T] = KB_KEY_T; + keycodes[KEY_Y] = KB_KEY_Y; + keycodes[KEY_U] = KB_KEY_U; + keycodes[KEY_I] = KB_KEY_I; + keycodes[KEY_O] = KB_KEY_O; + keycodes[KEY_P] = KB_KEY_P; + keycodes[KEY_LEFTBRACE] = KB_KEY_LEFT_BRACKET; + keycodes[KEY_RIGHTBRACE] = KB_KEY_RIGHT_BRACKET; + keycodes[KEY_A] = KB_KEY_A; + keycodes[KEY_S] = KB_KEY_S; + keycodes[KEY_D] = KB_KEY_D; + keycodes[KEY_F] = KB_KEY_F; + keycodes[KEY_G] = KB_KEY_G; + keycodes[KEY_H] = KB_KEY_H; + keycodes[KEY_J] = KB_KEY_J; + keycodes[KEY_K] = KB_KEY_K; + keycodes[KEY_L] = KB_KEY_L; + keycodes[KEY_SEMICOLON] = KB_KEY_SEMICOLON; + keycodes[KEY_APOSTROPHE] = KB_KEY_APOSTROPHE; + keycodes[KEY_Z] = KB_KEY_Z; + keycodes[KEY_X] = KB_KEY_X; + keycodes[KEY_C] = KB_KEY_C; + keycodes[KEY_V] = KB_KEY_V; + keycodes[KEY_B] = KB_KEY_B; + keycodes[KEY_N] = KB_KEY_N; + keycodes[KEY_M] = KB_KEY_M; + keycodes[KEY_COMMA] = KB_KEY_COMMA; + keycodes[KEY_DOT] = KB_KEY_PERIOD; + keycodes[KEY_SLASH] = KB_KEY_SLASH; + keycodes[KEY_BACKSLASH] = KB_KEY_BACKSLASH; + keycodes[KEY_ESC] = KB_KEY_ESCAPE; + keycodes[KEY_TAB] = KB_KEY_TAB; + keycodes[KEY_LEFTSHIFT] = KB_KEY_LEFT_SHIFT; + keycodes[KEY_RIGHTSHIFT] = KB_KEY_RIGHT_SHIFT; + keycodes[KEY_LEFTCTRL] = KB_KEY_LEFT_CONTROL; + keycodes[KEY_RIGHTCTRL] = KB_KEY_RIGHT_CONTROL; + keycodes[KEY_LEFTALT] = KB_KEY_LEFT_ALT; + keycodes[KEY_RIGHTALT] = KB_KEY_RIGHT_ALT; + keycodes[KEY_LEFTMETA] = KB_KEY_LEFT_SUPER; + keycodes[KEY_RIGHTMETA] = KB_KEY_RIGHT_SUPER; + keycodes[KEY_MENU] = KB_KEY_MENU; + keycodes[KEY_NUMLOCK] = KB_KEY_NUM_LOCK; + keycodes[KEY_CAPSLOCK] = KB_KEY_CAPS_LOCK; + keycodes[KEY_PRINT] = KB_KEY_PRINT_SCREEN; + keycodes[KEY_SCROLLLOCK] = KB_KEY_SCROLL_LOCK; + keycodes[KEY_PAUSE] = KB_KEY_PAUSE; + keycodes[KEY_DELETE] = KB_KEY_DELETE; + keycodes[KEY_BACKSPACE] = KB_KEY_BACKSPACE; + keycodes[KEY_ENTER] = KB_KEY_ENTER; + keycodes[KEY_HOME] = KB_KEY_HOME; + keycodes[KEY_END] = KB_KEY_END; + keycodes[KEY_PAGEUP] = KB_KEY_PAGE_UP; + keycodes[KEY_PAGEDOWN] = KB_KEY_PAGE_DOWN; + keycodes[KEY_INSERT] = KB_KEY_INSERT; + keycodes[KEY_LEFT] = KB_KEY_LEFT; + keycodes[KEY_RIGHT] = KB_KEY_RIGHT; + keycodes[KEY_DOWN] = KB_KEY_DOWN; + keycodes[KEY_UP] = KB_KEY_UP; + keycodes[KEY_F1] = KB_KEY_F1; + keycodes[KEY_F2] = KB_KEY_F2; + keycodes[KEY_F3] = KB_KEY_F3; + keycodes[KEY_F4] = KB_KEY_F4; + keycodes[KEY_F5] = KB_KEY_F5; + keycodes[KEY_F6] = KB_KEY_F6; + keycodes[KEY_F7] = KB_KEY_F7; + keycodes[KEY_F8] = KB_KEY_F8; + keycodes[KEY_F9] = KB_KEY_F9; + keycodes[KEY_F10] = KB_KEY_F10; + keycodes[KEY_F11] = KB_KEY_F11; + keycodes[KEY_F12] = KB_KEY_F12; + keycodes[KEY_F13] = KB_KEY_F13; + keycodes[KEY_F14] = KB_KEY_F14; + keycodes[KEY_F15] = KB_KEY_F15; + keycodes[KEY_F16] = KB_KEY_F16; + keycodes[KEY_F17] = KB_KEY_F17; + keycodes[KEY_F18] = KB_KEY_F18; + keycodes[KEY_F19] = KB_KEY_F19; + keycodes[KEY_F20] = KB_KEY_F20; + keycodes[KEY_F21] = KB_KEY_F21; + keycodes[KEY_F22] = KB_KEY_F22; + keycodes[KEY_F23] = KB_KEY_F23; + keycodes[KEY_F24] = KB_KEY_F24; + keycodes[KEY_KPSLASH] = KB_KEY_KP_DIVIDE; + keycodes[KEY_KPDOT] = KB_KEY_KP_MULTIPLY; + keycodes[KEY_KPMINUS] = KB_KEY_KP_SUBTRACT; + keycodes[KEY_KPPLUS] = KB_KEY_KP_ADD; + keycodes[KEY_KP0] = KB_KEY_KP_0; + keycodes[KEY_KP1] = KB_KEY_KP_1; + keycodes[KEY_KP2] = KB_KEY_KP_2; + keycodes[KEY_KP3] = KB_KEY_KP_3; + keycodes[KEY_KP4] = KB_KEY_KP_4; + keycodes[KEY_KP5] = KB_KEY_KP_5; + keycodes[KEY_KP6] = KB_KEY_KP_6; + keycodes[KEY_KP7] = KB_KEY_KP_7; + keycodes[KEY_KP8] = KB_KEY_KP_8; + keycodes[KEY_KP9] = KB_KEY_KP_9; + keycodes[KEY_KPCOMMA] = KB_KEY_KP_DECIMAL; + keycodes[KEY_KPEQUAL] = KB_KEY_KP_EQUAL; + keycodes[KEY_KPENTER] = KB_KEY_KP_ENTER; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool +mfb_set_viewport(unsigned offset_x, unsigned offset_y, unsigned width, unsigned height) { + + if(offset_x + width > g_window_data.window_width) { + return false; + } + if(offset_y + height > g_window_data.window_height) { + return false; + } + + // TODO: Not yet + // g_window_data.dst_offset_x = offset_x; + // g_window_data.dst_offset_y = offset_y; + // g_window_data.dst_width = width; + // g_window_data.dst_height = height; + + return false; +} + diff --git a/src/wayland/WaylandWindowData.h b/src/wayland/WaylandWindowData.h new file mode 100644 index 0000000..bfd4c7d --- /dev/null +++ b/src/wayland/WaylandWindowData.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include + +struct wl_display; +struct wl_registry; +struct wl_compositor; +struct wl_shell; +struct wl_seat; +struct wl_keyboard; +struct wl_pointer; +struct wl_callback; +struct wl_shm; +struct wl_shm_pool; +struct wl_surface; +struct wl_shell_surface; +struct wl_buffer; + +typedef struct +{ + struct wl_display *display; + struct wl_registry *registry; + struct wl_compositor *compositor; + struct wl_shell *shell; + struct wl_seat *seat; + struct wl_keyboard *keyboard; + + struct wl_pointer *pointer; + struct wl_cursor_theme *cursor_theme; + struct wl_cursor *default_cursor; + struct wl_surface *cursor_surface; + + struct wl_shm *shm; + struct wl_shm_pool *shm_pool; + struct wl_surface *surface; + struct wl_shell_surface *shell_surface; + + uint32_t seat_version; + uint32_t shm_format; + uint32_t *shm_ptr; + + uint32_t window_width; + uint32_t window_height; + uint32_t dst_offset_x; + uint32_t dst_offset_y; + uint32_t dst_width; + uint32_t dst_height; + + struct wl_buffer *draw_buffer; + uint32_t buffer_width; + uint32_t buffer_height; + uint32_t buffer_stride; + + uint32_t mod_keys; + bool close; +} SWindowData; diff --git a/src/windows/WinMiniFB.c b/src/windows/WinMiniFB.c index fdc1a03..1a2059a 100644 --- a/src/windows/WinMiniFB.c +++ b/src/windows/WinMiniFB.c @@ -1,146 +1,571 @@ -#include "MiniFB.h" - -#define WIN32_LEAN_AND_MEAN -#include +#include +#include +#include "WinWindowData.h" #include /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -static WNDCLASS s_wc; -static HWND s_wnd; -static int s_close = 0; -static int s_width; -static int s_height; -static HDC s_hdc; -static void* s_buffer; -static BITMAPINFO* s_bitmapInfo; +SWindowData g_window_data = { 0 }; + +long s_window_style = WS_POPUP | WS_SYSMENU | WS_CAPTION; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +uint32_t translate_mod(); +Key translate_key(unsigned int wParam, unsigned long lParam); + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { - LRESULT res = 0; + LRESULT res = 0; - switch (message) - { - case WM_PAINT: - { - if (s_buffer) - { - StretchDIBits(s_hdc, 0, 0, s_width, s_height, 0, 0, s_width, s_height, s_buffer, - s_bitmapInfo, DIB_RGB_COLORS, SRCCOPY); + switch (message) + { + case WM_PAINT: + { + if (g_window_data.draw_buffer) + { + if (g_window_data.dst_offset_x > 0) { + BitBlt(g_window_data.s_hdc, 0, g_window_data.dst_offset_y, g_window_data.dst_offset_x, g_window_data.dst_height, 0, 0, 0, BLACKNESS); + } + if (g_window_data.dst_offset_y > 0) { + BitBlt(g_window_data.s_hdc, 0, 0, g_window_data.window_width, g_window_data.dst_offset_y, 0, 0, 0, BLACKNESS); + } + uint32_t offsetY = g_window_data.dst_offset_y + g_window_data.dst_height; + if (offsetY < g_window_data.window_height) { + BitBlt(g_window_data.s_hdc, 0, offsetY, g_window_data.window_width, g_window_data.window_height-offsetY, 0, 0, 0, BLACKNESS); + } + uint32_t offsetX = g_window_data.dst_offset_x + g_window_data.dst_width; + if (offsetX < g_window_data.window_width) { + BitBlt(g_window_data.s_hdc, offsetX, g_window_data.dst_offset_y, g_window_data.window_width-offsetX, g_window_data.dst_height, 0, 0, 0, BLACKNESS); + } - ValidateRect(hWnd, NULL); - } + StretchDIBits(g_window_data.s_hdc, g_window_data.dst_offset_x, g_window_data.dst_offset_y, g_window_data.dst_width, g_window_data.dst_height, 0, 0, g_window_data.buffer_width, g_window_data.buffer_height, g_window_data.draw_buffer, + g_window_data.s_bitmapInfo, DIB_RGB_COLORS, SRCCOPY); - break; - } + ValidateRect(hWnd, NULL); + } - case WM_KEYDOWN: - { - if ((wParam&0xFF) == 27) - s_close = 1; + break; + } - break; - } + case WM_DESTROY: + case WM_CLOSE: + { + g_window_data.close = true; + break; + } - case WM_CLOSE: - { - s_close = 1; - break; - } + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + case WM_KEYUP: + case WM_SYSKEYUP: + { + Key kb_key = translate_key((unsigned int)wParam, (unsigned long)lParam); + int is_pressed = !((lParam >> 31) & 1); + g_window_data.mod_keys = translate_mod(); - default: - { - res = DefWindowProc(hWnd, message, wParam, lParam); - } - } + if (kb_key == KB_KEY_UNKNOWN) + return FALSE; - return res; + kCall(g_keyboard_func, kb_key, g_window_data.mod_keys, is_pressed); + break; + } + + case WM_CHAR: + case WM_SYSCHAR: + case WM_UNICHAR: + { + + if(message == WM_UNICHAR && wParam == UNICODE_NOCHAR) { + // WM_UNICHAR is not sent by Windows, but is sent by some third-party input method engine + // Returning TRUE here announces support for this message + return TRUE; + } + + kCall(g_char_input_func, wParam); + break; + } + + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: + case WM_XBUTTONUP: + case WM_LBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_RBUTTONDOWN: + case WM_RBUTTONDBLCLK: + case WM_MBUTTONDOWN: + case WM_MBUTTONDBLCLK: + case WM_XBUTTONDOWN: + case WM_XBUTTONDBLCLK: + { + MouseButton button = MOUSE_BTN_0; + g_window_data.mod_keys = translate_mod(); + int is_pressed = 0; + switch (message) { + case WM_LBUTTONDOWN: + is_pressed = 1; + case WM_LBUTTONUP: + button = MOUSE_BTN_1; + break; + case WM_RBUTTONDOWN: + is_pressed = 1; + case WM_RBUTTONUP: + button = MOUSE_BTN_2; + break; + case WM_MBUTTONDOWN: + is_pressed = 1; + case WM_MBUTTONUP: + button = MOUSE_BTN_3; + break; + + default: + button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? MOUSE_BTN_5 : MOUSE_BTN_6); + if (message == WM_XBUTTONDOWN) { + is_pressed = 1; + } + } + kCall(g_mouse_btn_func, button, g_window_data.mod_keys, is_pressed); + break; + } + + case WM_MOUSEWHEEL: + kCall(g_mouse_wheel_func, translate_mod(), 0.0f, (SHORT)HIWORD(wParam) / (float)WHEEL_DELTA); + break; + + case WM_MOUSEHWHEEL: + // This message is only sent on Windows Vista and later + // NOTE: The X-axis is inverted for consistency with macOS and X11 + kCall(g_mouse_wheel_func, translate_mod(), -((SHORT)HIWORD(wParam) / (float)WHEEL_DELTA), 0.0f); + break; + + case WM_MOUSEMOVE: + if(g_window_data.s_mouse_inside == false) { + g_window_data.s_mouse_inside = true; + TRACKMOUSEEVENT tme; + ZeroMemory(&tme, sizeof(tme)); + tme.cbSize = sizeof(tme); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = hWnd; + TrackMouseEvent(&tme); + } + kCall(g_mouse_move_func, ((int)(short)LOWORD(lParam)), ((int)(short)HIWORD(lParam))); + break; + + case WM_MOUSELEAVE: + g_window_data.s_mouse_inside = false; + break; + + case WM_SIZE: + { + g_window_data.dst_offset_x = 0; + g_window_data.dst_offset_y = 0; + g_window_data.dst_width = LOWORD(lParam); + g_window_data.dst_height = HIWORD(lParam); + g_window_data.window_width = g_window_data.dst_width; + g_window_data.window_height = g_window_data.dst_height; + kCall(g_resize_func, g_window_data.dst_width, g_window_data.dst_height); + break; + } + + case WM_SETFOCUS: + kCall(g_active_func, true); + break; + + case WM_KILLFOCUS: + kCall(g_active_func, false); + break; + + default: + { + res = DefWindowProc(hWnd, message, wParam, lParam); + } + } + + return res; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -int mfb_open(const char* title, int width, int height) -{ - RECT rect = { 0 }; +int mfb_open_ex(const char* title, int width, int height, int flags) { + RECT rect = { 0 }; + int x, y; - s_wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW; - s_wc.lpfnWndProc = WndProc; - s_wc.hCursor = LoadCursor(0, IDC_ARROW); - s_wc.lpszClassName = title; - RegisterClass(&s_wc); + init_keycodes(); - rect.right = width; - rect.bottom = height; + g_window_data.buffer_width = width; + g_window_data.buffer_height = height; - AdjustWindowRect(&rect, WS_POPUP | WS_SYSMENU | WS_CAPTION, 0); + s_window_style = WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX & ~WS_THICKFRAME; + if (flags & WF_FULLSCREEN) { + flags = WF_FULLSCREEN; // Remove all other flags + x = 0; + y = 0; + rect.right = GetSystemMetrics(SM_CXSCREEN); + rect.bottom = GetSystemMetrics(SM_CYSCREEN); + s_window_style = WS_POPUP & ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE | WS_SYSMENU); - rect.right -= rect.left; - rect.bottom -= rect.top; + DEVMODE settings; + EnumDisplaySettings(0, 0, &settings); + settings.dmPelsWidth = GetSystemMetrics(SM_CXSCREEN); + settings.dmPelsHeight = GetSystemMetrics(SM_CYSCREEN); + settings.dmBitsPerPel = 32; + settings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; - s_width = width; - s_height = height; + if (ChangeDisplaySettings(&settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { + flags = WF_FULLSCREEN_DESKTOP; + } + } - s_wnd = CreateWindowEx(0, - title, title, - WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX & ~WS_THICKFRAME, - CW_USEDEFAULT, CW_USEDEFAULT, - rect.right, rect.bottom, - 0, 0, 0, 0); + if (flags & WF_BORDERLESS) { + s_window_style = WS_POPUP; + } - if (!s_wnd) - return 0; + if (flags & WF_RESIZABLE) { + s_window_style |= WS_MAXIMIZEBOX | WS_SIZEBOX; + } - ShowWindow(s_wnd, SW_NORMAL); + if (flags & WF_FULLSCREEN_DESKTOP) { + s_window_style = WS_OVERLAPPEDWINDOW; - s_bitmapInfo = (BITMAPINFO*)calloc(1, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 3); - s_bitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - s_bitmapInfo->bmiHeader.biPlanes = 1; - s_bitmapInfo->bmiHeader.biBitCount = 32; - s_bitmapInfo->bmiHeader.biCompression = BI_BITFIELDS; - s_bitmapInfo->bmiHeader.biWidth = width; - s_bitmapInfo->bmiHeader.biHeight = -height; - s_bitmapInfo->bmiColors[0].rgbRed = 0xff; - s_bitmapInfo->bmiColors[1].rgbGreen = 0xff; - s_bitmapInfo->bmiColors[2].rgbBlue = 0xff; + width = GetSystemMetrics(SM_CXFULLSCREEN); + height = GetSystemMetrics(SM_CYFULLSCREEN); - s_hdc = GetDC(s_wnd); + rect.right = width; + rect.bottom = height; + AdjustWindowRect(&rect, s_window_style, 0); + if (rect.left < 0) { + width += rect.left * 2; + rect.right += rect.left; + rect.left = 0; + } + if (rect.bottom > height) { + height -= (rect.bottom - height); + rect.bottom += (rect.bottom - height); + rect.top = 0; + } + x = 0; + y = 0; + } + else if (!(flags & WF_FULLSCREEN)) { + rect.right = width; + rect.bottom = height; - return 1; + AdjustWindowRect(&rect, s_window_style, 0); + + rect.right -= rect.left; + rect.bottom -= rect.top; + + x = (GetSystemMetrics(SM_CXSCREEN) - rect.right) / 2; + y = (GetSystemMetrics(SM_CYSCREEN) - rect.bottom + rect.top) / 2; + } + + g_window_data.s_wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW; + g_window_data.s_wc.lpfnWndProc = WndProc; + g_window_data.s_wc.hCursor = LoadCursor(0, IDC_ARROW); + g_window_data.s_wc.lpszClassName = title; + RegisterClass(&g_window_data.s_wc); + + if (g_window_data.dst_width == 0) + g_window_data.dst_width = width; + + if (g_window_data.dst_height == 0) + g_window_data.dst_height = height; + + g_window_data.window_width = rect.right; + g_window_data.window_height = rect.bottom; + + g_window_data.window = CreateWindowEx( + 0, + title, title, + s_window_style, + x, y, + g_window_data.window_width, g_window_data.window_height, + 0, 0, 0, 0); + + if (!g_window_data.window) + return 0; + + if (flags & WF_ALWAYS_ON_TOP) + SetWindowPos(g_window_data.window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + + ShowWindow(g_window_data.window, SW_NORMAL); + + g_window_data.s_bitmapInfo = (BITMAPINFO *) calloc(1, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 3); + g_window_data.s_bitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + g_window_data.s_bitmapInfo->bmiHeader.biPlanes = 1; + g_window_data.s_bitmapInfo->bmiHeader.biBitCount = 32; + g_window_data.s_bitmapInfo->bmiHeader.biCompression = BI_BITFIELDS; + g_window_data.s_bitmapInfo->bmiHeader.biWidth = g_window_data.buffer_width; + g_window_data.s_bitmapInfo->bmiHeader.biHeight = -(LONG)g_window_data.buffer_height; + g_window_data.s_bitmapInfo->bmiColors[0].rgbRed = 0xff; + g_window_data.s_bitmapInfo->bmiColors[1].rgbGreen = 0xff; + g_window_data.s_bitmapInfo->bmiColors[2].rgbBlue = 0xff; + + g_window_data.s_hdc = GetDC(g_window_data.window); + + if (g_keyboard_func == 0x0) { + mfb_keyboard_callback(keyboard_default); + } + + return 1; +} + +int mfb_open(const char* title, int width, int height) { + return mfb_open_ex(title, width, height, 0); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int mfb_update(void* buffer) { - MSG msg; - - s_buffer = buffer; + MSG msg; + + if (buffer == 0x0) + return -2; - InvalidateRect(s_wnd, NULL, TRUE); - SendMessage(s_wnd, WM_PAINT, 0, 0); + if (g_window_data.close == true) + return -1; - while (PeekMessage(&msg, s_wnd, 0, 0, PM_REMOVE)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } + g_window_data.draw_buffer = buffer; - if (s_close == 1) - return -1; + InvalidateRect(g_window_data.window, NULL, TRUE); + SendMessage(g_window_data.window, WM_PAINT, 0, 0); - return 0; + while (g_window_data.close == false && PeekMessage(&msg, g_window_data.window, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return 0; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void mfb_close() { - s_buffer = 0; - free(s_bitmapInfo); - ReleaseDC(s_wnd, s_hdc); - DestroyWindow(s_wnd); + g_window_data.draw_buffer = 0x0; + if (g_window_data.s_bitmapInfo != 0x0) { + free(g_window_data.s_bitmapInfo); + } + if (g_window_data.window != 0 && g_window_data.s_hdc != 0) { + ReleaseDC(g_window_data.window, g_window_data.s_hdc); + DestroyWindow(g_window_data.window); + } + + g_window_data.window = 0; + g_window_data.s_hdc = 0; + g_window_data.s_bitmapInfo = 0x0; + g_window_data.close = true; } +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void keyboard_default(void *user_data, Key key, KeyMod mod, bool isPressed) { + kUnused(user_data); + kUnused(mod); + kUnused(isPressed); + if (key == KB_KEY_ESCAPE) + g_window_data.close = true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +uint32_t translate_mod() { + uint32_t mods = 0; + + if (GetKeyState(VK_SHIFT) & 0x8000) + mods |= KB_MOD_SHIFT; + if (GetKeyState(VK_CONTROL) & 0x8000) + mods |= KB_MOD_CONTROL; + if (GetKeyState(VK_MENU) & 0x8000) + mods |= KB_MOD_ALT; + if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x8000) + mods |= KB_MOD_SUPER; + if (GetKeyState(VK_CAPITAL) & 1) + mods |= KB_MOD_CAPS_LOCK; + if (GetKeyState(VK_NUMLOCK) & 1) + mods |= KB_MOD_NUM_LOCK; + + return mods; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +extern short int keycodes[512]; + +void init_keycodes() { + + // Clear keys + for (size_t i = 0; i < sizeof(keycodes) / sizeof(keycodes[0]); ++i) + keycodes[i] = 0; + + keycodes[0x00B] = KB_KEY_0; + keycodes[0x002] = KB_KEY_1; + keycodes[0x003] = KB_KEY_2; + keycodes[0x004] = KB_KEY_3; + keycodes[0x005] = KB_KEY_4; + keycodes[0x006] = KB_KEY_5; + keycodes[0x007] = KB_KEY_6; + keycodes[0x008] = KB_KEY_7; + keycodes[0x009] = KB_KEY_8; + keycodes[0x00A] = KB_KEY_9; + keycodes[0x01E] = KB_KEY_A; + keycodes[0x030] = KB_KEY_B; + keycodes[0x02E] = KB_KEY_C; + keycodes[0x020] = KB_KEY_D; + keycodes[0x012] = KB_KEY_E; + keycodes[0x021] = KB_KEY_F; + keycodes[0x022] = KB_KEY_G; + keycodes[0x023] = KB_KEY_H; + keycodes[0x017] = KB_KEY_I; + keycodes[0x024] = KB_KEY_J; + keycodes[0x025] = KB_KEY_K; + keycodes[0x026] = KB_KEY_L; + keycodes[0x032] = KB_KEY_M; + keycodes[0x031] = KB_KEY_N; + keycodes[0x018] = KB_KEY_O; + keycodes[0x019] = KB_KEY_P; + keycodes[0x010] = KB_KEY_Q; + keycodes[0x013] = KB_KEY_R; + keycodes[0x01F] = KB_KEY_S; + keycodes[0x014] = KB_KEY_T; + keycodes[0x016] = KB_KEY_U; + keycodes[0x02F] = KB_KEY_V; + keycodes[0x011] = KB_KEY_W; + keycodes[0x02D] = KB_KEY_X; + keycodes[0x015] = KB_KEY_Y; + keycodes[0x02C] = KB_KEY_Z; + + keycodes[0x028] = KB_KEY_APOSTROPHE; + keycodes[0x02B] = KB_KEY_BACKSLASH; + keycodes[0x033] = KB_KEY_COMMA; + keycodes[0x00D] = KB_KEY_EQUAL; + keycodes[0x029] = KB_KEY_GRAVE_ACCENT; + keycodes[0x01A] = KB_KEY_LEFT_BRACKET; + keycodes[0x00C] = KB_KEY_MINUS; + keycodes[0x034] = KB_KEY_PERIOD; + keycodes[0x01B] = KB_KEY_RIGHT_BRACKET; + keycodes[0x027] = KB_KEY_SEMICOLON; + keycodes[0x035] = KB_KEY_SLASH; + keycodes[0x056] = KB_KEY_WORLD_2; + + keycodes[0x00E] = KB_KEY_BACKSPACE; + keycodes[0x153] = KB_KEY_DELETE; + keycodes[0x14F] = KB_KEY_END; + keycodes[0x01C] = KB_KEY_ENTER; + keycodes[0x001] = KB_KEY_ESCAPE; + keycodes[0x147] = KB_KEY_HOME; + keycodes[0x152] = KB_KEY_INSERT; + keycodes[0x15D] = KB_KEY_MENU; + keycodes[0x151] = KB_KEY_PAGE_DOWN; + keycodes[0x149] = KB_KEY_PAGE_UP; + keycodes[0x045] = KB_KEY_PAUSE; + keycodes[0x146] = KB_KEY_PAUSE; + keycodes[0x039] = KB_KEY_SPACE; + keycodes[0x00F] = KB_KEY_TAB; + keycodes[0x03A] = KB_KEY_CAPS_LOCK; + keycodes[0x145] = KB_KEY_NUM_LOCK; + keycodes[0x046] = KB_KEY_SCROLL_LOCK; + keycodes[0x03B] = KB_KEY_F1; + keycodes[0x03C] = KB_KEY_F2; + keycodes[0x03D] = KB_KEY_F3; + keycodes[0x03E] = KB_KEY_F4; + keycodes[0x03F] = KB_KEY_F5; + keycodes[0x040] = KB_KEY_F6; + keycodes[0x041] = KB_KEY_F7; + keycodes[0x042] = KB_KEY_F8; + keycodes[0x043] = KB_KEY_F9; + keycodes[0x044] = KB_KEY_F10; + keycodes[0x057] = KB_KEY_F11; + keycodes[0x058] = KB_KEY_F12; + keycodes[0x064] = KB_KEY_F13; + keycodes[0x065] = KB_KEY_F14; + keycodes[0x066] = KB_KEY_F15; + keycodes[0x067] = KB_KEY_F16; + keycodes[0x068] = KB_KEY_F17; + keycodes[0x069] = KB_KEY_F18; + keycodes[0x06A] = KB_KEY_F19; + keycodes[0x06B] = KB_KEY_F20; + keycodes[0x06C] = KB_KEY_F21; + keycodes[0x06D] = KB_KEY_F22; + keycodes[0x06E] = KB_KEY_F23; + keycodes[0x076] = KB_KEY_F24; + keycodes[0x038] = KB_KEY_LEFT_ALT; + keycodes[0x01D] = KB_KEY_LEFT_CONTROL; + keycodes[0x02A] = KB_KEY_LEFT_SHIFT; + keycodes[0x15B] = KB_KEY_LEFT_SUPER; + keycodes[0x137] = KB_KEY_PRINT_SCREEN; + keycodes[0x138] = KB_KEY_RIGHT_ALT; + keycodes[0x11D] = KB_KEY_RIGHT_CONTROL; + keycodes[0x036] = KB_KEY_RIGHT_SHIFT; + keycodes[0x15C] = KB_KEY_RIGHT_SUPER; + keycodes[0x150] = KB_KEY_DOWN; + keycodes[0x14B] = KB_KEY_LEFT; + keycodes[0x14D] = KB_KEY_RIGHT; + keycodes[0x148] = KB_KEY_UP; + + keycodes[0x052] = KB_KEY_KP_0; + keycodes[0x04F] = KB_KEY_KP_1; + keycodes[0x050] = KB_KEY_KP_2; + keycodes[0x051] = KB_KEY_KP_3; + keycodes[0x04B] = KB_KEY_KP_4; + keycodes[0x04C] = KB_KEY_KP_5; + keycodes[0x04D] = KB_KEY_KP_6; + keycodes[0x047] = KB_KEY_KP_7; + keycodes[0x048] = KB_KEY_KP_8; + keycodes[0x049] = KB_KEY_KP_9; + keycodes[0x04E] = KB_KEY_KP_ADD; + keycodes[0x053] = KB_KEY_KP_DECIMAL; + keycodes[0x135] = KB_KEY_KP_DIVIDE; + keycodes[0x11C] = KB_KEY_KP_ENTER; + keycodes[0x037] = KB_KEY_KP_MULTIPLY; + keycodes[0x04A] = KB_KEY_KP_SUBTRACT; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +Key translate_key(unsigned int wParam, unsigned long lParam) { + if (wParam == VK_CONTROL) { + MSG next; + DWORD time; + + if (lParam & 0x01000000) + return KB_KEY_RIGHT_CONTROL; + + time = GetMessageTime(); + if (PeekMessageW(&next, NULL, 0, 0, PM_NOREMOVE)) + if (next.message == WM_KEYDOWN || next.message == WM_SYSKEYDOWN || next.message == WM_KEYUP || next.message == WM_SYSKEYUP) + if (next.wParam == VK_MENU && (next.lParam & 0x01000000) && next.time == time) + return KB_KEY_UNKNOWN; + + return KB_KEY_LEFT_CONTROL; + } + + if (wParam == VK_PROCESSKEY) + return KB_KEY_UNKNOWN; + + return (Key) keycodes[HIWORD(lParam) & 0x1FF]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool mfb_set_viewport(unsigned offset_x, unsigned offset_y, unsigned width, unsigned height) +{ + if(offset_x + width > g_window_data.window_width) { + return false; + } + if(offset_y + height > g_window_data.window_height) { + return false; + } + + g_window_data.dst_offset_x = offset_x; + g_window_data.dst_offset_y = offset_y; + + g_window_data.dst_width = width; + g_window_data.dst_height = height; + + return true; +} diff --git a/src/windows/WinWindowData.h b/src/windows/WinWindowData.h new file mode 100644 index 0000000..57d5a8f --- /dev/null +++ b/src/windows/WinWindowData.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#define WIN32_LEAN_AND_MEAN +#include + + +typedef struct { + HWND window; + WNDCLASS s_wc; + HDC s_hdc; + BITMAPINFO *s_bitmapInfo; + bool s_mouse_inside; + + uint32_t window_width; + uint32_t window_height; + + uint32_t dst_offset_x; + uint32_t dst_offset_y; + uint32_t dst_width; + uint32_t dst_height; + + void *draw_buffer; + uint32_t buffer_width; + uint32_t buffer_height; + + uint32_t mod_keys; + bool close; +} SWindowData; diff --git a/src/x11/X11MiniFB.c b/src/x11/X11MiniFB.c index d2b46f4..0c0b413 100644 --- a/src/x11/X11MiniFB.c +++ b/src/x11/X11MiniFB.c @@ -1,148 +1,632 @@ -#include -#include -#include - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -#define KEY_FUNCTION 0xFF -#define KEY_ESC 0x1B - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -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; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int mfb_open(const char* title, int width, int height) -{ - int depth, i, formatCount, convDepth = -1; - XPixmapFormatValues* formats; - XSetWindowAttributes windowAttributes; - XSizeHints sizeHints; - Visual* visual; - - s_display = XOpenDisplay(0); - - if (!s_display) - return -1; - - s_screen = DefaultScreen(s_display); - 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) - { - convDepth = formats[i].bits_per_pixel; - break; - } - } - - XFree(formats); - - // We only support 32-bit right now - if (convDepth != 32) - { - XCloseDisplay(s_display); - return -1; - } - - int screenWidth = DisplayWidth(s_display, s_screen); - int screenHeight = DisplayHeight(s_display, s_screen); - - 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, - &windowAttributes); - if (!s_window) - return 0; - - XSelectInput(s_display, s_window, KeyPressMask | KeyReleaseMask); - XStoreName(s_display, s_window, title); - - 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, s_window, &sizeHints); - XClearWindow(s_display, s_window); - XMapRaised(s_display, s_window); - XFlush(s_display); - - s_gc = DefaultGC(s_display, s_screen); - - s_ximage = XCreateImage(s_display, CopyFromParent, depth, ZPixmap, 0, NULL, width, height, 32, width * 4); - - s_width = width; - s_height = height; - - return 1; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -static int processEvents() -{ - XEvent event; - KeySym sym; - - if (!XPending(s_display)) - return 0; - - 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; - - return 0; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -int mfb_update(void* buffer) -{ - s_ximage->data = (char*)buffer; - - XPutImage(s_display, s_window, s_gc, s_ximage, 0, 0, 0, 0, s_width, s_height); - XFlush(s_display); - - if (processEvents() < 0) - return -1; - - return 0; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void mfb_close (void) -{ - s_ximage->data = NULL; - XDestroyImage(s_ximage); - XDestroyWindow(s_display, s_window); - XCloseDisplay(s_display); -} +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "X11WindowData.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +SWindowData g_window_data = { 0 }; + +extern void +stretch_image(uint32_t *srcImage, uint32_t srcX, uint32_t srcY, uint32_t srcWidth, uint32_t srcHeight, uint32_t srcPitch, + uint32_t *dstImage, uint32_t dstX, uint32_t dstY, uint32_t dstWidth, uint32_t dstHeight, uint32_t dstPitch); + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int mfb_open_ex(const char* title, int width, int height, int flags) { + int depth, i, formatCount, convDepth = -1; + XPixmapFormatValues* formats; + XSetWindowAttributes windowAttributes; + XSizeHints sizeHints; + Visual* visual; + + g_window_data.display = XOpenDisplay(0); + if (!g_window_data.display) + return -1; + + init_keycodes(); + + g_window_data.screen = DefaultScreen(g_window_data.display); + + visual = DefaultVisual(g_window_data.display, g_window_data.screen); + formats = XListPixmapFormats(g_window_data.display, &formatCount); + depth = DefaultDepth(g_window_data.display, g_window_data.screen); + + Window defaultRootWindow = DefaultRootWindow(g_window_data.display); + + 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) + { + XCloseDisplay(g_window_data.display); + return -1; + } + + int screenWidth = DisplayWidth(g_window_data.display, g_window_data.screen); + int screenHeight = DisplayHeight(g_window_data.display, g_window_data.screen); + + windowAttributes.border_pixel = BlackPixel(g_window_data.display, g_window_data.screen); + windowAttributes.background_pixel = BlackPixel(g_window_data.display, g_window_data.screen); + windowAttributes.backing_store = NotUseful; + + int posX, posY; + int windowWidth, windowHeight; + + g_window_data.window_width = width; + g_window_data.window_height = height; + g_window_data.buffer_width = width; + g_window_data.buffer_height = height; + g_window_data.dst_offset_x = 0; + g_window_data.dst_offset_y = 0; + g_window_data.dst_width = width; + g_window_data.dst_height = height; + + if (flags & WF_FULLSCREEN_DESKTOP) { + posX = 0; + posY = 0; + windowWidth = screenWidth; + windowHeight = screenHeight; + } + else { + posX = (screenWidth - width) / 2; + posY = (screenHeight - height) / 2; + windowWidth = width; + windowHeight = height; + } + + g_window_data.window = XCreateWindow( + g_window_data.display, + defaultRootWindow, + posX, posY, + windowWidth, windowHeight, + 0, + depth, + InputOutput, + visual, + CWBackPixel | CWBorderPixel | CWBackingStore, + &windowAttributes); + if (!g_window_data.window) + return 0; + + XSelectInput(g_window_data.display, g_window_data.window, + KeyPressMask | KeyReleaseMask + | ButtonPressMask | ButtonReleaseMask | PointerMotionMask + | StructureNotifyMask | ExposureMask + | FocusChangeMask + | EnterWindowMask | LeaveWindowMask + ); + + XStoreName(g_window_data.display, g_window_data.window, title); + + if (flags & WF_BORDERLESS) { + struct StyleHints { + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long inputMode; + unsigned long status; + } sh = { + .flags = 2, + .functions = 0, + .decorations = 0, + .inputMode = 0, + .status = 0, + }; + Atom sh_p = XInternAtom(g_window_data.display, "_MOTIF_WM_HINTS", True); + XChangeProperty(g_window_data.display, g_window_data.window, sh_p, sh_p, 32, PropModeReplace, (unsigned char*)&sh, 5); + } + + if (flags & WF_ALWAYS_ON_TOP) { + Atom sa_p = XInternAtom(g_window_data.display, "_NET_WM_STATE_ABOVE", False); + XChangeProperty(g_window_data.display, g_window_data.window, XInternAtom(g_window_data.display, "_NET_WM_STATE", False), XA_ATOM, 32, PropModeReplace, (unsigned char *)&sa_p, 1); + } + + if (flags & WF_FULLSCREEN) { + Atom sf_p = XInternAtom(g_window_data.display, "_NET_WM_STATE_FULLSCREEN", True); + XChangeProperty(g_window_data.display, g_window_data.window, XInternAtom(g_window_data.display, "_NET_WM_STATE", True), XA_ATOM, 32, PropModeReplace, (unsigned char*)&sf_p, 1); + } + + sizeHints.flags = PPosition | PMinSize | PMaxSize; + sizeHints.x = 0; + sizeHints.y = 0; + sizeHints.min_width = width; + sizeHints.min_height = height; + if(flags & WF_RESIZABLE) { + sizeHints.max_width = screenWidth; + sizeHints.max_height = screenHeight; + } + else { + sizeHints.max_width = width; + sizeHints.max_height = height; + } + + XSetWMNormalHints(g_window_data.display, g_window_data.window, &sizeHints); + XClearWindow(g_window_data.display, g_window_data.window); + XMapRaised(g_window_data.display, g_window_data.window); + XFlush(g_window_data.display); + + g_window_data.gc = DefaultGC(g_window_data.display, g_window_data.screen); + + g_window_data.image = XCreateImage(g_window_data.display, CopyFromParent, depth, ZPixmap, 0, NULL, width, height, 32, width * 4); + + if (g_keyboard_func == 0x0) { + mfb_keyboard_callback(keyboard_default); + } + + printf("Window created using X11 API\n"); + + return 1; +} + +int mfb_open(const char* title, int width, int height) +{ + return mfb_open_ex(title, width, height, 0); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int translate_key(int scancode); +int translate_mod(int state); +int translate_mod_ex(int key, int state, int is_pressed); + +static int processEvents() +{ + XEvent event; + + while ((g_window_data.close == false) && XPending(g_window_data.display)) { + XNextEvent(g_window_data.display, &event); + + switch (event.type) { + case KeyPress: + case KeyRelease: + { + int kb_key = translate_key(event.xkey.keycode); + int is_pressed = (event.type == KeyPress); + g_window_data.mod_keys = translate_mod_ex(kb_key, event.xkey.state, is_pressed); + + kCall(g_keyboard_func, kb_key, g_window_data.mod_keys, is_pressed); + } + break; + + case ButtonPress: + case ButtonRelease: + { + MouseButton button = event.xbutton.button; + int is_pressed = (event.type == ButtonPress); + g_window_data.mod_keys = translate_mod(event.xkey.state); + switch (button) { + case Button1: + case Button2: + case Button3: + kCall(g_mouse_btn_func, button, g_window_data.mod_keys, is_pressed); + break; + + case Button4: + kCall(g_mouse_wheel_func, g_window_data.mod_keys, 0.0f, 1.0f); + break; + case Button5: + kCall(g_mouse_wheel_func, g_window_data.mod_keys, 0.0f, -1.0f); + break; + + case 6: + kCall(g_mouse_wheel_func, g_window_data.mod_keys, 1.0f, 0.0f); + break; + case 7: + kCall(g_mouse_wheel_func, g_window_data.mod_keys, -1.0f, 0.0f); + break; + + default: + kCall(g_mouse_btn_func, button - 4, g_window_data.mod_keys, is_pressed); + break; + } + } + break; + + case MotionNotify: + kCall(g_mouse_move_func, event.xmotion.x, event.xmotion.y); + break; + + case ConfigureNotify: + { + g_window_data.window_width = event.xconfigure.width; + g_window_data.window_height = event.xconfigure.height; + g_window_data.dst_offset_x = 0; + g_window_data.dst_offset_y = 0; + g_window_data.dst_width = g_window_data.window_width; + g_window_data.dst_height = g_window_data.window_height; + + XClearWindow(g_window_data.display, g_window_data.window); + kCall(g_resize_func, g_window_data.window_width, g_window_data.window_height); + } + break; + + case EnterNotify: + case LeaveNotify: + break; + + case FocusIn: + kCall(g_active_func, true); + break; + + case FocusOut: + kCall(g_active_func, false); + break; + + case DestroyNotify: + return -1; + break; + } + } + + if(g_window_data.close == true) + return -1; + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int mfb_update(void* buffer) +{ + if (buffer == 0x0) { + return -2; + } + + if (g_window_data.buffer_width != g_window_data.dst_width || g_window_data.buffer_height != g_window_data.dst_height) { + if(g_window_data.image_scaler_width != g_window_data.dst_width || g_window_data.image_scaler_height != g_window_data.dst_height) { + if(g_window_data.image_scaler != 0x0) { + g_window_data.image_scaler->data = 0x0; + XDestroyImage(g_window_data.image_scaler); + } + if(g_window_data.image_buffer != 0x0) { + free(g_window_data.image_buffer); + g_window_data.image_buffer = 0x0; + } + int depth = DefaultDepth(g_window_data.display, g_window_data.screen); + g_window_data.image_buffer = malloc(g_window_data.dst_width * g_window_data.dst_height * 4); + g_window_data.image_scaler_width = g_window_data.dst_width; + g_window_data.image_scaler_height = g_window_data.dst_height; + g_window_data.image_scaler = XCreateImage(g_window_data.display, CopyFromParent, depth, ZPixmap, 0, NULL, g_window_data.image_scaler_width, g_window_data.image_scaler_height, 32, g_window_data.image_scaler_width * 4); + } + } + + if(g_window_data.image_scaler != 0x0) { + stretch_image(buffer, 0, 0, g_window_data.buffer_width, g_window_data.buffer_height, g_window_data.buffer_width, g_window_data.image_buffer, 0, 0, g_window_data.dst_width, g_window_data.dst_height, g_window_data.dst_width); + g_window_data.image_scaler->data = g_window_data.image_buffer; + XPutImage(g_window_data.display, g_window_data.window, g_window_data.gc, g_window_data.image_scaler, 0, 0, g_window_data.dst_offset_x, g_window_data.dst_offset_y, g_window_data.dst_width, g_window_data.dst_height); + } + else { + g_window_data.image->data = (char *) buffer; + XPutImage(g_window_data.display, g_window_data.window, g_window_data.gc, g_window_data.image, 0, 0, g_window_data.dst_offset_x, g_window_data.dst_offset_y, g_window_data.dst_width, g_window_data.dst_height); + } + XFlush(g_window_data.display); + + if (processEvents() < 0) + return -1; + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void mfb_close(void) +{ + if(g_window_data.image != 0x0) { + g_window_data.image->data = 0x0; + XDestroyImage(g_window_data.image); + XDestroyWindow(g_window_data.display, g_window_data.window); + XCloseDisplay(g_window_data.display); + + g_window_data.image = 0x0; + g_window_data.display = 0x0; + g_window_data.window = 0; + } + g_window_data.close = true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +extern short int keycodes[512]; + +static int translateKeyCodeB(int keySym) { + + switch (keySym) + { + case XK_KP_0: return KB_KEY_KP_0; + case XK_KP_1: return KB_KEY_KP_1; + case XK_KP_2: return KB_KEY_KP_2; + case XK_KP_3: return KB_KEY_KP_3; + case XK_KP_4: return KB_KEY_KP_4; + case XK_KP_5: return KB_KEY_KP_5; + case XK_KP_6: return KB_KEY_KP_6; + case XK_KP_7: return KB_KEY_KP_7; + case XK_KP_8: return KB_KEY_KP_8; + case XK_KP_9: return KB_KEY_KP_9; + case XK_KP_Separator: + case XK_KP_Decimal: return KB_KEY_KP_DECIMAL; + case XK_KP_Equal: return KB_KEY_KP_EQUAL; + case XK_KP_Enter: return KB_KEY_KP_ENTER; + } + + return KB_KEY_UNKNOWN; +} + +static int translateKeyCodeA(int keySym) { + switch (keySym) + { + case XK_Escape: return KB_KEY_ESCAPE; + case XK_Tab: return KB_KEY_TAB; + case XK_Shift_L: return KB_KEY_LEFT_SHIFT; + case XK_Shift_R: return KB_KEY_RIGHT_SHIFT; + case XK_Control_L: return KB_KEY_LEFT_CONTROL; + case XK_Control_R: return KB_KEY_RIGHT_CONTROL; + case XK_Meta_L: + case XK_Alt_L: return KB_KEY_LEFT_ALT; + case XK_Mode_switch: // Mapped to Alt_R on many keyboards + case XK_ISO_Level3_Shift: // AltGr on at least some machines + case XK_Meta_R: + case XK_Alt_R: return KB_KEY_RIGHT_ALT; + case XK_Super_L: return KB_KEY_LEFT_SUPER; + case XK_Super_R: return KB_KEY_RIGHT_SUPER; + case XK_Menu: return KB_KEY_MENU; + case XK_Num_Lock: return KB_KEY_NUM_LOCK; + case XK_Caps_Lock: return KB_KEY_CAPS_LOCK; + case XK_Print: return KB_KEY_PRINT_SCREEN; + case XK_Scroll_Lock: return KB_KEY_SCROLL_LOCK; + case XK_Pause: return KB_KEY_PAUSE; + case XK_Delete: return KB_KEY_DELETE; + case XK_BackSpace: return KB_KEY_BACKSPACE; + case XK_Return: return KB_KEY_ENTER; + case XK_Home: return KB_KEY_HOME; + case XK_End: return KB_KEY_END; + case XK_Page_Up: return KB_KEY_PAGE_UP; + case XK_Page_Down: return KB_KEY_PAGE_DOWN; + case XK_Insert: return KB_KEY_INSERT; + case XK_Left: return KB_KEY_LEFT; + case XK_Right: return KB_KEY_RIGHT; + case XK_Down: return KB_KEY_DOWN; + case XK_Up: return KB_KEY_UP; + case XK_F1: return KB_KEY_F1; + case XK_F2: return KB_KEY_F2; + case XK_F3: return KB_KEY_F3; + case XK_F4: return KB_KEY_F4; + case XK_F5: return KB_KEY_F5; + case XK_F6: return KB_KEY_F6; + case XK_F7: return KB_KEY_F7; + case XK_F8: return KB_KEY_F8; + case XK_F9: return KB_KEY_F9; + case XK_F10: return KB_KEY_F10; + case XK_F11: return KB_KEY_F11; + case XK_F12: return KB_KEY_F12; + case XK_F13: return KB_KEY_F13; + case XK_F14: return KB_KEY_F14; + case XK_F15: return KB_KEY_F15; + case XK_F16: return KB_KEY_F16; + case XK_F17: return KB_KEY_F17; + case XK_F18: return KB_KEY_F18; + case XK_F19: return KB_KEY_F19; + case XK_F20: return KB_KEY_F20; + case XK_F21: return KB_KEY_F21; + case XK_F22: return KB_KEY_F22; + case XK_F23: return KB_KEY_F23; + case XK_F24: return KB_KEY_F24; + case XK_F25: return KB_KEY_F25; + + // Numeric keypad + case XK_KP_Divide: return KB_KEY_KP_DIVIDE; + case XK_KP_Multiply: return KB_KEY_KP_MULTIPLY; + case XK_KP_Subtract: return KB_KEY_KP_SUBTRACT; + case XK_KP_Add: return KB_KEY_KP_ADD; + + // These should have been detected in secondary keysym test above! + case XK_KP_Insert: return KB_KEY_KP_0; + case XK_KP_End: return KB_KEY_KP_1; + case XK_KP_Down: return KB_KEY_KP_2; + case XK_KP_Page_Down: return KB_KEY_KP_3; + case XK_KP_Left: return KB_KEY_KP_4; + case XK_KP_Right: return KB_KEY_KP_6; + case XK_KP_Home: return KB_KEY_KP_7; + case XK_KP_Up: return KB_KEY_KP_8; + case XK_KP_Page_Up: return KB_KEY_KP_9; + case XK_KP_Delete: return KB_KEY_KP_DECIMAL; + case XK_KP_Equal: return KB_KEY_KP_EQUAL; + case XK_KP_Enter: return KB_KEY_KP_ENTER; + + // Last resort: Check for printable keys (should not happen if the XKB + // extension is available). This will give a layout dependent mapping + // (which is wrong, and we may miss some keys, especially on non-US + // keyboards), but it's better than nothing... + case XK_a: return KB_KEY_A; + case XK_b: return KB_KEY_B; + case XK_c: return KB_KEY_C; + case XK_d: return KB_KEY_D; + case XK_e: return KB_KEY_E; + case XK_f: return KB_KEY_F; + case XK_g: return KB_KEY_G; + case XK_h: return KB_KEY_H; + case XK_i: return KB_KEY_I; + case XK_j: return KB_KEY_J; + case XK_k: return KB_KEY_K; + case XK_l: return KB_KEY_L; + case XK_m: return KB_KEY_M; + case XK_n: return KB_KEY_N; + case XK_o: return KB_KEY_O; + case XK_p: return KB_KEY_P; + case XK_q: return KB_KEY_Q; + case XK_r: return KB_KEY_R; + case XK_s: return KB_KEY_S; + case XK_t: return KB_KEY_T; + case XK_u: return KB_KEY_U; + case XK_v: return KB_KEY_V; + case XK_w: return KB_KEY_W; + case XK_x: return KB_KEY_X; + case XK_y: return KB_KEY_Y; + case XK_z: return KB_KEY_Z; + case XK_1: return KB_KEY_1; + case XK_2: return KB_KEY_2; + case XK_3: return KB_KEY_3; + case XK_4: return KB_KEY_4; + case XK_5: return KB_KEY_5; + case XK_6: return KB_KEY_6; + case XK_7: return KB_KEY_7; + case XK_8: return KB_KEY_8; + case XK_9: return KB_KEY_9; + case XK_0: return KB_KEY_0; + case XK_space: return KB_KEY_SPACE; + case XK_minus: return KB_KEY_MINUS; + case XK_equal: return KB_KEY_EQUAL; + case XK_bracketleft: return KB_KEY_LEFT_BRACKET; + case XK_bracketright: return KB_KEY_RIGHT_BRACKET; + case XK_backslash: return KB_KEY_BACKSLASH; + case XK_semicolon: return KB_KEY_SEMICOLON; + case XK_apostrophe: return KB_KEY_APOSTROPHE; + case XK_grave: return KB_KEY_GRAVE_ACCENT; + case XK_comma: return KB_KEY_COMMA; + case XK_period: return KB_KEY_PERIOD; + case XK_slash: return KB_KEY_SLASH; + case XK_less: return KB_KEY_WORLD_1; // At least in some layouts... + default: break; + } + + return KB_KEY_UNKNOWN; +} + +void init_keycodes() { + size_t i; + int keySym; + + // Clear keys + for (i = 0; i < sizeof(keycodes) / sizeof(keycodes[0]); ++i) + keycodes[i] = KB_KEY_UNKNOWN; + + // Valid key code range is [8,255], according to the Xlib manual + for(int i=8; i<=255; ++i) { + // Try secondary keysym, for numeric keypad keys + keySym = XkbKeycodeToKeysym(g_window_data.display, i, 0, 1); + keycodes[i] = translateKeyCodeB(keySym); + if(keycodes[i] == KB_KEY_UNKNOWN) { + keySym = XkbKeycodeToKeysym(g_window_data.display, i, 0, 0); + keycodes[i] = translateKeyCodeA(keySym); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int translate_key(int scancode) { + if (scancode < 0 || scancode > 255) + return KB_KEY_UNKNOWN; + + return keycodes[scancode]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int translate_mod(int state) { + int mod_keys = 0; + + if (state & ShiftMask) + mod_keys |= KB_MOD_SHIFT; + if (state & ControlMask) + mod_keys |= KB_MOD_CONTROL; + if (state & Mod1Mask) + mod_keys |= KB_MOD_ALT; + if (state & Mod4Mask) + mod_keys |= KB_MOD_SUPER; + if (state & LockMask) + mod_keys |= KB_MOD_CAPS_LOCK; + if (state & Mod2Mask) + mod_keys |= KB_MOD_NUM_LOCK; + + return mod_keys; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int translate_mod_ex(int key, int state, int is_pressed) { + int mod_keys = 0; + + mod_keys = translate_mod(state); + + switch (key) + { + case KB_KEY_LEFT_SHIFT: + case KB_KEY_RIGHT_SHIFT: + if(is_pressed) + mod_keys |= KB_MOD_SHIFT; + else + mod_keys &= ~KB_MOD_SHIFT; + break; + + case KB_KEY_LEFT_CONTROL: + case KB_KEY_RIGHT_CONTROL: + if(is_pressed) + mod_keys |= KB_MOD_CONTROL; + else + mod_keys &= ~KB_MOD_CONTROL; + break; + + case KB_KEY_LEFT_ALT: + case KB_KEY_RIGHT_ALT: + if(is_pressed) + mod_keys |= KB_MOD_ALT; + else + mod_keys &= ~KB_MOD_ALT; + break; + + case KB_KEY_LEFT_SUPER: + case KB_KEY_RIGHT_SUPER: + if(is_pressed) + mod_keys |= KB_MOD_SUPER; + else + mod_keys &= ~KB_MOD_SUPER; + break; + } + + return mod_keys; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void keyboard_default(void *user_data, Key key, KeyMod mod, bool isPressed) { + kUnused(user_data); + kUnused(mod); + kUnused(isPressed); + if (key == KB_KEY_ESCAPE) { + g_window_data.close = true; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool mfb_set_viewport(unsigned offset_x, unsigned offset_y, unsigned width, unsigned height) +{ + if(offset_x + width > g_window_data.window_width) { + return false; + } + if(offset_y + height > g_window_data.window_height) { + return false; + } + + g_window_data.dst_offset_x = offset_x; + g_window_data.dst_offset_y = offset_y; + g_window_data.dst_width = width; + g_window_data.dst_height = height; + return true; +} diff --git a/src/x11/X11WindowData.h b/src/x11/X11WindowData.h new file mode 100644 index 0000000..e09ac30 --- /dev/null +++ b/src/x11/X11WindowData.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include + + +typedef struct { + Window window; + uint32_t window_width; + uint32_t window_height; + + uint32_t dst_offset_x; + uint32_t dst_offset_y; + uint32_t dst_width; + uint32_t dst_height; + + void *draw_buffer; + uint32_t buffer_width; + uint32_t buffer_height; + + uint32_t mod_keys; + bool close; + + Display *display; + int screen; + GC gc; + XImage *image; + + void *image_buffer; + XImage *image_scaler; + uint32_t image_scaler_width; + uint32_t image_scaler_height; +} SWindowData; diff --git a/tests/noise.c b/tests/noise.c index e067a95..0fe3b53 100644 --- a/tests/noise.c +++ b/tests/noise.c @@ -1,42 +1,163 @@ #include +#include +#include + +#define kUnused(var) (void) var; #define WIDTH 800 #define HEIGHT 600 static unsigned int s_buffer[WIDTH * HEIGHT]; +//------------------------------------- +// C interface +//------------------------------------- +void active(void *user_data, bool isActive) { + kUnused(user_data); + fprintf(stdout, "active: %d\n", isActive); +} + +void resize(void *user_data, int width, int height) { + uint32_t x = 0; + uint32_t y = 0; + + kUnused(user_data); + fprintf(stdout, "resize: %d, %d\n", width, height); + if(width > WIDTH) { + x = (width - WIDTH) >> 1; + width = WIDTH; + } + if(height > HEIGHT) { + y = (height - HEIGHT) >> 1; + height = HEIGHT; + } + mfb_set_viewport(x, y, width, height); +} + +void keyboard(void *user_data, Key key, KeyMod mod, bool isPressed) { + kUnused(user_data); + fprintf(stdout, "keyboard: key: %s (pressed: %d) [KeyMod: %x]\n", mfb_get_key_name(key), isPressed, mod); + if(key == KB_KEY_ESCAPE) { + mfb_close(); + } +} + +void char_input(void *user_data, unsigned int charCode) { + kUnused(user_data); + fprintf(stdout, "charCode: %d\n", charCode); +} + +void mouse_btn(void *user_data, MouseButton button, KeyMod mod, bool isPressed) { + kUnused(user_data); + fprintf(stdout, "mouse_btn: button: %d (pressed: %d) [KeyMod: %x]\n", button, isPressed, mod); +} + +void mouse_move(void *user_data, int x, int y) { + kUnused(user_data); + kUnused(x); + kUnused(y); + //fprintf(stdout, "mouse_move: %d, %d\n", x, y); +} + +void mouse_scroll(void *user_data, KeyMod mod, float deltaX, float deltaY) { + kUnused(user_data); + fprintf(stdout, "mouse_scroll: x: %f, y: %f [KeyMod: %x]\n", deltaX, deltaY, mod); +} + +//------------------------------------- +// C++ interface (calling C functions) +//------------------------------------- +#if defined(__cplusplus) + +class Events { +public: + void active(void *user_data, bool isActive) { + ::active(user_data, isActive); + } + + void resize(void *user_data, int width, int height) { + ::resize(user_data, width, height); + } + + void keyboard(void *user_data, Key key, KeyMod mod, bool isPressed) { + ::keyboard(user_data, key, mod, isPressed); + } + + void char_input(void *user_data, unsigned int charCode) { + ::char_input(user_data, charCode); + } + + void mouse_btn(void *user_data, MouseButton button, KeyMod mod, bool isPressed) { + ::mouse_btn(user_data, button, mod, isPressed); + } + + void mouse_move(void *user_data, int x, int y) { + ::mouse_move(user_data, x, y); + } + + void mouse_scroll(void *user_data, KeyMod mod, float deltaX, float deltaY) { + ::mouse_scroll(user_data, mod, deltaX, deltaY); + } +}; + +#endif + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int main() { - int noise, carry, seed = 0xbeef; + int noise, carry, seed = 0xbeef; - if (!mfb_open("Noise Test", WIDTH, HEIGHT)) - return 0; +#if defined(__cplusplus) - for (;;) - { - int i, state; + Events e; - for (i = 0; i < WIDTH * HEIGHT; ++i) - { - noise = seed; - noise >>= 3; - noise ^= seed; - carry = noise & 1; - noise >>= 1; - seed >>= 1; - seed |= (carry << 30); - noise &= 0xFF; - s_buffer[i] = MFB_RGB(noise, noise, noise); - } + mfb_active_callback(&e, &Events::active); + mfb_resize_callback(&e, &Events::resize); + mfb_keyboard_callback(&e, &Events::keyboard); + mfb_char_input_callback(&e, &Events::char_input); + mfb_mouse_button_callback(&e, &Events::mouse_btn); + mfb_mouse_move_callback(&e, &Events::mouse_move); + mfb_mouse_scroll_callback(&e, &Events::mouse_scroll); - state = mfb_update(s_buffer); +#else - if (state < 0) - break; - } + mfb_active_callback(active); + mfb_resize_callback(resize); + mfb_keyboard_callback(keyboard); + mfb_char_input_callback(char_input); + mfb_mouse_button_callback(mouse_btn); + mfb_mouse_move_callback(mouse_move); + mfb_mouse_scroll_callback(mouse_scroll); - mfb_close(); +#endif - return 0; + if (!mfb_open_ex("Noise Test", WIDTH, HEIGHT, WF_RESIZABLE)) + return 0; + + for (;;) + { + int i, state; + + for (i = 0; i < WIDTH * HEIGHT; ++i) + { + noise = seed; + noise >>= 3; + noise ^= seed; + carry = noise & 1; + noise >>= 1; + seed >>= 1; + seed |= (carry << 30); + noise &= 0xFF; + s_buffer[i] = MFB_RGB(noise, noise, noise); + } + + state = mfb_update(s_buffer); + + if (state < 0) + break; + } + + mfb_close(); + + return 0; }