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
This commit is contained in:
Carlos Aragonés 2019-06-08 20:11:02 +02:00 committed by Daniel Collin
parent d6a52459ef
commit 8b6148cf97
22 changed files with 4322 additions and 1153 deletions

6
.gitignore vendored
View File

@ -1,2 +1,8 @@
.tundra2.* .tundra2.*
t2-output t2-output
/.vscode
/win
/macosx
/linux
/build
/.history

75
CMakeLists.txt Normal file
View File

@ -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()

View File

@ -1,30 +1,60 @@
#ifndef _MINIFB_H_ #ifndef _MINIFB_H_
#define _MINIFB_H_ #define _MINIFB_H_
#ifdef __cplusplus #include "MiniFB_enums.h"
extern "C" {
#endif #ifdef __cplusplus
extern "C" {
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #endif
#define MFB_RGB(r, g, b) (((unsigned int)r) << 16) | (((unsigned int)g) << 8) | b ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #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);
// Create a window that is used to display the buffer sent into the mfb_update function, returns 0 if fails
// Update the display. Input buffer is assumed to be a 32-bit buffer of the size given in the open call int mfb_open(const char* name, int width, int height);
// Will return -1 when ESC key is pressed (later on will return keycode and -1 on other close signal) int mfb_open_ex(const char* name, int width, int height, int flags);
int mfb_update(void* buffer);
// Update the display. Input buffer is assumed to be a 32-bit buffer of the size given in the open call
// Close the window // Will return -1 when ESC key is pressed (later on will return keycode and -1 on other close signal)
void mfb_close(); int mfb_update(void* buffer);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Close the window
void mfb_close();
#ifdef __cplusplus
} // Set user data
#endif void mfb_set_user_data(void *user_data);
#endif // 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

124
include/MiniFB_cpp.h Executable file
View File

@ -0,0 +1,124 @@
#pragma once
#if defined(__cplusplus)
#include <functional>
#include "MiniFB.h"
template <class T>
void mfb_active_callback(T *obj, void (T::*method)(void *, bool));
template <class T>
void mfb_resize_callback(T *obj, void (T::*method)(void *, int, int));
template <class T>
void mfb_keyboard_callback(T *obj, void (T::*method)(void *, Key, KeyMod, bool));
template <class T>
void mfb_char_input_callback(T *obj, void (T::*method)(void *, unsigned int));
template <class T>
void mfb_mouse_button_callback(T *obj, void (T::*method)(void *, MouseButton, KeyMod, bool));
template <class T>
void mfb_mouse_move_callback(T *obj, void (T::*method)(void *, int, int));
template <class T>
void mfb_mouse_scroll_callback(T *obj, void (T::*method)(void *, KeyMod, float, float));
//-------------------------------------
// To avoid clumsy hands
//-------------------------------------
class Stub {
template <class T>
friend void mfb_active_callback(T *obj, void (T::*method)(void *, bool));
template <class T>
friend void mfb_resize_callback(T *obj, void (T::*method)(void *, int, int));
template <class T>
friend void mfb_mouse_button_callback(T *obj, void (T::*method)(void *, MouseButton, KeyMod, bool));
template <class T>
friend void mfb_keyboard_callback(T *obj, void (T::*method)(void *, Key, KeyMod, bool));
template <class T>
friend void mfb_char_input_callback(T *obj, void (T::*method)(void *, unsigned int));
template <class T>
friend void mfb_mouse_button_callback(T *obj, void (T::*method)(void *, MouseButton, KeyMod, bool));
template <class T>
friend void mfb_mouse_move_callback(T *obj, void (T::*method)(void *, int, int));
template <class T>
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<void(void *user_data, bool)> m_active;
static std::function<void(void *user_data, int, int)> m_resize;
static std::function<void(void *user_data, Key, KeyMod, bool)> m_keyboard;
static std::function<void(void *user_data, unsigned int)> m_char_input;
static std::function<void(void *user_data, MouseButton, KeyMod, bool)> m_mouse_btn;
static std::function<void(void *user_data, int, int)> m_mouse_move;
static std::function<void(void *user_data, KeyMod, float, float)> m_scroll;
};
//-------------------------------------
template <class T>
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 <class T>
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 <class T>
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 <class T>
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 <class T>
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 <class T>
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 <class T>
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

163
include/MiniFB_enums.h Executable file
View File

@ -0,0 +1,163 @@
#pragma once
#include <stdbool.h>
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;

421
src/MiniFB_common.c Executable file
View File

@ -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";
}

38
src/MiniFB_cpp.cpp Executable file
View File

@ -0,0 +1,38 @@
#include <MiniFB_cpp.h>
#include <MiniFB_enums.h>
std::function<void(void *, bool)> Stub::m_active;
std::function<void(void *, int, int)> Stub::m_resize;
std::function<void(void *, Key, KeyMod, bool)> Stub::m_keyboard;
std::function<void(void *, unsigned int)> Stub::m_char_input;
std::function<void(void *, MouseButton, KeyMod, bool)> Stub::m_mouse_btn;
std::function<void(void *, int, int)> Stub::m_mouse_move;
std::function<void(void *, KeyMod, float, float)> 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);
}

75
src/MiniFB_internal.c Normal file
View File

@ -0,0 +1,75 @@
#include "MiniFB_internal.h"
#include <stdint.h>
//#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<dstHeight; ++y) {
srcOffsetX = 0;
for(x=0; x<dstWidth; ++x) {
#if defined(kUseBilinearInterpolation)
dstImage[x] = interpolate(srcImage, x+srcX, y+srcY, srcOffsetX, srcOffsetY, srcWidth, srcHeight, srcPitch);
#else
dstImage[x] = srcImage[srcOffsetX >> 16];
#endif
srcOffsetX += deltaX;
}
srcOffsetY += deltaY;
if(srcOffsetY >= 0x10000) {
srcImage += (srcOffsetY >> 16) * srcPitch;
srcOffsetY &= 0xffff;
}
dstImage += dstPitch;
}
}

28
src/MiniFB_internal.h Executable file
View File

@ -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

View File

@ -1,273 +1,496 @@
#include "OSXWindow.h" #include "OSXWindow.h"
#include "OSXWindowFrameView.h" #include "OSXWindowFrameView.h"
#include <Cocoa/Cocoa.h> #include "OSXWindowData.h"
#if defined(USE_METAL_API) #include <MiniFB.h>
#include <Carbon/Carbon.h> #include <MiniFB_enums.h>
#include <MetalKit/MetalKit.h> #include <MiniFB_internal.h>
#endif #include <Cocoa/Cocoa.h>
#include <unistd.h> #if defined(USE_METAL_API)
#include "MiniFB.h" #include <Carbon/Carbon.h>
#include <MetalKit/MetalKit.h>
#if defined(USE_METAL_API) #endif
extern id<MTLDevice> g_metal_device; #include <unistd.h>
extern id<MTLCommandQueue> g_command_queue;
extern id<MTLLibrary> g_library; SWindowData g_window_data = { 0 };
extern id<MTLRenderPipelineState> g_pipeline_state;
void init_keycodes();
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if defined(USE_METAL_API)
NSString* g_shadersSrc = @ extern id<MTLDevice> g_metal_device;
" #include <metal_stdlib>\n" extern id<MTLCommandQueue> g_command_queue;
"using namespace metal;\n" extern id<MTLLibrary> g_library;
extern id<MTLRenderPipelineState> g_pipeline_state;
"struct VertexOutput {\n"
"float4 pos [[position]];\n" Vertex gVertices[4] = {
"float2 texcoord;\n" {-1.0, -1.0, 0, 1},
"};\n" {-1.0, 1.0, 0, 1},
{ 1.0, -1.0, 0, 1},
"vertex VertexOutput vertFunc(\n" { 1.0, 1.0, 0, 1},
"unsigned int vID[[vertex_id]])\n" };
"{\n"
"VertexOutput out;\n" ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
"out.pos.x = (float)(vID / 2) * 4.0 - 1.0;\n" NSString* g_shadersSrc = @
"out.pos.y = (float)(vID % 2) * 4.0 - 1.0;\n" " #include <metal_stdlib>\n"
"out.pos.z = 0.0;\n" "using namespace metal;\n"
"out.pos.w = 1.0;\n"
"struct VertexOutput {\n"
"out.texcoord.x = (float)(vID / 2) * 2.0;\n" "float4 pos [[position]];\n"
"out.texcoord.y = 1.0 - (float)(vID % 2) * 2.0;\n" "float2 texcoord;\n"
"};\n"
"return out;\n"
"}\n" "vertex VertexOutput vertFunc(unsigned int vID[[vertex_id]])\n"
"{\n"
"fragment float4 fragFunc(VertexOutput input [[stage_in]],\n" "VertexOutput out;\n"
"texture2d<half> colorTexture [[ texture(0) ]])\n"
"{\n" "out.pos.x = (float)(vID / 2) * 4.0 - 1.0;\n"
"constexpr sampler textureSampler(mag_filter::nearest, min_filter::nearest);\n" "out.pos.y = (float)(vID % 2) * 4.0 - 1.0;\n"
"out.pos.z = 0.0;\n"
// Sample the texture to obtain a color "out.pos.w = 1.0;\n"
"const half4 colorSample = colorTexture.sample(textureSampler, input.texcoord);\n"
"out.texcoord.x = (float)(vID / 2) * 2.0;\n"
// We return the color of the texture "out.texcoord.y = 1.0 - (float)(vID % 2) * 2.0;\n"
"return float4(colorSample);\n"
//"return half4(input.texcoord.x, input.texcoord.y, 0.0, 1.0);\n" "return out;\n"
"}\n"; "}\n"
#endif "struct Vertex\n"
"{\n"
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// "float4 position [[position]];\n"
"};\n"
#if !defined(USE_METAL_API)
void* g_updateBuffer = 0; "vertex VertexOutput vertFunc2(unsigned int vID[[vertex_id]], device Vertex *pos [[buffer(0)]])\n"
int g_width = 0; "{\n"
int g_height = 0; "VertexOutput out;\n"
#endif
static OSXWindow *s_window; "out.pos = pos[vID].position;\n"
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// "out.texcoord.x = (float)(vID / 2);\n"
"out.texcoord.y = 1.0 - (float)(vID % 2);\n"
#if defined(USE_METAL_API)
static bool create_shaders() { "return out;\n"
// Error "}\n"
NSError* nsError = NULL;
NSError** nsErrorPtr = &nsError; "fragment float4 fragFunc(VertexOutput input [[stage_in]],\n"
"texture2d<half> colorTexture [[ texture(0) ]])\n"
id<MTLLibrary> library = [g_metal_device newLibraryWithSource:g_shadersSrc "{\n"
options:[[MTLCompileOptions alloc] init] "constexpr sampler textureSampler(mag_filter::nearest, min_filter::nearest);\n"
error:nsErrorPtr];
// Sample the texture to obtain a color
// Error update "const half4 colorSample = colorTexture.sample(textureSampler, input.texcoord);\n"
if (nsError || !library) {
NSLog(@"Unable to create shaders %@", nsError); // We return the color of the texture
return false; "return float4(colorSample);\n"
} //"return float4(input.texcoord.x, input.texcoord.y, 0.0, 1.0);\n"
"}\n";
g_library = library;
NSLog(@"Names %@", [g_library functionNames]); #endif
id<MTLFunction> vertex_shader_func = [g_library newFunctionWithName:@"vertFunc"]; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
id<MTLFunction> fragment_shader_func = [g_library newFunctionWithName:@"fragFunc"];
#if defined(USE_METAL_API)
if (!vertex_shader_func) { static bool create_shaders() {
printf("Unable to get vertFunc!\n"); // Error
return false; NSError* nsError = NULL;
} NSError** nsErrorPtr = &nsError;
if (!fragment_shader_func) { id<MTLLibrary> library = [g_metal_device newLibraryWithSource:g_shadersSrc
printf("Unable to get fragFunc!\n"); options:[[MTLCompileOptions alloc] init]
return false; error:nsErrorPtr];
}
// Error update
// Create a reusable pipeline state if (nsError || !library) {
MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; NSLog(@"Unable to create shaders %@", nsError);
pipelineStateDescriptor.label = @"MyPipeline"; return false;
pipelineStateDescriptor.vertexFunction = vertex_shader_func; }
pipelineStateDescriptor.fragmentFunction = fragment_shader_func;
pipelineStateDescriptor.colorAttachments[0].pixelFormat = 80; //bgra8Unorm; g_library = library;
NSLog(@"Names %@", [g_library functionNames]);
NSError *error = NULL;
g_pipeline_state = [g_metal_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error]; id<MTLFunction> vertex_shader_func = [g_library newFunctionWithName:@"vertFunc2"];
if (!g_pipeline_state) id<MTLFunction> fragment_shader_func = [g_library newFunctionWithName:@"fragFunc"];
{
NSLog(@"Failed to created pipeline state, error %@", error); if (!vertex_shader_func) {
} printf("Unable to get vertFunc!\n");
return false;
return true; }
}
#endif if (!fragment_shader_func) {
printf("Unable to get fragFunc!\n");
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// return false;
}
int mfb_open(const char* name, int width, int height)
{ // Create a reusable pipeline state
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
pipelineStateDescriptor.label = @"MyPipeline";
#if !defined(USE_METAL_API) pipelineStateDescriptor.vertexFunction = vertex_shader_func;
g_width = width; pipelineStateDescriptor.fragmentFunction = fragment_shader_func;
g_height = height; pipelineStateDescriptor.colorAttachments[0].pixelFormat = 80; //bgra8Unorm;
#endif
[NSApplication sharedApplication]; NSError *error = NULL;
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; g_pipeline_state = [g_metal_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error];
if (!g_pipeline_state)
#if defined(USE_METAL_API) {
g_metal_device = MTLCreateSystemDefaultDevice(); NSLog(@"Failed to created pipeline state, error %@", error);
}
if (!g_metal_device) {
printf("Your device/OS doesn't support Metal."); return true;
return -1; }
} #endif
if (!create_shaders()) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
return -2;
} int mfb_open(const char* name, int width, int height)
#endif {
return mfb_open_ex(name, width, height, 0);
NSWindowStyleMask styles = NSWindowStyleMaskResizable | NSWindowStyleMaskClosable | NSWindowStyleMaskTitled; }
NSRect rectangle = NSMakeRect(0, 0, width, height); ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
s_window = [[OSXWindow alloc] initWithContentRect:rectangle styleMask:styles backing:NSBackingStoreBuffered defer:NO];
int mfb_open_ex(const char* name, int width, int height, int flags)
if (!s_window) {
return -3; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
#if defined(USE_METAL_API) init_keycodes();
s_window->draw_buffer = malloc(width * height * 4);
g_window_data.window_width = width;
if (!s_window->draw_buffer) g_window_data.window_height = height;
return -4;
g_window_data.dst_width = width;
// Setup command queue g_window_data.dst_height = height;
g_command_queue = [g_metal_device newCommandQueue];
g_window_data.buffer_width = width;
WindowViewController* viewController = [WindowViewController new]; g_window_data.buffer_height = height;
MTLTextureDescriptor* textureDescriptor = [[MTLTextureDescriptor alloc] init]; [NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
// 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) #if defined(USE_METAL_API)
textureDescriptor.pixelFormat = MTLPixelFormatBGRA8Unorm; g_metal_device = MTLCreateSystemDefaultDevice();
// Set the pixel dimensions of the texture if (!g_metal_device) {
textureDescriptor.width = width; printf("Your device/OS doesn't support Metal.");
textureDescriptor.height = height; return -1;
}
// Create the texture from the device by using the descriptor
if (!create_shaders()) {
for (int i = 0; i < MaxBuffersInFlight; ++i) { return -2;
viewController->m_texture_buffers[i] = [g_metal_device newTextureWithDescriptor:textureDescriptor]; }
} #endif
// Used for syncing the CPU and GPU NSWindowStyleMask styles = NSWindowStyleMaskClosable | NSWindowStyleMaskTitled;
viewController->m_semaphore = dispatch_semaphore_create(MaxBuffersInFlight);
viewController->m_draw_buffer = s_window->draw_buffer; if (flags & WF_BORDERLESS)
viewController->m_width = width; styles |= NSWindowStyleMaskBorderless;
viewController->m_height = height;
if (flags & WF_RESIZABLE)
MTKView* view = [[MTKView alloc] initWithFrame:rectangle]; styles |= NSWindowStyleMaskResizable;
view.device = g_metal_device;
view.delegate = viewController; NSRect rectangle = NSMakeRect(0, 0, width, height);
view.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; g_window_data.window = [[OSXWindow alloc] initWithContentRect:rectangle styleMask:styles backing:NSBackingStoreBuffered defer:NO];
[s_window.contentView addSubview:view]; if (!g_window_data.window)
return -3;
s_window->width = width;
s_window->height = height; #if defined(USE_METAL_API)
g_window_data.draw_buffer = malloc(width * height * 4);
//[s_window updateSize];
#endif if (!g_window_data.draw_buffer)
return -4;
[s_window setTitle:[NSString stringWithUTF8String:name]];
[s_window setReleasedWhenClosed:NO]; // Setup command queue
[s_window performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) withObject:nil waitUntilDone:YES]; g_command_queue = [g_metal_device newCommandQueue];
[s_window center]; WindowViewController* viewController = [WindowViewController new];
[NSApp activateIgnoringOtherApps:YES]; MTLTextureDescriptor* textureDescriptor = [[MTLTextureDescriptor alloc] init];
#if defined(USE_METAL_API) // Indicate that each pixel has a blue, green, red, and alpha channel, where each channel is
[NSApp finishLaunching]; // an 8-bit unsigned normalized value (i.e. 0 maps to 0.0 and 255 maps to 1.0)
#endif textureDescriptor.pixelFormat = MTLPixelFormatBGRA8Unorm;
[pool drain]; // Set the pixel dimensions of the texture
textureDescriptor.width = width;
return 1; textureDescriptor.height = height;
}
// Create the texture from the device by using the descriptor
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
for (int i = 0; i < MaxBuffersInFlight; ++i) {
void mfb_close() viewController->m_texture_buffers[i] = [g_metal_device newTextureWithDescriptor:textureDescriptor];
{ }
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
// Used for syncing the CPU and GPU
if (s_window) viewController->m_semaphore = dispatch_semaphore_create(MaxBuffersInFlight);
[s_window close]; viewController->m_draw_buffer = g_window_data.draw_buffer;
viewController->m_width = width;
[pool drain]; viewController->m_height = height;
}
MTKView* view = [[MTKView alloc] initWithFrame:rectangle];
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// view.device = g_metal_device;
view.delegate = viewController;
static int updateEvents() view.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
{ [g_window_data.window.contentView addSubview:view];
int state = 0;
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; g_window_data.buffer_width = width;
NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; g_window_data.buffer_height = height;
if (event)
{ //[g_window_data.window updateSize];
switch ([event type]) #endif
{
case NSEventTypeKeyDown: [g_window_data.window setTitle:[NSString stringWithUTF8String:name]];
case NSEventTypeKeyUp: [g_window_data.window setReleasedWhenClosed:NO];
{ [g_window_data.window performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) withObject:nil waitUntilDone:YES];
state = -1; [g_window_data.window setAcceptsMouseMovedEvents:YES];
break;
} [g_window_data.window center];
default : [NSApp activateIgnoringOtherApps:YES];
{
[NSApp sendEvent:event]; #if defined(USE_METAL_API)
break; [NSApp finishLaunching];
} #endif
}
} if (g_keyboard_func == 0x0) {
[pool release]; mfb_keyboard_callback(keyboard_default);
}
if (s_window->closed)
state = -1; #if defined(USE_METAL_API)
NSLog(@"Window created using Metal API");
return state; #else
} NSLog(@"Window created using Cocoa API");
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[pool drain];
int mfb_update(void* buffer)
{ return 1;
#if defined(USE_METAL_API) }
memcpy(s_window->draw_buffer, buffer, s_window->width * s_window->height * 4);
#else ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
g_updateBuffer = buffer;
#endif void keyboard_default(void *user_data, Key key, KeyMod mod, bool isPressed) {
int state = updateEvents(); kUnused(user_data);
[[s_window contentView] setNeedsDisplay:YES]; kUnused(mod);
return state; 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;
}

View File

@ -1,16 +1,10 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
// @class OSXWindowFrameView; // @class OSXWindowFrameView;
@interface OSXWindow : NSWindow @interface OSXWindow : NSWindow<NSWindowDelegate>
{ {
NSView* childContentView; NSView *childContentView;
@public bool closed; }
#if defined(USE_METAL_API)
@public int width; @end
@public int height;
@public void* draw_buffer;
#endif
}
@end

View File

@ -1,155 +1,276 @@
#import "OSXWindow.h" #import "OSXWindow.h"
#import "OSXWindowFrameView.h" #import "OSXWindowFrameView.h"
#include "OSXWindowData.h"
@implementation OSXWindow #include <MiniFB_internal.h>
#include <MiniFB_enums.h>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
extern SWindowData g_window_data;
#if !defined(USE_METAL_API) extern short int g_keycodes[512];
- (id)initWithContentRect:(NSRect)contentRect
styleMask:(NSWindowStyleMask)windowStyle bool gActive = false;
backing:(NSBackingStoreType)bufferingType
defer:(BOOL)deferCreation @implementation OSXWindow
{
self = [super ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
initWithContentRect:contentRect
styleMask:windowStyle - (id)initWithContentRect:(NSRect)contentRect
backing:bufferingType styleMask:(NSWindowStyleMask)windowStyle
defer:deferCreation]; backing:(NSBackingStoreType)bufferingType
if (self) defer:(BOOL)deferCreation
{ {
[self setOpaque:YES]; self = [super
[self setBackgroundColor:[NSColor clearColor]]; initWithContentRect:contentRect
styleMask:windowStyle
[[NSNotificationCenter defaultCenter] backing:bufferingType
addObserver:self defer:deferCreation];
selector:@selector(mainWindowChanged:)
name:NSWindowDidBecomeMainNotification if (self)
object:self]; {
[self setOpaque:YES];
[[NSNotificationCenter defaultCenter] [self setBackgroundColor:[NSColor clearColor]];
addObserver:self
selector:@selector(mainWindowChanged:) self.delegate = self;
name:NSWindowDidResignMainNotification }
object:self]; return self;
}
[[NSNotificationCenter defaultCenter]
addObserver:self ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
selector:@selector(willClose)
name:NSWindowWillCloseNotification - (void)dealloc
object:self]; {
[[NSNotificationCenter defaultCenter]
closed = false; removeObserver:self];
} [super dealloc];
return self; }
}
#endif ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (void)setContentSize:(NSSize)newSize
{
- (void)dealloc NSSize sizeDelta = newSize;
{ NSSize childBoundsSize = [childContentView bounds].size;
[[NSNotificationCenter defaultCenter] sizeDelta.width -= childBoundsSize.width;
removeObserver:self]; sizeDelta.height -= childBoundsSize.height;
[super dealloc];
} OSXWindowFrameView *frameView = [super contentView];
NSSize newFrameSize = [frameView bounds].size;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// newFrameSize.width += sizeDelta.width;
newFrameSize.height += sizeDelta.height;
- (void)setContentSize:(NSSize)newSize
{ [super setContentSize:newFrameSize];
NSSize sizeDelta = newSize; }
NSSize childBoundsSize = [childContentView bounds].size;
sizeDelta.width -= childBoundsSize.width; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
sizeDelta.height -= childBoundsSize.height;
-(void)flagsChanged:(NSEvent *)event
OSXWindowFrameView *frameView = [super contentView]; {
NSSize newFrameSize = [frameView bounds].size; const uint32_t flags = [event modifierFlags];
newFrameSize.width += sizeDelta.width; uint32_t mod_keys = 0, mod_keys_aux = 0;
newFrameSize.height += sizeDelta.height;
//NSEventModifierFlagHelp = 1 << 22,
[super setContentSize:newFrameSize]; //NSEventModifierFlagFunction = 1 << 23,
} if(flags & NSEventModifierFlagCapsLock) {
mod_keys |= KB_MOD_CAPS_LOCK;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// }
if(flags & NSEventModifierFlagShift) {
- (void)mainWindowChanged:(NSNotification *)aNotification mod_keys |= KB_MOD_SHIFT;
{ }
(void)aNotification; if(flags & NSEventModifierFlagControl) {
} mod_keys |= KB_MOD_CONTROL;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// if(flags & NSEventModifierFlagOption) {
mod_keys |= KB_MOD_ALT;
- (void)setContentView:(NSView *)aView }
{ if(flags & NSEventModifierFlagCommand) {
if ([childContentView isEqualTo:aView]) mod_keys |= KB_MOD_SUPER;
{ }
return; if(flags & NSEventModifierFlagNumericPad) {
} mod_keys |= KB_MOD_NUM_LOCK;
}
NSRect bounds = [self frame];
bounds.origin = NSZeroPoint; if(mod_keys != g_window_data.mod_keys) {
short int keyCode = keycodes[[event keyCode] & 0x1ff];
OSXWindowFrameView *frameView = [super contentView]; if(keyCode != KB_KEY_UNKNOWN) {
if (!frameView) mod_keys_aux = mod_keys ^ g_window_data.mod_keys;
{ if(mod_keys_aux & KB_MOD_CAPS_LOCK) {
frameView = [[[OSXWindowFrameView alloc] initWithFrame:bounds] autorelease]; kCall(g_keyboard_func, keyCode, mod_keys, (mod_keys & KB_MOD_CAPS_LOCK) != 0);
}
[super setContentView:frameView]; if(mod_keys_aux & KB_MOD_SHIFT) {
} kCall(g_keyboard_func, keyCode, mod_keys, (mod_keys & KB_MOD_SHIFT) != 0);
}
if (childContentView) if(mod_keys_aux & KB_MOD_CONTROL) {
{ kCall(g_keyboard_func, keyCode, mod_keys, (mod_keys & KB_MOD_CONTROL) != 0);
[childContentView removeFromSuperview]; }
} if(mod_keys_aux & KB_MOD_ALT) {
childContentView = aView; kCall(g_keyboard_func, keyCode, mod_keys, (mod_keys & KB_MOD_ALT) != 0);
[childContentView setFrame:[self contentRectForFrameRect:bounds]]; }
[childContentView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; if(mod_keys_aux & KB_MOD_SUPER) {
[frameView addSubview:childContentView]; 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);
}
- (NSView *)contentView }
{ }
return childContentView; g_window_data.mod_keys = mod_keys;
} //NSLog(@"KeyCode: %d (%x) - %x", [event keyCode], [event keyCode], flags);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// [super flagsChanged:event];
}
- (BOOL)canBecomeKeyWindow
{ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
return YES;
} - (void)keyDown:(NSEvent *)event
{
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// short int keyCode = keycodes[[event keyCode] & 0x1ff];
kCall(g_keyboard_func, keyCode, g_window_data.mod_keys, true);
- (BOOL)canBecomeMainWindow }
{
return YES; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
- (void)keyUp:(NSEvent *)event
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// {
short int keyCode = keycodes[[event keyCode] & 0x1ff];
- (NSRect)contentRectForFrameRect:(NSRect)windowFrame kCall(g_keyboard_func, keyCode, g_window_data.mod_keys, false);
{ }
windowFrame.origin = NSZeroPoint;
return NSInsetRect(windowFrame, 0, 0); ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// {
NSString *characters;
+ (NSRect)frameRectForContentRect:(NSRect)windowContentRect styleMask:(NSWindowStyleMask)windowStyle NSUInteger length;
{
(void)windowStyle; kUnused(replacementRange);
return NSInsetRect(windowContentRect, 0, 0);
} if ([string isKindOfClass:[NSAttributedString class]])
characters = [string string];
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// else
characters = (NSString*) string;
- (void)willClose
{ length = [characters length];
closed = true; for (NSUInteger i = 0; i < length; i++)
} {
const unichar code = [characters characterAtIndex:i];
@end 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

View File

@ -0,0 +1,22 @@
#pragma once
#include <MiniFB_enums.h>
@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;

View File

@ -1,35 +1,39 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#if defined(USE_METAL_API) #if defined(USE_METAL_API)
#import <MetalKit/MetalKit.h> #import <MetalKit/MetalKit.h>
// Number of textures in flight (tripple buffered) typedef struct Vertex {
static const int MaxBuffersInFlight = 3; float x, y, z, w;
} Vertex;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Number of textures in flight (tripple buffered)
@interface WindowViewController : NSViewController<MTKViewDelegate> static const int MaxBuffersInFlight = 3;
{
@public id<MTLTexture> m_texture_buffers[MaxBuffersInFlight]; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@public int m_current_buffer;
@public void* m_draw_buffer; @interface WindowViewController : NSViewController<MTKViewDelegate>
@public int m_width; {
@public int m_height; @public id<MTLTexture> m_texture_buffers[MaxBuffersInFlight];
// Used for syncing with CPU/GPU @public int m_current_buffer;
@public dispatch_semaphore_t m_semaphore; @public void* m_draw_buffer;
} @public int m_width;
@public int m_height;
@end // Used for syncing with CPU/GPU
#endif @public dispatch_semaphore_t m_semaphore;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@end
@interface OSXWindowFrameView : NSView #endif
{
#if defined(USE_METAL_API) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@private NSTrackingArea* trackingArea;
#endif @interface OSXWindowFrameView : NSView
} {
#if defined(USE_METAL_API)
@end @private NSTrackingArea* trackingArea;
#endif
}
@end

View File

@ -1,155 +1,319 @@
#import "OSXWindowFrameView.h" #import "OSXWindowFrameView.h"
#import "OSXWindow.h"
#if defined(USE_METAL_API) #include "OSXWindowData.h"
#import <MetalKit/MetalKit.h> #include <MiniFB_internal.h>
id<MTLDevice> g_metal_device; extern SWindowData g_window_data;
id<MTLCommandQueue> g_command_queue;
id<MTLLibrary> g_library; #if defined(USE_METAL_API)
id<MTLRenderPipelineState> g_pipeline_state; #import <MetalKit/MetalKit.h>
@implementation WindowViewController id<MTLDevice> g_metal_device;
id<MTLCommandQueue> g_command_queue;
-(void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size id<MTLLibrary> g_library;
{ id<MTLRenderPipelineState> g_pipeline_state;
(void)view;
(void)size; extern Vertex gVertices[4];
// resize
} @implementation WindowViewController
-(void)drawInMTKView:(nonnull MTKView *)view -(void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size
{ {
// Wait to ensure only MaxBuffersInFlight number of frames are getting proccessed (void)view;
// by any stage in the Metal pipeline (App, Metal, Drivers, GPU, etc) (void)size;
dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER); // resize
}
// 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; -(void)drawInMTKView:(nonnull MTKView *)view
{
// Calculate the number of bytes per row of our image. // Wait to ensure only MaxBuffersInFlight number of frames are getting proccessed
NSUInteger bytesPerRow = 4 * m_width; // by any stage in the Metal pipeline (App, Metal, Drivers, GPU, etc)
MTLRegion region = { { 0, 0, 0 }, { m_width, m_height, 1 } }; dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
// Copy the bytes from our data object into the texture // Iterate through our Metal buffers, and cycle back to the first when we've written to MaxBuffersInFlight
[m_texture_buffers[m_current_buffer] replaceRegion:region m_current_buffer = (m_current_buffer + 1) % MaxBuffersInFlight;
mipmapLevel:0 withBytes:m_draw_buffer bytesPerRow:bytesPerRow];
// Calculate the number of bytes per row of our image.
// Create a new command buffer for each render pass to the current drawable NSUInteger bytesPerRow = 4 * m_width;
id<MTLCommandBuffer> commandBuffer = [g_command_queue commandBuffer]; MTLRegion region = { { 0, 0, 0 }, { m_width, m_height, 1 } };
commandBuffer.label = @"minifb_command_buffer";
// Copy the bytes from our data object into the texture
// Add completion hander which signals _inFlightSemaphore when Metal and the GPU has fully [m_texture_buffers[m_current_buffer] replaceRegion:region
// finished processing the commands we're encoding this frame. This indicates when the mipmapLevel:0 withBytes:m_draw_buffer bytesPerRow:bytesPerRow];
// 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 // Create a new command buffer for each render pass to the current drawable
// corrupting the rendering. id<MTLCommandBuffer> commandBuffer = [g_command_queue commandBuffer];
__block dispatch_semaphore_t block_sema = m_semaphore; commandBuffer.label = @"minifb_command_buffer";
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer)
{ // Add completion hander which signals _inFlightSemaphore when Metal and the GPU has fully
(void)buffer; // finished processing the commands we're encoding this frame. This indicates when the
dispatch_semaphore_signal(block_sema); // 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.
MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor; __block dispatch_semaphore_t block_sema = m_semaphore;
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer)
if (renderPassDescriptor != nil) {
{ (void)buffer;
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(1.0, 0.0, 0.0, 1.0); dispatch_semaphore_signal(block_sema);
}];
// Create a render command encoder so we can render into something
id<MTLRenderCommandEncoder> renderEncoder = MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor;
[commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
renderEncoder.label = @"minifb_command_encoder"; if (renderPassDescriptor != nil)
{
// Set render command encoder state renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0);
[renderEncoder setRenderPipelineState:g_pipeline_state];
// Create a render command encoder so we can render into something
[renderEncoder setFragmentTexture:m_texture_buffers[m_current_buffer] atIndex:0]; id<MTLRenderCommandEncoder> renderEncoder =
[commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
// Draw the vertices of our quads renderEncoder.label = @"minifb_command_encoder";
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle
vertexStart:0 // Set render command encoder state
vertexCount:3]; [renderEncoder setRenderPipelineState:g_pipeline_state];
// We're done encoding commands [renderEncoder setVertexBytes:gVertices
[renderEncoder endEncoding]; length:sizeof(gVertices)
atIndex:0];
// Schedule a present once the framebuffer is complete using the current drawable
[commandBuffer presentDrawable:view.currentDrawable]; [renderEncoder setFragmentTexture:m_texture_buffers[m_current_buffer] atIndex:0];
}
// Draw the vertices of our quads
// Finalize rendering here & push the command buffer to the GPU // [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle
[commandBuffer commit]; // vertexStart:0
} // vertexCount:3];
@end [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip
#endif vertexStart:0
vertexCount:4];
@implementation OSXWindowFrameView
// We're done encoding commands
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// [renderEncoder endEncoding];
#if defined(USE_METAL_API) // Schedule a present once the framebuffer is complete using the current drawable
-(void)updateTrackingAreas [commandBuffer presentDrawable:view.currentDrawable];
{ }
if(trackingArea != nil) {
[self removeTrackingArea:trackingArea]; // Finalize rendering here & push the command buffer to the GPU
[trackingArea release]; [commandBuffer commit];
} }
@end
int opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways); #endif
trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds]
options:opts @implementation OSXWindowFrameView
owner:self
userInfo:nil]; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[self addTrackingArea:trackingArea];
} #if defined(USE_METAL_API)
#else -(void)updateTrackingAreas
extern void* g_updateBuffer; {
extern int g_width; if(trackingArea != nil) {
extern int g_height; [self removeTrackingArea:trackingArea];
[trackingArea release];
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// }
- (NSRect)resizeRect int opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways);
{ trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds]
const CGFloat resizeBoxSize = 16.0; options:opts
const CGFloat contentViewPadding = 5.5; owner:self
userInfo:nil];
NSRect contentViewRect = [[self window] contentRectForFrameRect:[[self window] frame]]; [self addTrackingArea:trackingArea];
NSRect resizeRect = NSMakeRect( }
NSMaxX(contentViewRect) + contentViewPadding, #else
NSMinY(contentViewRect) - resizeBoxSize - contentViewPadding,
resizeBoxSize, ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
resizeBoxSize);
- (NSRect)resizeRect
return resizeRect; {
} const CGFloat resizeBoxSize = 16.0;
const CGFloat contentViewPadding = 5.5;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
NSRect contentViewRect = [[self window] contentRectForFrameRect:[[self window] frame]];
- (void)drawRect:(NSRect)rect NSRect resizeRect = NSMakeRect(
{ NSMaxX(contentViewRect) + contentViewPadding,
(void)rect; NSMinY(contentViewRect) - resizeBoxSize - contentViewPadding,
resizeBoxSize,
if (!g_updateBuffer) resizeBoxSize);
return;
return resizeRect;
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; }
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, g_updateBuffer, g_width * g_height * 4, NULL);
- (void)drawRect:(NSRect)rect
CGImageRef img = CGImageCreate(g_width, g_height, 8, 32, g_width * 4, space, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little, {
provider, NULL, false, kCGRenderingIntentDefault); (void)rect;
CGColorSpaceRelease(space); if (!g_window_data.window || !g_window_data.draw_buffer)
CGDataProviderRelease(provider); return;
CGContextDrawImage(context, CGRectMake(0, 0, g_width, g_height), img); CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
CGImageRelease(img); CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
} CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, g_window_data.draw_buffer, g_window_data.buffer_width * g_window_data.buffer_height * 4, NULL);
#endif
CGImageRef img = CGImageCreate(g_window_data.buffer_width, g_window_data.buffer_height, 8, 32, g_window_data.buffer_width * 4, space, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little,
@end 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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,57 @@
#pragma once
#include <MiniFB_enums.h>
#include <stdint.h>
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;

View File

@ -1,146 +1,571 @@
#include "MiniFB.h" #include <MiniFB.h>
#include <MiniFB_internal.h>
#define WIN32_LEAN_AND_MEAN #include "WinWindowData.h"
#include <windows.h>
#include <stdlib.h> #include <stdlib.h>
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static WNDCLASS s_wc; SWindowData g_window_data = { 0 };
static HWND s_wnd;
static int s_close = 0; long s_window_style = WS_POPUP | WS_SYSMENU | WS_CAPTION;
static int s_width;
static int s_height;
static HDC s_hdc;
static void* s_buffer;
static BITMAPINFO* s_bitmapInfo;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
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) switch (message)
{ {
case WM_PAINT: case WM_PAINT:
{ {
if (s_buffer) if (g_window_data.draw_buffer)
{ {
StretchDIBits(s_hdc, 0, 0, s_width, s_height, 0, 0, s_width, s_height, s_buffer, if (g_window_data.dst_offset_x > 0) {
s_bitmapInfo, DIB_RGB_COLORS, SRCCOPY); 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: break;
{ }
if ((wParam&0xFF) == 27)
s_close = 1;
break; case WM_DESTROY:
} case WM_CLOSE:
{
g_window_data.close = true;
break;
}
case WM_CLOSE: case WM_KEYDOWN:
{ case WM_SYSKEYDOWN:
s_close = 1; case WM_KEYUP:
break; 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: if (kb_key == KB_KEY_UNKNOWN)
{ return FALSE;
res = DefWindowProc(hWnd, message, wParam, lParam);
}
}
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) int mfb_open_ex(const char* title, int width, int height, int flags) {
{ RECT rect = { 0 };
RECT rect = { 0 }; int x, y;
s_wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW; init_keycodes();
s_wc.lpfnWndProc = WndProc;
s_wc.hCursor = LoadCursor(0, IDC_ARROW);
s_wc.lpszClassName = title;
RegisterClass(&s_wc);
rect.right = width; g_window_data.buffer_width = width;
rect.bottom = height; 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; DEVMODE settings;
rect.bottom -= rect.top; 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; if (ChangeDisplaySettings(&settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
s_height = height; flags = WF_FULLSCREEN_DESKTOP;
}
}
s_wnd = CreateWindowEx(0, if (flags & WF_BORDERLESS) {
title, title, s_window_style = WS_POPUP;
WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX & ~WS_THICKFRAME, }
CW_USEDEFAULT, CW_USEDEFAULT,
rect.right, rect.bottom,
0, 0, 0, 0);
if (!s_wnd) if (flags & WF_RESIZABLE) {
return 0; 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); width = GetSystemMetrics(SM_CXFULLSCREEN);
s_bitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); height = GetSystemMetrics(SM_CYFULLSCREEN);
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;
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) int mfb_update(void* buffer)
{ {
MSG msg; MSG msg;
s_buffer = buffer; if (buffer == 0x0)
return -2;
InvalidateRect(s_wnd, NULL, TRUE); if (g_window_data.close == true)
SendMessage(s_wnd, WM_PAINT, 0, 0); return -1;
while (PeekMessage(&msg, s_wnd, 0, 0, PM_REMOVE)) g_window_data.draw_buffer = buffer;
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (s_close == 1) InvalidateRect(g_window_data.window, NULL, TRUE);
return -1; 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() void mfb_close()
{ {
s_buffer = 0; g_window_data.draw_buffer = 0x0;
free(s_bitmapInfo); if (g_window_data.s_bitmapInfo != 0x0) {
ReleaseDC(s_wnd, s_hdc); free(g_window_data.s_bitmapInfo);
DestroyWindow(s_wnd); }
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;
}

View File

@ -0,0 +1,30 @@
#pragma once
#include <MiniFB_enums.h>
#include <stdint.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
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;

View File

@ -1,148 +1,632 @@
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/Xutil.h> #include <X11/Xutil.h>
#include <MiniFB.h> #include <X11/XKBlib.h>
#include <X11/keysym.h>
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #include <X11/Xatom.h>
#include <X11/cursorfont.h>
#define KEY_FUNCTION 0xFF #include <stdlib.h>
#define KEY_ESC 0x1B #include <MiniFB.h>
#include <MiniFB_internal.h>
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #include "X11WindowData.h"
static Display* s_display; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static int s_screen;
static int s_width; SWindowData g_window_data = { 0 };
static int s_height;
static Window s_window; extern void
static GC s_gc; stretch_image(uint32_t *srcImage, uint32_t srcX, uint32_t srcY, uint32_t srcWidth, uint32_t srcHeight, uint32_t srcPitch,
static XImage *s_ximage; uint32_t *dstImage, uint32_t dstX, uint32_t dstY, uint32_t dstWidth, uint32_t dstHeight, uint32_t dstPitch);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int mfb_open(const char* title, int width, int height) int mfb_open_ex(const char* title, int width, int height, int flags) {
{ int depth, i, formatCount, convDepth = -1;
int depth, i, formatCount, convDepth = -1; XPixmapFormatValues* formats;
XPixmapFormatValues* formats; XSetWindowAttributes windowAttributes;
XSetWindowAttributes windowAttributes; XSizeHints sizeHints;
XSizeHints sizeHints; Visual* visual;
Visual* visual;
g_window_data.display = XOpenDisplay(0);
s_display = XOpenDisplay(0); if (!g_window_data.display)
return -1;
if (!s_display)
return -1; init_keycodes();
s_screen = DefaultScreen(s_display); g_window_data.screen = DefaultScreen(g_window_data.display);
visual = DefaultVisual(s_display, s_screen);
formats = XListPixmapFormats(s_display, &formatCount); visual = DefaultVisual(g_window_data.display, g_window_data.screen);
depth = DefaultDepth(s_display, s_screen); formats = XListPixmapFormats(g_window_data.display, &formatCount);
Window defaultRootWindow = DefaultRootWindow(s_display); depth = DefaultDepth(g_window_data.display, g_window_data.screen);
for (i = 0; i < formatCount; ++i) Window defaultRootWindow = DefaultRootWindow(g_window_data.display);
{
if (depth == formats[i].depth) for (i = 0; i < formatCount; ++i)
{ {
convDepth = formats[i].bits_per_pixel; if (depth == formats[i].depth)
break; {
} convDepth = formats[i].bits_per_pixel;
} break;
}
XFree(formats); }
// We only support 32-bit right now XFree(formats);
if (convDepth != 32)
{ // We only support 32-bit right now
XCloseDisplay(s_display); if (convDepth != 32)
return -1; {
} XCloseDisplay(g_window_data.display);
return -1;
int screenWidth = DisplayWidth(s_display, s_screen); }
int screenHeight = DisplayHeight(s_display, s_screen);
int screenWidth = DisplayWidth(g_window_data.display, g_window_data.screen);
windowAttributes.border_pixel = BlackPixel(s_display, s_screen); int screenHeight = DisplayHeight(g_window_data.display, g_window_data.screen);
windowAttributes.background_pixel = BlackPixel(s_display, s_screen);
windowAttributes.backing_store = NotUseful; windowAttributes.border_pixel = BlackPixel(g_window_data.display, g_window_data.screen);
windowAttributes.background_pixel = BlackPixel(g_window_data.display, g_window_data.screen);
s_window = XCreateWindow(s_display, defaultRootWindow, (screenWidth - width) / 2, windowAttributes.backing_store = NotUseful;
(screenHeight - height) / 2, width, height, 0, depth, InputOutput,
visual, CWBackPixel | CWBorderPixel | CWBackingStore, int posX, posY;
&windowAttributes); int windowWidth, windowHeight;
if (!s_window)
return 0; g_window_data.window_width = width;
g_window_data.window_height = height;
XSelectInput(s_display, s_window, KeyPressMask | KeyReleaseMask); g_window_data.buffer_width = width;
XStoreName(s_display, s_window, title); g_window_data.buffer_height = height;
g_window_data.dst_offset_x = 0;
sizeHints.flags = PPosition | PMinSize | PMaxSize; g_window_data.dst_offset_y = 0;
sizeHints.x = 0; g_window_data.dst_width = width;
sizeHints.y = 0; g_window_data.dst_height = height;
sizeHints.min_width = width;
sizeHints.max_width = width; if (flags & WF_FULLSCREEN_DESKTOP) {
sizeHints.min_height = height; posX = 0;
sizeHints.max_height = height; posY = 0;
windowWidth = screenWidth;
XSetWMNormalHints(s_display, s_window, &sizeHints); windowHeight = screenHeight;
XClearWindow(s_display, s_window); }
XMapRaised(s_display, s_window); else {
XFlush(s_display); posX = (screenWidth - width) / 2;
posY = (screenHeight - height) / 2;
s_gc = DefaultGC(s_display, s_screen); windowWidth = width;
windowHeight = height;
s_ximage = XCreateImage(s_display, CopyFromParent, depth, ZPixmap, 0, NULL, width, height, 32, width * 4); }
s_width = width; g_window_data.window = XCreateWindow(
s_height = height; g_window_data.display,
defaultRootWindow,
return 1; posX, posY,
} windowWidth, windowHeight,
0,
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// depth,
InputOutput,
static int processEvents() visual,
{ CWBackPixel | CWBorderPixel | CWBackingStore,
XEvent event; &windowAttributes);
KeySym sym; if (!g_window_data.window)
return 0;
if (!XPending(s_display))
return 0; XSelectInput(g_window_data.display, g_window_data.window,
KeyPressMask | KeyReleaseMask
XNextEvent(s_display, &event); | ButtonPressMask | ButtonReleaseMask | PointerMotionMask
| StructureNotifyMask | ExposureMask
if (event.type != KeyPress) | FocusChangeMask
return 0; | EnterWindowMask | LeaveWindowMask
);
sym = XLookupKeysym(&event.xkey, 0);
XStoreName(g_window_data.display, g_window_data.window, title);
if ((sym >> 8) != KEY_FUNCTION)
return 0; if (flags & WF_BORDERLESS) {
struct StyleHints {
if ((sym & 0xFF) == KEY_ESC) unsigned long flags;
return -1; unsigned long functions;
unsigned long decorations;
return 0; long inputMode;
} unsigned long status;
} sh = {
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// .flags = 2,
.functions = 0,
int mfb_update(void* buffer) .decorations = 0,
{ .inputMode = 0,
s_ximage->data = (char*)buffer; .status = 0,
};
XPutImage(s_display, s_window, s_gc, s_ximage, 0, 0, 0, 0, s_width, s_height); Atom sh_p = XInternAtom(g_window_data.display, "_MOTIF_WM_HINTS", True);
XFlush(s_display); XChangeProperty(g_window_data.display, g_window_data.window, sh_p, sh_p, 32, PropModeReplace, (unsigned char*)&sh, 5);
}
if (processEvents() < 0)
return -1; if (flags & WF_ALWAYS_ON_TOP) {
Atom sa_p = XInternAtom(g_window_data.display, "_NET_WM_STATE_ABOVE", False);
return 0; 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);
void mfb_close (void) 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);
{ }
s_ximage->data = NULL;
XDestroyImage(s_ximage); sizeHints.flags = PPosition | PMinSize | PMaxSize;
XDestroyWindow(s_display, s_window); sizeHints.x = 0;
XCloseDisplay(s_display); 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;
}

34
src/x11/X11WindowData.h Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include <MiniFB_enums.h>
#include <stdint.h>
#include <X11/Xlib.h>
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;

View File

@ -1,42 +1,163 @@
#include <MiniFB.h> #include <MiniFB.h>
#include <stdio.h>
#include <stdint.h>
#define kUnused(var) (void) var;
#define WIDTH 800 #define WIDTH 800
#define HEIGHT 600 #define HEIGHT 600
static unsigned int s_buffer[WIDTH * HEIGHT]; 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 main()
{ {
int noise, carry, seed = 0xbeef; int noise, carry, seed = 0xbeef;
if (!mfb_open("Noise Test", WIDTH, HEIGHT)) #if defined(__cplusplus)
return 0;
for (;;) Events e;
{
int i, state;
for (i = 0; i < WIDTH * HEIGHT; ++i) mfb_active_callback(&e, &Events::active);
{ mfb_resize_callback(&e, &Events::resize);
noise = seed; mfb_keyboard_callback(&e, &Events::keyboard);
noise >>= 3; mfb_char_input_callback(&e, &Events::char_input);
noise ^= seed; mfb_mouse_button_callback(&e, &Events::mouse_btn);
carry = noise & 1; mfb_mouse_move_callback(&e, &Events::mouse_move);
noise >>= 1; mfb_mouse_scroll_callback(&e, &Events::mouse_scroll);
seed >>= 1;
seed |= (carry << 30);
noise &= 0xFF;
s_buffer[i] = MFB_RGB(noise, noise, noise);
}
state = mfb_update(s_buffer); #else
if (state < 0) mfb_active_callback(active);
break; 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;
} }