├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── include ├── MiniFB.h ├── MiniFB_cpp.h ├── MiniFB_enums.h └── MiniFB_ios.h ├── src ├── MiniFB_common.c ├── MiniFB_cpp.cpp ├── MiniFB_internal.c ├── MiniFB_internal.h ├── MiniFB_linux.c ├── MiniFB_timer.c ├── WindowData.h ├── android │ ├── AndroidMiniFB.c │ └── WindowData_Android.h ├── gl │ ├── MiniFB_GL.c │ └── MiniFB_GL.h ├── ios │ ├── WindowData_IOS.h │ ├── iOSMiniFB.m │ ├── iOSView.h │ ├── iOSView.m │ ├── iOSViewController.h │ ├── iOSViewController.m │ ├── iOSViewDelegate.h │ └── iOSViewDelegate.m ├── macosx │ ├── MacMiniFB.m │ ├── OSXView.h │ ├── OSXView.m │ ├── OSXViewDelegate.h │ ├── OSXViewDelegate.m │ ├── OSXWindow.h │ ├── OSXWindow.m │ └── WindowData_OSX.h ├── wayland │ ├── WaylandMiniFB.c │ ├── WindowData_Way.h │ └── generated │ │ ├── xdg-shell-client-protocol.h │ │ └── xdg-shell-protocol.c ├── web │ └── WebMiniFB.c ├── windows │ ├── WinMiniFB.c │ └── WindowData_Win.h └── x11 │ ├── WindowData_X11.h │ └── X11MiniFB.c ├── tests ├── android │ ├── .idea │ │ └── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── cpp │ │ │ ├── CMakeLists.txt │ │ │ └── noise.c │ │ │ └── res │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ └── values │ │ │ └── strings.xml │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── fullscreen.c ├── hidpi.c ├── input_events.c ├── input_events_cpp.cpp ├── ios │ ├── AppDelegate.h │ ├── AppDelegate.m │ └── main.m ├── multiple_windows.c ├── noise.c ├── timer.c └── web │ ├── hidpi.html │ ├── index.html │ ├── input_events.html │ ├── multiple_windows.html │ ├── noise.html │ └── timer.html ├── tundra.lua └── units.lua /.gitignore: -------------------------------------------------------------------------------- 1 | .tundra2.* 2 | t2-output 3 | /.vscode 4 | /win 5 | /macosx 6 | /linux 7 | /build 8 | /.history 9 | /xcode 10 | .gradle 11 | .idea 12 | .cxx 13 | build 14 | kk 15 | vc 16 | cmake-build-debug 17 | cmake-build-debug-emscripten 18 | cmake-build-release 19 | cmake-build-release-emscripten 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | jobs: 4 | include: 5 | - os: linux 6 | dist: bionic 7 | name: "Linux X11" 8 | addons: 9 | apt: 10 | packages: 11 | - libx11-dev 12 | 13 | - os: linux 14 | dist: bionic 15 | name: "Linux Wayland" 16 | addons: 17 | apt: 18 | packages: 19 | - libwayland-dev 20 | env: 21 | - USE_WAYLAND=ON 22 | 23 | - os: osx 24 | name: "MacOS X Cocoa" 25 | 26 | - os: osx 27 | name: "MacOS X Metal" 28 | env: 29 | - USE_METAL_API=ON 30 | 31 | - os: osx 32 | name: "MacOS iOS" 33 | env: 34 | - USE_IOS=ON 35 | 36 | - os: windows 37 | name: "Windows Visual Studio" 38 | env: 39 | - USE_WINDOWS=ON 40 | 41 | script: 42 | - mkdir build 43 | - cd build 44 | - echo off 45 | - echo "---------" 46 | - echo "Config..." 47 | - echo "---------" 48 | - if [ $TRAVIS_OS_NAME = 'osx' ]; then 49 | if test -n "${USE_IOS}"; then 50 | cmake .. -G Xcode -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 ; 51 | elif test -n "${USE_METAL_API}"; then 52 | cmake .. -USE_METAL_API=ON; 53 | else 54 | cmake ..; 55 | fi 56 | elif [ $TRAVIS_OS_NAME = 'linux' ]; then 57 | if test -n "${USE_WAYLAND}"; then 58 | cmake .. -DUSE_WAYLAND=ON; 59 | else 60 | cmake ..; 61 | fi 62 | else 63 | cmake ..; 64 | fi 65 | - echo "--------" 66 | - echo "Build..." 67 | - echo "--------" 68 | - if test -n "${USE_IOS}"; then 69 | cmake --build . --target minifb ; 70 | else 71 | cmake --build . ; 72 | fi 73 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7) 2 | 3 | set(PROJECT_NAME MiniFB) 4 | project(${PROJECT_NAME}) 5 | 6 | message(STATUS "Processing " ${PROJECT_NAME}) 7 | 8 | include(GNUInstallDirs) 9 | 10 | # Detect iOS 11 | #-------------------------------------- 12 | if(NOT DEFINED IOS) 13 | if(DEFINED CMAKE_SYSTEM_NAME) 14 | string(TOLOWER CMAKE_SYSTEM_NAME CMAKE_SYSTEM_NAME_LOWER) 15 | if(CMAKE_SYSTEM_NAME_LOWER STREQUAL "ios") 16 | set(IOS true) 17 | endif() 18 | endif() 19 | endif() 20 | 21 | # Sources 22 | #-------------------------------------- 23 | set(SrcLib 24 | include/MiniFB.h 25 | include/MiniFB_cpp.h 26 | include/MiniFB_enums.h 27 | 28 | src/MiniFB_common.c 29 | src/MiniFB_cpp.cpp 30 | src/MiniFB_internal.c 31 | src/MiniFB_internal.h 32 | src/MiniFB_timer.c 33 | src/WindowData.h 34 | ) 35 | 36 | #-- 37 | set(SrcWindows 38 | src/windows/WinMiniFB.c 39 | src/windows/WindowData_Win.h 40 | ) 41 | 42 | #-- 43 | set(SrcMacOSX 44 | src/macosx/MacMiniFB.m 45 | src/macosx/OSXWindow.h 46 | src/macosx/OSXWindow.m 47 | src/macosx/OSXView.h 48 | src/macosx/OSXView.m 49 | src/macosx/OSXViewDelegate.h 50 | src/macosx/OSXViewDelegate.m 51 | src/macosx/WindowData_OSX.h 52 | ) 53 | 54 | #-- 55 | set(SrcIOS 56 | src/ios/WindowData_IOS.h 57 | src/ios/iOSMiniFB.m 58 | src/ios/iOSView.h 59 | src/ios/iOSView.m 60 | src/ios/iOSViewController.h 61 | src/ios/iOSViewController.m 62 | src/ios/iOSViewDelegate.h 63 | src/ios/iOSViewDelegate.m 64 | include/MiniFB_ios.h 65 | ) 66 | 67 | #-- 68 | set(SrcWayland 69 | src/wayland/generated/xdg-shell-protocol.c 70 | src/wayland/WaylandMiniFB.c 71 | src/wayland/WindowData_Way.h 72 | src/MiniFB_linux.c 73 | ) 74 | 75 | #-- 76 | set(SrcX11 77 | src/x11/X11MiniFB.c 78 | src/x11/WindowData_X11.h 79 | src/MiniFB_linux.c 80 | ) 81 | 82 | set(SrcGL 83 | src/gl/MiniFB_GL.h 84 | src/gl/MiniFB_GL.c 85 | ) 86 | 87 | #-- 88 | set(SrcWeb 89 | src/web/WebMiniFB.c 90 | ) 91 | 92 | # Set features 93 | #-------------------------------------- 94 | set(CMAKE_CXX_STANDARD 11) 95 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 96 | set(CMAKE_CXX_EXTENSIONS OFF) 97 | 98 | #-------------------------------------- 99 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 100 | 101 | # Code generation options 102 | #-------------------------------------- 103 | option(MINIFB_BUILD_EXAMPLES "Build minifb example programs" TRUE) 104 | option(MINIFB_AVOID_CPP_HEADERS "Avoid including C++ Headers" FALSE) 105 | 106 | if(APPLE AND NOT IOS) 107 | option(USE_METAL_API "Build the project using metal API code" ON) 108 | option(USE_INVERTED_Y_ON_MACOS "Use default mouse position: (0, 0) at (left, down)" OFF) 109 | elseif(UNIX) 110 | if (NOT EMSCRIPTEN) 111 | option(USE_WAYLAND_API "Build the project using wayland API code" OFF) 112 | if(NOT USE_WAYLAND_API) 113 | option(USE_OPENGL_API "Build the project using OpenGL API code" ON) 114 | endif() 115 | endif() 116 | elseif(WIN32) 117 | option(USE_OPENGL_API "Build the project using OpenGL API code" ON) 118 | endif() 119 | 120 | # Set GCC/Clang flags 121 | #-------------------------------------- 122 | if(NOT MSVC) 123 | # Avoid default flag values 124 | #-------------------------------------- 125 | set(CMAKE_C_FLAGS "") 126 | set(CMAKE_C_FLAGS_DEBUG "" ) 127 | set(CMAKE_C_FLAGS_RELEASE "") 128 | 129 | set(CMAKE_CXX_FLAGS "") 130 | set(CMAKE_CXX_FLAGS_DEBUG "") 131 | set(CMAKE_CXX_FLAGS_RELEASE "") 132 | 133 | set(CMAKE_OBJC_FLAGS "") 134 | set(CMAKE_OBJC_FLAGS_DEBUG "") 135 | set(CMAKE_OBJC_FLAGS_RELEASE "") 136 | 137 | set(CMAKE_OBJCXX_FLAGS "") 138 | set(CMAKE_OBJCXX_FLAGS_DEBUG "") 139 | set(CMAKE_OBJCXX_FLAGS_RELEASE "") 140 | 141 | # Set our flags 142 | #-------------------------------------- 143 | add_compile_options("$<$:-g>") 144 | add_compile_options("$,-O0,-O2>") 145 | add_compile_options(-Wall -Wextra) 146 | add_compile_options(-Wno-switch -Wno-unused-function -Wno-unused-parameter -Wno-implicit-fallthrough) 147 | 148 | if(NOT APPLE) 149 | add_compile_options(-Wno-cast-function-type) 150 | endif() 151 | else() 152 | # Security check 153 | add_compile_options(/GS) 154 | # Function level linking 155 | add_compile_options(/Gy) 156 | # Exceptions 157 | add_compile_options(/EHsc) 158 | if(MSVC_VERSION GREATER_EQUAL 1900) 159 | # SDL checks 2015+ 160 | add_compile_options(/sdl) 161 | endif() 162 | if(MSVC_VERSION LESS_EQUAL 1920) 163 | # Enable Minimal Rebuild (required for Edit and Continue) (deprecated) 164 | add_compile_options(/Gm) 165 | endif() 166 | add_compile_options(/fp:fast) 167 | # Program database for edit and continue 168 | add_compile_options("$,/ZI,/Zi>") 169 | # Optimizations 170 | add_compile_options("$,/Od,/O2>") 171 | # Inline function expansion 172 | add_compile_options("$,/Ob0,/Ob2>") 173 | # Basic runtime checks 174 | add_compile_options("$<$:/RTC1>") 175 | # Force Visual Studio to actualize __cplusplus version macro 176 | add_compile_options(/Zc:__cplusplus) 177 | 178 | endif() 179 | 180 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 181 | add_definitions(-D_DEBUG) 182 | add_definitions(-DDEBUG) 183 | if(EMSCRIPTEN) 184 | add_link_options(-g) 185 | endif() 186 | endif() 187 | 188 | # Set compiler/platform specific flags and dependencies 189 | #-------------------------------------- 190 | if(WIN32) 191 | 192 | if(MSVC) 193 | add_definitions(-D_CRT_SECURE_NO_WARNINGS) 194 | endif() 195 | add_definitions(-D_WIN32_WINNT=0x0601) # Windows 7 (we are in 2020) 196 | 197 | if(USE_OPENGL_API) 198 | list(APPEND SrcLib ${SrcGL}) 199 | 200 | add_definitions(-DUSE_OPENGL_API) 201 | endif() 202 | 203 | list(APPEND SrcLib ${SrcWindows}) 204 | 205 | elseif(IOS) 206 | 207 | list(APPEND SrcLib ${SrcIOS}) 208 | 209 | elseif(APPLE) 210 | 211 | if(USE_METAL_API) 212 | add_definitions(-DUSE_METAL_API) 213 | endif() 214 | 215 | if(USE_INVERTED_Y_ON_MACOS) 216 | add_definitions(-DUSE_INVERTED_Y_ON_MACOS) 217 | endif() 218 | 219 | list(APPEND SrcLib ${SrcMacOSX}) 220 | 221 | elseif(UNIX) 222 | 223 | if(USE_WAYLAND_API) 224 | list(APPEND SrcLib ${SrcWayland}) 225 | elseif(EMSCRIPTEN) 226 | list(APPEND SrcLib ${SrcWeb}) 227 | else() 228 | if(USE_OPENGL_API) 229 | list(APPEND SrcLib ${SrcGL}) 230 | 231 | add_definitions(-DUSE_OPENGL_API) 232 | endif() 233 | list(APPEND SrcLib ${SrcX11}) 234 | endif() 235 | 236 | endif() 237 | 238 | # Library 239 | #-------------------------------------- 240 | add_library(minifb STATIC 241 | ${SrcLib} 242 | ) 243 | add_library(minifb::minifb ALIAS minifb) 244 | 245 | # Link 246 | #-------------------------------------- 247 | if(APPLE) 248 | 249 | if(IOS) 250 | target_link_libraries(minifb PRIVATE 251 | "-framework UIKit" 252 | "-framework QuartzCore" 253 | "-framework Metal" 254 | "-framework MetalKit" 255 | ) 256 | else() 257 | target_link_libraries(minifb PRIVATE 258 | "-framework Cocoa" 259 | "-framework QuartzCore" 260 | "-framework Metal" 261 | "-framework MetalKit" 262 | ) 263 | endif() 264 | 265 | elseif(UNIX) 266 | 267 | if(USE_WAYLAND_API) 268 | target_link_libraries(minifb PRIVATE 269 | "-lwayland-client" 270 | "-lwayland-cursor" 271 | ) 272 | elseif(EMSCRIPTEN) 273 | add_link_options( 274 | "-sSTRICT=1" 275 | "-sENVIRONMENT=web" 276 | "-sLLD_REPORT_UNDEFINED" 277 | "-sMODULARIZE=1" 278 | "-sALLOW_MEMORY_GROWTH=1" 279 | "-sALLOW_TABLE_GROWTH" 280 | "-sMALLOC=emmalloc" 281 | "-sEXPORT_ALL=1" 282 | "-sEXPORTED_FUNCTIONS=[\"_malloc\",\"_free\",\"_main\"]" 283 | "-sEXPORTED_RUNTIME_METHODS=ccall,cwrap" 284 | "-sASYNCIFY" 285 | "--no-entry" 286 | "-sSINGLE_FILE" 287 | ) 288 | else() 289 | target_link_libraries(minifb PRIVATE 290 | "-lX11" 291 | #"-lxkbcommon" 292 | #"-lXrandr" DPI NOT WORKING 293 | ) 294 | if(USE_OPENGL_API) 295 | target_link_libraries(minifb PRIVATE 296 | "-lGL" 297 | ) 298 | endif() 299 | endif() 300 | 301 | elseif(WIN32) 302 | 303 | if(USE_OPENGL_API) 304 | target_link_libraries(minifb PRIVATE 305 | "Opengl32.lib" 306 | ) 307 | endif() 308 | 309 | target_link_libraries(minifb PRIVATE 310 | "winmm.lib" 311 | ) 312 | 313 | endif() 314 | 315 | # For all projects 316 | #-------------------------------------- 317 | target_include_directories(minifb PUBLIC 318 | $ 319 | $) 320 | target_include_directories(minifb PRIVATE src) 321 | 322 | link_libraries(minifb) 323 | 324 | # Examples 325 | #-------------------------------------- 326 | if(MINIFB_BUILD_EXAMPLES) 327 | if(NOT IOS) 328 | 329 | add_executable(noise 330 | tests/noise.c 331 | ) 332 | 333 | add_executable(input_events 334 | tests/input_events.c 335 | ) 336 | 337 | add_executable(input_events_cpp 338 | tests/input_events_cpp.cpp 339 | ) 340 | 341 | add_executable(multiple_windows 342 | tests/multiple_windows.c 343 | ) 344 | 345 | add_executable(hidpi 346 | tests/hidpi.c 347 | ) 348 | 349 | add_executable(fullscreen 350 | tests/fullscreen.c 351 | ) 352 | 353 | add_executable(timer 354 | tests/timer.c 355 | ) 356 | 357 | if(EMSCRIPTEN) 358 | add_custom_target(web_assets 359 | COMMAND ${CMAKE_COMMAND} -E copy_directory 360 | ${CMAKE_CURRENT_SOURCE_DIR}/tests/web 361 | ${CMAKE_CURRENT_BINARY_DIR} 362 | ) 363 | add_dependencies(noise web_assets) 364 | target_link_options(noise PRIVATE "-sEXPORT_NAME=noise") 365 | add_dependencies(input_events web_assets) 366 | target_link_options(input_events PRIVATE "-sEXPORT_NAME=input_events") 367 | add_dependencies(hidpi web_assets) 368 | target_link_options(hidpi PRIVATE "-sEXPORT_NAME=hidpi") 369 | add_dependencies(multiple_windows web_assets) 370 | target_link_options(multiple_windows PRIVATE "-sEXPORT_NAME=multiple_windows") 371 | add_dependencies(timer web_assets) 372 | target_link_options(timer PRIVATE "-sEXPORT_NAME=timer") 373 | endif() 374 | 375 | else() 376 | 377 | add_executable(noise 378 | tests/ios/main.m 379 | tests/ios/AppDelegate.h 380 | tests/ios/AppDelegate.m 381 | ) 382 | 383 | set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0" CACHE STRING "Set CMake deployment target" ${FORCE_CACHE}) 384 | 385 | target_include_directories(noise PRIVATE src) 386 | target_include_directories(noise PRIVATE src/ios) 387 | 388 | add_definitions(-DTVOS_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}) 389 | 390 | endif() 391 | 392 | endif() 393 | 394 | # Organize IDE Folders 395 | #-------------------------------------- 396 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 397 | set_property(TARGET minifb PROPERTY FOLDER "Libs") 398 | 399 | if(MINIFB_BUILD_EXAMPLES) 400 | set_property(TARGET noise PROPERTY FOLDER "Tests") 401 | set_property(TARGET input_events PROPERTY FOLDER "Tests") 402 | set_property(TARGET input_events_cpp PROPERTY FOLDER "Tests") 403 | set_property(TARGET multiple_windows PROPERTY FOLDER "Tests") 404 | set_property(TARGET hidpi PROPERTY FOLDER "Tests") 405 | endif() 406 | 407 | install(TARGETS minifb EXPORT minifb) 408 | file(GLOB_RECURSE HEADERS "${CMAKE_CURRENT_LIST_DIR}/include/*.h") 409 | install(FILES ${HEADERS} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") 410 | install(EXPORT minifb FILE minifb-config.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/minifb" NAMESPACE minifb::) 411 | 412 | message(STATUS "Done " ${PROJECT_NAME}) 413 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Daniel Collin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /include/MiniFB.h: -------------------------------------------------------------------------------- 1 | #ifndef _MINIFB_H_ 2 | #define _MINIFB_H_ 3 | 4 | #include "MiniFB_enums.h" 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 11 | #ifndef __ANDROID__ 12 | #define MFB_RGB(r, g, b) (((uint32_t) r) << 16) | (((uint32_t) g) << 8) | ((uint32_t) b) 13 | #define MFB_ARGB(a, r, g, b) (((uint32_t) a) << 24) | (((uint32_t) r) << 16) | (((uint32_t) g) << 8) | ((uint32_t) b) 14 | #else 15 | #ifdef HOST_WORDS_BIGENDIAN 16 | #define MFB_RGB(r, g, b) (((uint32_t) r) << 16) | (((uint32_t) g) << 8) | ((uint32_t) b) 17 | #define MFB_ARGB(a, r, g, b) (((uint32_t) a) << 24) | (((uint32_t) r) << 16) | (((uint32_t) g) << 8) | ((uint32_t) b) 18 | #else 19 | #define MFB_ARGB(r, g, b) (((uint32_t) a) << 24) | (((uint32_t) b) << 16) | (((uint32_t) g) << 8) | ((uint32_t) r) 20 | #define MFB_RGB(r, g, b) (((uint32_t) b) << 16) | (((uint32_t) g) << 8) | ((uint32_t) r) 21 | #endif 22 | #endif 23 | 24 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 25 | 26 | // Create a window that is used to display the buffer sent into the mfb_update function, returns 0 if fails 27 | struct mfb_window * mfb_open(const char *title, unsigned width, unsigned height); 28 | struct mfb_window * mfb_open_ex(const char *title, unsigned width, unsigned height, unsigned flags); 29 | 30 | // Update the display 31 | // Input buffer is assumed to be a 32-bit buffer of the size given in the open call 32 | // Will return a negative status if something went wrong or the user want to exit 33 | // Also updates the window events 34 | mfb_update_state mfb_update(struct mfb_window *window, void *buffer); 35 | 36 | mfb_update_state mfb_update_ex(struct mfb_window *window, void *buffer, unsigned width, unsigned height); 37 | 38 | // Only updates the window events 39 | mfb_update_state mfb_update_events(struct mfb_window *window); 40 | 41 | // Close the window 42 | void mfb_close(struct mfb_window *window); 43 | 44 | // Set user data 45 | void mfb_set_user_data(struct mfb_window *window, void *user_data); 46 | void * mfb_get_user_data(struct mfb_window *window); 47 | 48 | // Set viewport (useful when resize) 49 | bool mfb_set_viewport(struct mfb_window *window, unsigned offset_x, unsigned offset_y, unsigned width, unsigned height); 50 | // Let mfb to calculate the best fit from your framebuffer original size 51 | bool mfb_set_viewport_best_fit(struct mfb_window *window, unsigned old_width, unsigned old_height); 52 | 53 | // DPI 54 | // [Deprecated]: Probably a better name will be mfb_get_monitor_scale 55 | void mfb_get_monitor_dpi(struct mfb_window *window, float *dpi_x, float *dpi_y); 56 | // Use this instead 57 | void mfb_get_monitor_scale(struct mfb_window *window, float *scale_x, float *scale_y); 58 | 59 | // Callbacks 60 | void mfb_set_active_callback(struct mfb_window *window, mfb_active_func callback); 61 | void mfb_set_resize_callback(struct mfb_window *window, mfb_resize_func callback); 62 | void mfb_set_close_callback(struct mfb_window* window, mfb_close_func callback); 63 | void mfb_set_keyboard_callback(struct mfb_window *window, mfb_keyboard_func callback); 64 | void mfb_set_char_input_callback(struct mfb_window *window, mfb_char_input_func callback); 65 | void mfb_set_mouse_button_callback(struct mfb_window *window, mfb_mouse_button_func callback); 66 | void mfb_set_mouse_move_callback(struct mfb_window *window, mfb_mouse_move_func callback); 67 | void mfb_set_mouse_scroll_callback(struct mfb_window *window, mfb_mouse_scroll_func callback); 68 | 69 | // Getters 70 | const char * mfb_get_key_name(mfb_key key); 71 | 72 | bool mfb_is_window_active(struct mfb_window *window); 73 | unsigned mfb_get_window_width(struct mfb_window *window); 74 | unsigned mfb_get_window_height(struct mfb_window *window); 75 | int mfb_get_mouse_x(struct mfb_window *window); // Last mouse pos X 76 | int mfb_get_mouse_y(struct mfb_window *window); // Last mouse pos Y 77 | float mfb_get_mouse_scroll_x(struct mfb_window *window); // Mouse wheel X as a sum. When you call this function it resets. 78 | float mfb_get_mouse_scroll_y(struct mfb_window *window); // Mouse wheel Y as a sum. When you call this function it resets. 79 | const uint8_t * mfb_get_mouse_button_buffer(struct mfb_window *window); // One byte for every button. Press (1), Release 0. (up to 8 buttons) 80 | const uint8_t * mfb_get_key_buffer(struct mfb_window *window); // One byte for every key. Press (1), Release 0. 81 | 82 | // FPS 83 | void mfb_set_target_fps(uint32_t fps); 84 | unsigned mfb_get_target_fps(void); 85 | bool mfb_wait_sync(struct mfb_window *window); 86 | 87 | // Timer 88 | struct mfb_timer * mfb_timer_create(void); 89 | void mfb_timer_destroy(struct mfb_timer *tmr); 90 | void mfb_timer_reset(struct mfb_timer *tmr); 91 | double mfb_timer_now(struct mfb_timer *tmr); 92 | double mfb_timer_delta(struct mfb_timer *tmr); 93 | double mfb_timer_get_frequency(void); 94 | double mfb_timer_get_resolution(void); 95 | 96 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 97 | 98 | #ifdef __cplusplus 99 | } 100 | 101 | #if !defined(MINIFB_AVOID_CPP_HEADERS) 102 | #include "MiniFB_cpp.h" 103 | #endif 104 | 105 | #endif 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /include/MiniFB_cpp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(__cplusplus) 4 | 5 | #include 6 | #include "MiniFB.h" 7 | 8 | //------------------------------------- 9 | // To be able to distinguish these C++ functions, using std::function, from C functions, using raw function pointers, we need to reverse params order. 10 | // 11 | // Note that FROM the compiler point of view 12 | // mfb_set_XXX_callback(window, &my_c_func) 13 | // and 14 | // mfb_set_XXX_callback(window, [](...) {}) 15 | // have the same parameters. 16 | //------------------------------------- 17 | void mfb_set_active_callback (std::function func, struct mfb_window *window); 18 | void mfb_set_resize_callback (std::function func, struct mfb_window *window); 19 | void mfb_set_close_callback (std::function func, struct mfb_window *window); 20 | void mfb_set_keyboard_callback (std::function func, struct mfb_window *window); 21 | void mfb_set_char_input_callback (std::function func, struct mfb_window *window); 22 | void mfb_set_mouse_button_callback(std::function func, struct mfb_window *window); 23 | void mfb_set_mouse_move_callback (std::function func, struct mfb_window *window); 24 | void mfb_set_mouse_scroll_callback(std::function func, struct mfb_window *window); 25 | //------------------------------------- 26 | 27 | //------------------------------------- 28 | template 29 | void mfb_set_active_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, bool)); 30 | 31 | template 32 | void mfb_set_resize_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, int, int)); 33 | 34 | template 35 | void mfb_set_keyboard_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, mfb_key, mfb_key_mod, bool)); 36 | 37 | template 38 | void mfb_set_char_input_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, unsigned int)); 39 | 40 | template 41 | void mfb_set_mouse_button_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, mfb_mouse_button, mfb_key_mod, bool)); 42 | 43 | template 44 | void mfb_set_mouse_move_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, int, int)); 45 | 46 | template 47 | void mfb_set_mouse_scroll_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, mfb_key_mod, float, float)); 48 | //------------------------------------- 49 | 50 | //------------------------------------- 51 | // To avoid clumsy hands 52 | //------------------------------------- 53 | class mfb_stub { 54 | mfb_stub() : m_window(0x0) {} 55 | 56 | friend void mfb_set_active_callback (std::function func, struct mfb_window *window); 57 | friend void mfb_set_resize_callback (std::function func, struct mfb_window *window); 58 | friend void mfb_set_close_callback (std::function func, struct mfb_window *window); 59 | friend void mfb_set_keyboard_callback (std::function func, struct mfb_window *window); 60 | friend void mfb_set_char_input_callback (std::function func, struct mfb_window *window); 61 | friend void mfb_set_mouse_button_callback(std::function func, struct mfb_window *window); 62 | friend void mfb_set_mouse_move_callback (std::function func, struct mfb_window *window); 63 | friend void mfb_set_mouse_scroll_callback(std::function func, struct mfb_window *window); 64 | 65 | template 66 | friend void mfb_set_active_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, bool)); 67 | template 68 | friend void mfb_set_resize_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, int, int)); 69 | template 70 | friend void mfb_set_close_callback(struct mfb_window *window, T *obj, bool (T::*method)(struct mfb_window *)); 71 | template 72 | friend void mfb_set_mouse_button_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, mfb_mouse_button, mfb_key_mod, bool)); 73 | template 74 | friend void mfb_set_keyboard_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, mfb_key, mfb_key_mod, bool)); 75 | template 76 | friend void mfb_set_char_input_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, unsigned int)); 77 | template 78 | friend void mfb_set_mouse_button_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, mfb_mouse_button, mfb_key_mod, bool)); 79 | template 80 | friend void mfb_set_mouse_move_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, int, int)); 81 | template 82 | friend void mfb_set_mouse_scroll_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, mfb_key_mod, float, float)); 83 | 84 | static mfb_stub *GetInstance(struct mfb_window *window); 85 | 86 | static void active_stub(struct mfb_window *window, bool isActive); 87 | static void resize_stub(struct mfb_window *window, int width, int height); 88 | static bool close_stub(struct mfb_window *window); 89 | static void keyboard_stub(struct mfb_window *window, mfb_key key, mfb_key_mod mod, bool isPressed); 90 | static void char_input_stub(struct mfb_window *window, unsigned int); 91 | static void mouse_btn_stub(struct mfb_window *window, mfb_mouse_button button, mfb_key_mod mod, bool isPressed); 92 | static void mouse_move_stub(struct mfb_window *window, int x, int y); 93 | static void scroll_stub(struct mfb_window *window, mfb_key_mod mod, float deltaX, float deltaY); 94 | 95 | struct mfb_window *m_window; 96 | std::function m_active; 97 | std::function m_resize; 98 | std::function m_close; 99 | std::function m_keyboard; 100 | std::function m_char_input; 101 | std::function m_mouse_btn; 102 | std::function m_mouse_move; 103 | std::function m_scroll; 104 | }; 105 | 106 | //------------------------------------- 107 | template 108 | inline void mfb_set_active_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *window, bool)) { 109 | using namespace std::placeholders; 110 | 111 | mfb_stub *stub = mfb_stub::GetInstance(window); 112 | stub->m_active = std::bind(method, obj, _1, _2); 113 | mfb_set_active_callback(window, mfb_stub::active_stub); 114 | } 115 | 116 | //------------------------------------- 117 | template 118 | inline void mfb_set_resize_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *window, int, int)) { 119 | using namespace std::placeholders; 120 | 121 | mfb_stub *stub = mfb_stub::GetInstance(window); 122 | stub->m_resize = std::bind(method, obj, _1, _2, _3); 123 | mfb_set_resize_callback(window, mfb_stub::resize_stub); 124 | } 125 | 126 | //------------------------------------- 127 | template 128 | inline void mfb_set_close_callback(struct mfb_window *window, T *obj, bool (T::*method)(struct mfb_window *window)) { 129 | using namespace std::placeholders; 130 | 131 | mfb_stub *stub = mfb_stub::GetInstance(window); 132 | stub->m_close = std::bind(method, obj, _1); 133 | mfb_set_close_callback(window, mfb_stub::close_stub); 134 | } 135 | 136 | //------------------------------------- 137 | template 138 | inline void mfb_set_keyboard_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *window, mfb_key, mfb_key_mod, bool)) { 139 | using namespace std::placeholders; 140 | 141 | mfb_stub *stub = mfb_stub::GetInstance(window); 142 | stub->m_keyboard = std::bind(method, obj, _1, _2, _3, _4); 143 | mfb_set_keyboard_callback(window, mfb_stub::keyboard_stub); 144 | } 145 | 146 | //------------------------------------- 147 | template 148 | inline void mfb_set_char_input_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *window, unsigned int)) { 149 | using namespace std::placeholders; 150 | 151 | mfb_stub *stub = mfb_stub::GetInstance(window); 152 | stub->m_char_input = std::bind(method, obj, _1, _2); 153 | mfb_set_char_input_callback(window, mfb_stub::char_input_stub); 154 | } 155 | 156 | //------------------------------------- 157 | template 158 | inline void mfb_set_mouse_button_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *window, mfb_mouse_button, mfb_key_mod, bool)) { 159 | using namespace std::placeholders; 160 | 161 | mfb_stub *stub = mfb_stub::GetInstance(window); 162 | stub->m_mouse_btn = std::bind(method, obj, _1, _2, _3, _4); 163 | mfb_set_mouse_button_callback(window, mfb_stub::mouse_btn_stub); 164 | } 165 | 166 | //------------------------------------- 167 | template 168 | inline void mfb_set_mouse_move_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *window, int, int)) { 169 | using namespace std::placeholders; 170 | 171 | mfb_stub *stub = mfb_stub::GetInstance(window); 172 | stub->m_mouse_move = std::bind(method, obj, _1, _2, _3); 173 | mfb_set_mouse_move_callback(window, mfb_stub::mouse_move_stub); 174 | } 175 | 176 | //------------------------------------- 177 | template 178 | inline void mfb_set_mouse_scroll_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *window, mfb_key_mod, float, float)) { 179 | using namespace std::placeholders; 180 | 181 | mfb_stub *stub = mfb_stub::GetInstance(window); 182 | stub->m_scroll = std::bind(method, obj, _1, _2, _3, _4); 183 | mfb_set_mouse_scroll_callback(window, mfb_stub::scroll_stub); 184 | } 185 | 186 | #endif 187 | -------------------------------------------------------------------------------- /include/MiniFB_enums.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // Enums 7 | typedef enum { 8 | STATE_OK = 0, 9 | STATE_EXIT = -1, 10 | STATE_INVALID_WINDOW = -2, 11 | STATE_INVALID_BUFFER = -3, 12 | STATE_INTERNAL_ERROR = -4, 13 | } mfb_update_state; 14 | 15 | typedef enum { 16 | MOUSE_BTN_0, // No mouse button 17 | MOUSE_BTN_1, 18 | MOUSE_BTN_2, 19 | MOUSE_BTN_3, 20 | MOUSE_BTN_4, 21 | MOUSE_BTN_5, 22 | MOUSE_BTN_6, 23 | MOUSE_BTN_7, 24 | } mfb_mouse_button; 25 | #define MOUSE_LEFT MOUSE_BTN_1 26 | #define MOUSE_RIGHT MOUSE_BTN_2 27 | #define MOUSE_MIDDLE MOUSE_BTN_3 28 | 29 | typedef enum { 30 | KB_KEY_UNKNOWN = -1, 31 | 32 | KB_KEY_SPACE = 32, 33 | KB_KEY_APOSTROPHE = 39, 34 | KB_KEY_COMMA = 44, 35 | KB_KEY_MINUS = 45, 36 | KB_KEY_PERIOD = 46, 37 | KB_KEY_SLASH = 47, 38 | KB_KEY_0 = 48, 39 | KB_KEY_1 = 49, 40 | KB_KEY_2 = 50, 41 | KB_KEY_3 = 51, 42 | KB_KEY_4 = 52, 43 | KB_KEY_5 = 53, 44 | KB_KEY_6 = 54, 45 | KB_KEY_7 = 55, 46 | KB_KEY_8 = 56, 47 | KB_KEY_9 = 57, 48 | KB_KEY_SEMICOLON = 59, 49 | KB_KEY_EQUAL = 61, 50 | KB_KEY_A = 65, 51 | KB_KEY_B = 66, 52 | KB_KEY_C = 67, 53 | KB_KEY_D = 68, 54 | KB_KEY_E = 69, 55 | KB_KEY_F = 70, 56 | KB_KEY_G = 71, 57 | KB_KEY_H = 72, 58 | KB_KEY_I = 73, 59 | KB_KEY_J = 74, 60 | KB_KEY_K = 75, 61 | KB_KEY_L = 76, 62 | KB_KEY_M = 77, 63 | KB_KEY_N = 78, 64 | KB_KEY_O = 79, 65 | KB_KEY_P = 80, 66 | KB_KEY_Q = 81, 67 | KB_KEY_R = 82, 68 | KB_KEY_S = 83, 69 | KB_KEY_T = 84, 70 | KB_KEY_U = 85, 71 | KB_KEY_V = 86, 72 | KB_KEY_W = 87, 73 | KB_KEY_X = 88, 74 | KB_KEY_Y = 89, 75 | KB_KEY_Z = 90, 76 | KB_KEY_LEFT_BRACKET = 91, 77 | KB_KEY_BACKSLASH = 92, 78 | KB_KEY_RIGHT_BRACKET = 93, 79 | KB_KEY_GRAVE_ACCENT = 96, 80 | KB_KEY_WORLD_1 = 161, 81 | KB_KEY_WORLD_2 = 162, 82 | 83 | KB_KEY_ESCAPE = 256, 84 | KB_KEY_ENTER = 257, 85 | KB_KEY_TAB = 258, 86 | KB_KEY_BACKSPACE = 259, 87 | KB_KEY_INSERT = 260, 88 | KB_KEY_DELETE = 261, 89 | KB_KEY_RIGHT = 262, 90 | KB_KEY_LEFT = 263, 91 | KB_KEY_DOWN = 264, 92 | KB_KEY_UP = 265, 93 | KB_KEY_PAGE_UP = 266, 94 | KB_KEY_PAGE_DOWN = 267, 95 | KB_KEY_HOME = 268, 96 | KB_KEY_END = 269, 97 | KB_KEY_CAPS_LOCK = 280, 98 | KB_KEY_SCROLL_LOCK = 281, 99 | KB_KEY_NUM_LOCK = 282, 100 | KB_KEY_PRINT_SCREEN = 283, 101 | KB_KEY_PAUSE = 284, 102 | KB_KEY_F1 = 290, 103 | KB_KEY_F2 = 291, 104 | KB_KEY_F3 = 292, 105 | KB_KEY_F4 = 293, 106 | KB_KEY_F5 = 294, 107 | KB_KEY_F6 = 295, 108 | KB_KEY_F7 = 296, 109 | KB_KEY_F8 = 297, 110 | KB_KEY_F9 = 298, 111 | KB_KEY_F10 = 299, 112 | KB_KEY_F11 = 300, 113 | KB_KEY_F12 = 301, 114 | KB_KEY_F13 = 302, 115 | KB_KEY_F14 = 303, 116 | KB_KEY_F15 = 304, 117 | KB_KEY_F16 = 305, 118 | KB_KEY_F17 = 306, 119 | KB_KEY_F18 = 307, 120 | KB_KEY_F19 = 308, 121 | KB_KEY_F20 = 309, 122 | KB_KEY_F21 = 310, 123 | KB_KEY_F22 = 311, 124 | KB_KEY_F23 = 312, 125 | KB_KEY_F24 = 313, 126 | KB_KEY_F25 = 314, 127 | KB_KEY_KP_0 = 320, 128 | KB_KEY_KP_1 = 321, 129 | KB_KEY_KP_2 = 322, 130 | KB_KEY_KP_3 = 323, 131 | KB_KEY_KP_4 = 324, 132 | KB_KEY_KP_5 = 325, 133 | KB_KEY_KP_6 = 326, 134 | KB_KEY_KP_7 = 327, 135 | KB_KEY_KP_8 = 328, 136 | KB_KEY_KP_9 = 329, 137 | KB_KEY_KP_DECIMAL = 330, 138 | KB_KEY_KP_DIVIDE = 331, 139 | KB_KEY_KP_MULTIPLY = 332, 140 | KB_KEY_KP_SUBTRACT = 333, 141 | KB_KEY_KP_ADD = 334, 142 | KB_KEY_KP_ENTER = 335, 143 | KB_KEY_KP_EQUAL = 336, 144 | KB_KEY_LEFT_SHIFT = 340, 145 | KB_KEY_LEFT_CONTROL = 341, 146 | KB_KEY_LEFT_ALT = 342, 147 | KB_KEY_LEFT_SUPER = 343, 148 | KB_KEY_RIGHT_SHIFT = 344, 149 | KB_KEY_RIGHT_CONTROL = 345, 150 | KB_KEY_RIGHT_ALT = 346, 151 | KB_KEY_RIGHT_SUPER = 347, 152 | KB_KEY_MENU = 348 153 | } mfb_key; 154 | #define KB_KEY_LAST KB_KEY_MENU 155 | 156 | typedef enum { 157 | KB_MOD_SHIFT = 0x0001, 158 | KB_MOD_CONTROL = 0x0002, 159 | KB_MOD_ALT = 0x0004, 160 | KB_MOD_SUPER = 0x0008, 161 | KB_MOD_CAPS_LOCK = 0x0010, 162 | KB_MOD_NUM_LOCK = 0x0020 163 | } mfb_key_mod; 164 | 165 | typedef enum { 166 | WF_RESIZABLE = 0x01, 167 | WF_FULLSCREEN = 0x02, 168 | WF_FULLSCREEN_DESKTOP = 0x04, 169 | WF_BORDERLESS = 0x08, 170 | WF_ALWAYS_ON_TOP = 0x10, 171 | } mfb_window_flags; 172 | 173 | // Opaque pointer 174 | struct mfb_window; 175 | struct mfb_timer; 176 | 177 | // Event callbacks 178 | typedef void(*mfb_active_func)(struct mfb_window *window, bool isActive); 179 | typedef void(*mfb_resize_func)(struct mfb_window *window, int width, int height); 180 | typedef bool(*mfb_close_func)(struct mfb_window* window); 181 | typedef void(*mfb_keyboard_func)(struct mfb_window *window, mfb_key key, mfb_key_mod mod, bool isPressed); 182 | typedef void(*mfb_char_input_func)(struct mfb_window *window, unsigned int code); 183 | typedef void(*mfb_mouse_button_func)(struct mfb_window *window, mfb_mouse_button button, mfb_key_mod mod, bool isPressed); 184 | typedef void(*mfb_mouse_move_func)(struct mfb_window *window, int x, int y); 185 | typedef void(*mfb_mouse_scroll_func)(struct mfb_window *window, mfb_key_mod mod, float deltaX, float deltaY); 186 | 187 | -------------------------------------------------------------------------------- /include/MiniFB_ios.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "MiniFB_enums.h" 4 | 5 | void user_implemented_init(struct mfb_window *window); 6 | 7 | void user_implemented_update(struct mfb_window *window); 8 | -------------------------------------------------------------------------------- /src/MiniFB_cpp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | //------------------------------------- 6 | mfb_stub * 7 | mfb_stub::GetInstance(struct mfb_window *window) { 8 | struct stub_vector { 9 | std::vector instances; 10 | 11 | stub_vector() = default; 12 | 13 | ~stub_vector() { 14 | for(mfb_stub *instance : instances) 15 | delete instance; 16 | } 17 | 18 | mfb_stub *Get(struct mfb_window *window) { 19 | for(mfb_stub *instance : instances) { 20 | if(instance->m_window == window) { 21 | return instance; 22 | } 23 | } 24 | 25 | instances.push_back(new mfb_stub); 26 | instances.back()->m_window = window; 27 | 28 | return instances.back(); 29 | } 30 | }; 31 | 32 | static stub_vector s_instances; 33 | 34 | return s_instances.Get(window); 35 | } 36 | 37 | //------------------------------------- 38 | void 39 | mfb_stub::active_stub(struct mfb_window *window, bool isActive) { 40 | mfb_stub *stub = mfb_stub::GetInstance(window); 41 | stub->m_active(window, isActive); 42 | } 43 | 44 | //------------------------------------- 45 | void 46 | mfb_stub::resize_stub(struct mfb_window *window, int width, int height) { 47 | mfb_stub *stub = mfb_stub::GetInstance(window); 48 | stub->m_resize(window, width, height); 49 | } 50 | 51 | //------------------------------------- 52 | bool 53 | mfb_stub::close_stub(struct mfb_window *window) { 54 | mfb_stub *stub = mfb_stub::GetInstance(window); 55 | return stub->m_close(window); 56 | } 57 | 58 | //------------------------------------- 59 | void 60 | mfb_stub::keyboard_stub(struct mfb_window *window, mfb_key key, mfb_key_mod mod, bool isPressed) { 61 | mfb_stub *stub = mfb_stub::GetInstance(window); 62 | stub->m_keyboard(window, key, mod, isPressed); 63 | } 64 | 65 | //------------------------------------- 66 | void 67 | mfb_stub::char_input_stub(struct mfb_window *window, unsigned int code) { 68 | mfb_stub *stub = mfb_stub::GetInstance(window); 69 | stub->m_char_input(window, code); 70 | } 71 | 72 | //------------------------------------- 73 | void 74 | mfb_stub::mouse_btn_stub(struct mfb_window *window, mfb_mouse_button button, mfb_key_mod mod, bool isPressed) { 75 | mfb_stub *stub = mfb_stub::GetInstance(window); 76 | stub->m_mouse_btn(window, button, mod, isPressed); 77 | } 78 | 79 | //------------------------------------- 80 | void 81 | mfb_stub::mouse_move_stub(struct mfb_window *window, int x, int y) { 82 | mfb_stub *stub = mfb_stub::GetInstance(window); 83 | stub->m_mouse_move(window, x, y); 84 | } 85 | 86 | //------------------------------------- 87 | void 88 | mfb_stub::scroll_stub(struct mfb_window *window, mfb_key_mod mod, float deltaX, float deltaY) { 89 | mfb_stub *stub = mfb_stub::GetInstance(window); 90 | stub->m_scroll(window, mod, deltaX, deltaY); 91 | } 92 | 93 | //------------------------------------- 94 | 95 | //------------------------------------- 96 | void 97 | mfb_set_active_callback(std::function func, struct mfb_window *window) { 98 | using namespace std::placeholders; 99 | 100 | mfb_stub *stub = mfb_stub::GetInstance(window); 101 | stub->m_active = std::bind(func, _1, _2); 102 | mfb_set_active_callback(window, mfb_stub::active_stub); 103 | } 104 | 105 | //------------------------------------- 106 | void 107 | mfb_set_resize_callback(std::function func, struct mfb_window *window) { 108 | using namespace std::placeholders; 109 | 110 | mfb_stub *stub = mfb_stub::GetInstance(window); 111 | stub->m_resize = std::bind(func, _1, _2, _3); 112 | mfb_set_resize_callback(window, mfb_stub::resize_stub); 113 | } 114 | 115 | //------------------------------------- 116 | void 117 | mfb_set_close_callback(std::function func, struct mfb_window *window) { 118 | using namespace std::placeholders; 119 | 120 | mfb_stub *stub = mfb_stub::GetInstance(window); 121 | stub->m_close = std::bind(func, _1); 122 | mfb_set_close_callback(window, mfb_stub::close_stub); 123 | } 124 | 125 | //------------------------------------- 126 | void 127 | mfb_set_keyboard_callback(std::function func, struct mfb_window *window) { 128 | using namespace std::placeholders; 129 | 130 | mfb_stub *stub = mfb_stub::GetInstance(window); 131 | stub->m_keyboard = std::bind(func, _1, _2, _3, _4); 132 | mfb_set_keyboard_callback(window, mfb_stub::keyboard_stub); 133 | } 134 | 135 | //------------------------------------- 136 | void 137 | mfb_set_char_input_callback(std::function func, struct mfb_window *window) { 138 | using namespace std::placeholders; 139 | 140 | mfb_stub *stub = mfb_stub::GetInstance(window); 141 | stub->m_char_input = std::bind(func, _1, _2); 142 | mfb_set_char_input_callback(window, mfb_stub::char_input_stub); 143 | } 144 | 145 | //------------------------------------- 146 | void 147 | mfb_set_mouse_button_callback(std::function func, struct mfb_window *window) { 148 | using namespace std::placeholders; 149 | 150 | mfb_stub *stub = mfb_stub::GetInstance(window); 151 | stub->m_mouse_btn = std::bind(func, _1, _2, _3, _4); 152 | mfb_set_mouse_button_callback(window, mfb_stub::mouse_btn_stub); 153 | } 154 | 155 | //------------------------------------- 156 | void 157 | mfb_set_mouse_move_callback(std::function func, struct mfb_window *window) { 158 | using namespace std::placeholders; 159 | 160 | mfb_stub *stub = mfb_stub::GetInstance(window); 161 | stub->m_mouse_move = std::bind(func, _1, _2, _3); 162 | mfb_set_mouse_move_callback(window, mfb_stub::mouse_move_stub); 163 | } 164 | 165 | //------------------------------------- 166 | void 167 | mfb_set_mouse_scroll_callback(std::function func, struct mfb_window *window) { 168 | using namespace std::placeholders; 169 | 170 | mfb_stub *stub = mfb_stub::GetInstance(window); 171 | stub->m_scroll = std::bind(func, _1, _2, _3, _4); 172 | mfb_set_mouse_scroll_callback(window, mfb_stub::scroll_stub); 173 | } 174 | -------------------------------------------------------------------------------- /src/MiniFB_internal.c: -------------------------------------------------------------------------------- 1 | #include "MiniFB_internal.h" 2 | #include 3 | 4 | //#define kUseBilinearInterpolation 5 | 6 | #if defined(kUseBilinearInterpolation) 7 | //------------------------------------- 8 | static uint32_t 9 | 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) { 10 | uint32_t incX = x + 1 < srcWidth ? 1 : 0; 11 | uint32_t incY = y + 1 < srcHeight ? srcPitch : 0; 12 | uint8_t *p00 = (uint8_t *) &srcImage[(srcOffsetX >> 16)]; 13 | uint8_t *p01 = (uint8_t *) &srcImage[(srcOffsetX >> 16) + incX]; 14 | uint8_t *p10 = (uint8_t *) &srcImage[(srcOffsetX >> 16) + incY]; 15 | uint8_t *p11 = (uint8_t *) &srcImage[(srcOffsetX >> 16) + incY + incX]; 16 | 17 | uint32_t wx2 = srcOffsetX & 0xffff; 18 | uint32_t wy2 = srcOffsetY & 0xffff; 19 | uint32_t wx1 = 0x10000 - wx2; 20 | uint32_t wy1 = 0x10000 - wy2; 21 | 22 | uint32_t w1 = ((uint64_t) wx1 * wy1) >> 16; 23 | uint32_t w2 = ((uint64_t) wx2 * wy1) >> 16; 24 | uint32_t w3 = ((uint64_t) wx1 * wy2) >> 16; 25 | uint32_t w4 = ((uint64_t) wx2 * wy2) >> 16; 26 | 27 | // If you don't have uint64_t 28 | //uint32_t b = (((p00[0] * wx1 + p01[0] * wx2) >> 16) * wy1 + ((p10[0] * wx1 + p11[0] * wx2) >> 16) * wy2) >> 16; 29 | //uint32_t g = (((p00[1] * wx1 + p01[1] * wx2) >> 16) * wy1 + ((p10[1] * wx1 + p11[1] * wx2) >> 16) * wy2) >> 16; 30 | //uint32_t r = (((p00[2] * wx1 + p01[2] * wx2) >> 16) * wy1 + ((p10[2] * wx1 + p11[2] * wx2) >> 16) * wy2) >> 16; 31 | //uint32_t a = (((p00[3] * wx1 + p01[3] * wx2) >> 16) * wy1 + ((p10[3] * wx1 + p11[3] * wx2) >> 16) * wy2) >> 16; 32 | 33 | uint32_t b = ((p00[0] * w1 + p01[0] * w2) + (p10[0] * w3 + p11[0] * w4)) >> 16; 34 | uint32_t g = ((p00[1] * w1 + p01[1] * w2) + (p10[1] * w3 + p11[1] * w4)) >> 16; 35 | uint32_t r = ((p00[2] * w1 + p01[2] * w2) + (p10[2] * w3 + p11[2] * w4)) >> 16; 36 | uint32_t a = ((p00[3] * w1 + p01[3] * w2) + (p10[3] * w3 + p11[3] * w4)) >> 16; 37 | 38 | return (a << 24) + (r << 16) + (g << 8) + b; 39 | } 40 | #endif 41 | 42 | // Only for 32 bits images 43 | //------------------------------------- 44 | void 45 | stretch_image(uint32_t *srcImage, uint32_t srcX, uint32_t srcY, uint32_t srcWidth, uint32_t srcHeight, uint32_t srcPitch, 46 | uint32_t *dstImage, uint32_t dstX, uint32_t dstY, uint32_t dstWidth, uint32_t dstHeight, uint32_t dstPitch) { 47 | 48 | uint32_t x, y; 49 | uint32_t srcOffsetX, srcOffsetY; 50 | 51 | if(srcImage == 0x0 || dstImage == 0x0) 52 | return; 53 | 54 | srcImage += srcX + srcY * srcPitch; 55 | dstImage += dstX + dstY * dstPitch; 56 | 57 | const uint32_t deltaX = (srcWidth << 16) / dstWidth; 58 | const uint32_t deltaY = (srcHeight << 16) / dstHeight; 59 | 60 | srcOffsetY = 0; 61 | for(y=0; y> 16]; 68 | #endif 69 | srcOffsetX += deltaX; 70 | } 71 | 72 | srcOffsetY += deltaY; 73 | if(srcOffsetY >= 0x10000) { 74 | srcImage += (srcOffsetY >> 16) * srcPitch; 75 | srcOffsetY &= 0xffff; 76 | } 77 | dstImage += dstPitch; 78 | } 79 | } 80 | 81 | //------------------------------------- 82 | void 83 | calc_dst_factor(SWindowData *window_data, uint32_t width, uint32_t height) { 84 | if (window_data->dst_width == 0) { 85 | window_data->dst_width = width; 86 | } 87 | window_data->factor_x = (float) window_data->dst_offset_x / (float) width; 88 | window_data->factor_width = (float) window_data->dst_width / (float) width; 89 | 90 | if (window_data->dst_height == 0) { 91 | window_data->dst_height = height; 92 | } 93 | window_data->factor_y = (float) window_data->dst_offset_y / (float) height; 94 | window_data->factor_height = (float) window_data->dst_height / (float) height; 95 | } 96 | 97 | //------------------------------------- 98 | void 99 | resize_dst(SWindowData *window_data, uint32_t width, uint32_t height) { 100 | window_data->dst_offset_x = (uint32_t) (width * window_data->factor_x); 101 | window_data->dst_offset_y = (uint32_t) (height * window_data->factor_y); 102 | window_data->dst_width = (uint32_t) (width * window_data->factor_width); 103 | window_data->dst_height = (uint32_t) (height * window_data->factor_height); 104 | } 105 | 106 | #if !defined(USE_OPENGL_API) && !defined(USE_METAL_API) 107 | 108 | //------------------------------------- 109 | void 110 | set_target_fps_aux() { 111 | } 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /src/MiniFB_internal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "WindowData.h" 5 | 6 | #define kCall(func, ...) if(window_data && window_data->func) window_data->func((struct mfb_window *) window_data, __VA_ARGS__); 7 | #define kUnused(var) (void) var; 8 | 9 | typedef struct mfb_timer { 10 | int64_t start_time; 11 | int64_t delta_counter; 12 | uint64_t time; 13 | } mfb_timer; 14 | 15 | #if defined(__cplusplus) 16 | extern "C" { 17 | #endif 18 | extern short int g_keycodes[512]; 19 | void keyboard_default(struct mfb_window *window, mfb_key key, mfb_key_mod mod, bool isPressed); 20 | 21 | void calc_dst_factor(SWindowData *window_data, uint32_t width, uint32_t height); 22 | void resize_dst(SWindowData *window_data, uint32_t width, uint32_t height); 23 | void set_target_fps_aux(); 24 | 25 | #if defined(__cplusplus) 26 | } 27 | #endif 28 | -------------------------------------------------------------------------------- /src/MiniFB_linux.c: -------------------------------------------------------------------------------- 1 | #if defined(__linux__) 2 | 3 | #include 4 | #include 5 | 6 | extern double g_timer_frequency; 7 | extern double g_timer_resolution; 8 | 9 | #define kClock CLOCK_MONOTONIC 10 | //#define kClock CLOCK_REALTIME 11 | 12 | uint64_t 13 | mfb_timer_tick() { 14 | struct timespec time; 15 | 16 | if (clock_gettime(kClock, &time) != 0) { 17 | return 0.0; 18 | } 19 | 20 | return time.tv_sec * 1e+9 + time.tv_nsec; 21 | } 22 | 23 | void 24 | mfb_timer_init() { 25 | struct timespec res; 26 | 27 | if (clock_getres(kClock, &res) != 0) { 28 | g_timer_frequency = 1e+9; 29 | } 30 | else { 31 | g_timer_frequency = res.tv_sec + res.tv_nsec * 1e+9; 32 | } 33 | g_timer_resolution = 1.0 / g_timer_frequency; 34 | } 35 | 36 | #endif 37 | 38 | -------------------------------------------------------------------------------- /src/MiniFB_timer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "MiniFB_internal.h" 3 | #include 4 | 5 | //------------------------------------- 6 | double g_timer_frequency; 7 | double g_timer_resolution; 8 | double g_time_for_frame = 1.0 / 60.0; 9 | bool g_use_hardware_sync = false; 10 | 11 | //------------------------------------- 12 | extern uint64_t mfb_timer_tick(void); 13 | extern void mfb_timer_init(void); 14 | 15 | //------------------------------------- 16 | void 17 | mfb_set_target_fps(uint32_t fps) { 18 | if(fps == 0) { 19 | g_time_for_frame = 0; 20 | } 21 | else { 22 | g_time_for_frame = 1.0 / fps; 23 | } 24 | set_target_fps_aux(); 25 | } 26 | 27 | //------------------------------------- 28 | unsigned 29 | mfb_get_target_fps() { 30 | if (g_time_for_frame == 0) { 31 | return 0; 32 | } 33 | else { 34 | return (unsigned) (1.0 / g_time_for_frame); 35 | } 36 | } 37 | 38 | //------------------------------------- 39 | struct mfb_timer * 40 | mfb_timer_create() { 41 | static int once = 1; 42 | mfb_timer *tmr; 43 | 44 | if(once) { 45 | once = 0; 46 | mfb_timer_init(); 47 | } 48 | 49 | tmr = malloc(sizeof(mfb_timer)); 50 | mfb_timer_reset(tmr); 51 | 52 | return tmr; 53 | } 54 | 55 | //------------------------------------- 56 | void 57 | mfb_timer_destroy(struct mfb_timer *tmr) { 58 | if(tmr != 0x0) { 59 | free(tmr); 60 | } 61 | } 62 | 63 | //------------------------------------- 64 | void 65 | mfb_timer_reset(struct mfb_timer *tmr) { 66 | if(tmr == 0x0) 67 | return; 68 | 69 | tmr->start_time = mfb_timer_tick(); 70 | tmr->delta_counter = tmr->start_time; 71 | tmr->time = 0; 72 | } 73 | 74 | //------------------------------------- 75 | double 76 | mfb_timer_now(struct mfb_timer *tmr) { 77 | uint64_t counter; 78 | 79 | if(tmr == 0x0) 80 | return 0.0; 81 | 82 | counter = mfb_timer_tick(); 83 | tmr->time += (counter - tmr->start_time); 84 | tmr->start_time = counter; 85 | 86 | return tmr->time * g_timer_resolution; 87 | } 88 | 89 | //------------------------------------- 90 | double 91 | mfb_timer_delta(struct mfb_timer *tmr) { 92 | int64_t counter; 93 | uint64_t delta; 94 | 95 | if(tmr == 0x0) 96 | return 0.0; 97 | 98 | counter = mfb_timer_tick(); 99 | delta = (counter - tmr->delta_counter); 100 | tmr->delta_counter = counter; 101 | 102 | return delta * g_timer_resolution; 103 | } 104 | 105 | //------------------------------------- 106 | double 107 | mfb_timer_get_frequency() { 108 | return g_timer_frequency; 109 | } 110 | 111 | //------------------------------------- 112 | double 113 | mfb_timer_get_resolution() { 114 | return g_timer_resolution; 115 | } 116 | -------------------------------------------------------------------------------- /src/WindowData.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | //------------------------------------- 8 | typedef struct { 9 | void *specific; 10 | void *user_data; 11 | 12 | mfb_active_func active_func; 13 | mfb_resize_func resize_func; 14 | mfb_close_func close_func; 15 | mfb_keyboard_func keyboard_func; 16 | mfb_char_input_func char_input_func; 17 | mfb_mouse_button_func mouse_btn_func; 18 | mfb_mouse_move_func mouse_move_func; 19 | mfb_mouse_scroll_func mouse_wheel_func; 20 | 21 | uint32_t window_width; 22 | uint32_t window_height; 23 | 24 | uint32_t dst_offset_x; 25 | uint32_t dst_offset_y; 26 | uint32_t dst_width; 27 | uint32_t dst_height; 28 | float factor_x; 29 | float factor_y; 30 | float factor_width; 31 | float factor_height; 32 | 33 | void *draw_buffer; 34 | uint32_t buffer_width; 35 | uint32_t buffer_height; 36 | uint32_t buffer_stride; 37 | 38 | int32_t mouse_pos_x; 39 | int32_t mouse_pos_y; 40 | float mouse_wheel_x; 41 | float mouse_wheel_y; 42 | uint8_t mouse_button_status[8]; 43 | uint8_t key_status[512]; 44 | uint32_t mod_keys; 45 | 46 | bool is_active; 47 | bool is_initialized; 48 | 49 | bool close; 50 | } SWindowData; 51 | -------------------------------------------------------------------------------- /src/android/WindowData_Android.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | //------------------------------------- 6 | typedef struct { 7 | struct android_app *app; 8 | struct mfb_timer *timer; 9 | } SWindowData_Android; 10 | -------------------------------------------------------------------------------- /src/gl/MiniFB_GL.c: -------------------------------------------------------------------------------- 1 | #if defined(USE_OPENGL_API) 2 | 3 | #include "MiniFB_GL.h" 4 | #include "MiniFB_internal.h" 5 | #if defined(_WIN32) || defined(WIN32) 6 | #include 7 | #include 8 | #elif defined(linux) 9 | #include 10 | #include 11 | #include 12 | #endif 13 | #include 14 | #include 15 | #include 16 | 17 | //#define kUse_Clean_UP 18 | #if defined(kUse_Clean_UP) 19 | #define UseCleanUp(x) x 20 | #else 21 | #define UseCleanUp(x) 22 | #endif 23 | 24 | extern double g_time_for_frame; 25 | extern bool g_use_hardware_sync; 26 | 27 | //------------------------------------- 28 | static bool 29 | CheckGLExtension(const char *name) { 30 | static const char *extensions = 0x0; 31 | 32 | if (extensions == 0x0) { 33 | #if defined(_WIN32) || defined(WIN32) 34 | // TODO: This is deprecated on OpenGL 3+. 35 | // Use glGetIntegerv(GL_NUM_EXTENSIONS, &n) and glGetStringi(GL_EXTENSIONS, index) 36 | extensions = (const char *) glGetString(GL_EXTENSIONS); 37 | #elif defined(linux) 38 | Display *display = glXGetCurrentDisplay(); 39 | 40 | extensions = glXQueryExtensionsString(display, DefaultScreen(display)); 41 | #endif 42 | } 43 | 44 | if (extensions != 0x0) { 45 | const char *start = extensions; 46 | const char *end, *where; 47 | while(1) { 48 | where = strstr(start, name); 49 | if(where == 0x0) 50 | return false; 51 | 52 | end = where + strlen(name); 53 | if (where == start || *(where - 1) == ' ') { 54 | if (*end == ' ' || *end == 0) 55 | break; 56 | } 57 | 58 | start = end; 59 | } 60 | } 61 | 62 | return true; 63 | } 64 | 65 | //------------------------------------- 66 | #if defined(_WIN32) || defined(WIN32) 67 | bool 68 | setup_pixel_format(HDC hDC) { 69 | int pixelFormat; 70 | 71 | PIXELFORMATDESCRIPTOR pfd = { 72 | sizeof(PIXELFORMATDESCRIPTOR), // size 73 | 1, // version 74 | PFD_SUPPORT_OPENGL | // 75 | PFD_DRAW_TO_WINDOW | // 76 | PFD_DOUBLEBUFFER, // support double-buffering 77 | PFD_TYPE_RGBA, // color type 78 | 24, // preferred color depth 79 | 0, 0, 0, 0, 0, 0, // color and shift bits (ignored) 80 | 0, // no alpha buffer 81 | 0, // alpha bits (ignored) 82 | 0, // no accumulation buffer 83 | 0, 0, 0, 0, // accum bits (ignored) 84 | 24, // depth buffer 85 | 8, // no stencil buffer 86 | 0, // no auxiliary buffers 87 | PFD_MAIN_PLANE, // main layer 88 | 0, // reserved 89 | 0, 0, 0, // no layer, visible, damage masks 90 | }; 91 | 92 | pixelFormat = ChoosePixelFormat(hDC, &pfd); 93 | if (pixelFormat == 0) { 94 | MessageBox(WindowFromDC(hDC), "ChoosePixelFormat failed.", "Error", MB_ICONERROR | MB_OK); 95 | return false; 96 | } 97 | 98 | if (SetPixelFormat(hDC, pixelFormat, &pfd) != TRUE) { 99 | MessageBox(WindowFromDC(hDC), "SetPixelFormat failed.", "Error", MB_ICONERROR | MB_OK); 100 | return false; 101 | } 102 | 103 | return true; 104 | } 105 | 106 | typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int); 107 | typedef int (WINAPI * PFNWGLGETSWAPINTERVALEXTPROC)(void); 108 | PFNWGLSWAPINTERVALEXTPROC SwapIntervalEXT = 0x0; 109 | PFNWGLGETSWAPINTERVALEXTPROC GetSwapIntervalEXT = 0x0; 110 | 111 | #elif defined(linux) 112 | 113 | bool 114 | setup_pixel_format(SWindowData_X11 *window_data_x11) { 115 | GLint glxAttribs[] = { 116 | GLX_RGBA, 117 | GLX_DOUBLEBUFFER, 118 | GLX_DEPTH_SIZE, 24, 119 | GLX_STENCIL_SIZE, 8, 120 | GLX_RED_SIZE, 8, 121 | GLX_GREEN_SIZE, 8, 122 | GLX_BLUE_SIZE, 8, 123 | GLX_DEPTH_SIZE, 24, 124 | GLX_STENCIL_SIZE, 8, 125 | GLX_SAMPLE_BUFFERS, 0, 126 | GLX_SAMPLES, 0, 127 | None 128 | }; 129 | 130 | XVisualInfo* visualInfo = glXChooseVisual(window_data_x11->display, window_data_x11->screen, glxAttribs); 131 | if (visualInfo == 0) { 132 | fprintf(stderr, "Could not create correct visual window.\n"); 133 | XCloseDisplay(window_data_x11->display); 134 | return false; 135 | } 136 | window_data_x11->context = glXCreateContext(window_data_x11->display, visualInfo, NULL, GL_TRUE); 137 | 138 | return true; 139 | } 140 | 141 | typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int); 142 | PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT = 0x0; 143 | 144 | #endif 145 | 146 | //------------------------------------- 147 | bool 148 | create_GL_context(SWindowData *window_data) { 149 | #if defined(_WIN32) || defined(WIN32) 150 | SWindowData_Win *window_data_win = (SWindowData_Win *) window_data->specific; 151 | 152 | if (setup_pixel_format(window_data_win->hdc) == false) 153 | return false; 154 | 155 | window_data_win->hGLRC = wglCreateContext(window_data_win->hdc); 156 | wglMakeCurrent(window_data_win->hdc, window_data_win->hGLRC); 157 | init_GL(window_data); 158 | 159 | SwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress("wglSwapIntervalEXT"); 160 | GetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC) wglGetProcAddress("wglGetSwapIntervalEXT"); 161 | set_target_fps_aux(); 162 | 163 | return true; 164 | 165 | #elif defined(linux) 166 | SWindowData_X11 *window_data_x11 = (SWindowData_X11 *) window_data->specific; 167 | 168 | GLint majorGLX, minorGLX = 0; 169 | glXQueryVersion(window_data_x11->display, &majorGLX, &minorGLX); 170 | if (majorGLX <= 1 && minorGLX < 2) { 171 | fprintf(stderr, "GLX 1.2 or greater is required.\n"); 172 | XCloseDisplay(window_data_x11->display); 173 | return false; 174 | } 175 | else { 176 | //fprintf(stdout, "GLX version: %d.%d\n", majorGLX, minorGLX); 177 | } 178 | 179 | if (setup_pixel_format(window_data_x11) == false) 180 | return false; 181 | 182 | glXMakeCurrent(window_data_x11->display, window_data_x11->window, window_data_x11->context); 183 | 184 | //fprintf(stdout, "GL Vendor: %s\n", glGetString(GL_VENDOR)); 185 | //fprintf(stdout, "GL Renderer: %s\n", glGetString(GL_RENDERER)); 186 | //fprintf(stdout, "GL Version: %s\n", glGetString(GL_VERSION)); 187 | //fprintf(stdout, "GL Shading Language: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION)); 188 | 189 | init_GL(window_data); 190 | 191 | if (CheckGLExtension("GLX_EXT_swap_control")) { 192 | SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) glXGetProcAddress((const GLubyte *)"glXSwapIntervalEXT"); 193 | set_target_fps_aux(); 194 | } 195 | 196 | return true; 197 | #endif 198 | } 199 | 200 | //------------------------------------- 201 | void 202 | destroy_GL_context(SWindowData *window_data) { 203 | #if defined(_WIN32) || defined(WIN32) 204 | 205 | SWindowData_Win *window_data_win = (SWindowData_Win *) window_data->specific; 206 | if (window_data_win->hGLRC) { 207 | wglMakeCurrent(NULL, NULL); 208 | wglDeleteContext(window_data_win->hGLRC); 209 | window_data_win->hGLRC = 0; 210 | } 211 | 212 | #elif defined(linux) 213 | 214 | SWindowData_X11 *window_data_x11 = (SWindowData_X11 *) window_data->specific; 215 | glXDestroyContext(window_data_x11->display, window_data_x11->context); 216 | 217 | #endif 218 | } 219 | 220 | //------------------------------------- 221 | #if defined(RGB) 222 | #undef RGB 223 | #endif 224 | 225 | #define TEXTURE0 0x84C0 // [ Core in gl 1.3, gles1 1.0, gles2 2.0, glsc2 2.0, Provided by GL_ARB_multitexture (gl) ] 226 | #define RGB 0x1907 // [ Core in gl 1.0, gles1 1.0, gles2 2.0, glsc2 2.0 ] 227 | #define RGBA 0x1908 // [ Core in gl 1.0, gles1 1.0, gles2 2.0, glsc2 2.0 ] 228 | #define BGR 0x80E0 // [ Core in gl 1.2 ] 229 | #define BGRA 0x80E1 // [ Core in gl 1.2, Provided by GL_ARB_vertex_array_bgra (gl|glcore) ] 230 | 231 | //------------------------------------- 232 | void 233 | init_GL(SWindowData *window_data) { 234 | #if defined(_WIN32) || defined(WIN32) 235 | 236 | SWindowData_Win *window_data_ex = (SWindowData_Win *) window_data->specific; 237 | 238 | #elif defined(linux) 239 | 240 | SWindowData_X11 *window_data_ex = (SWindowData_X11 *) window_data->specific; 241 | 242 | #endif 243 | 244 | 245 | glViewport(0, 0, window_data->window_width, window_data->window_height); 246 | 247 | glMatrixMode(GL_PROJECTION); 248 | glLoadIdentity(); 249 | glOrtho(0, window_data->window_width, window_data->window_height, 0, 2048, -2048); 250 | 251 | glMatrixMode(GL_MODELVIEW); 252 | glLoadIdentity(); 253 | 254 | glDisable(GL_DEPTH_TEST); 255 | glDisable(GL_STENCIL_TEST); 256 | 257 | glEnable(GL_TEXTURE_2D); 258 | 259 | glGenTextures(1, &window_data_ex->text_id); 260 | //glActiveTexture(TEXTURE0); 261 | glBindTexture(GL_TEXTURE_2D, window_data_ex->text_id); 262 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 263 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 264 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 265 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 266 | 267 | glEnableClientState(GL_VERTEX_ARRAY); 268 | glEnableClientState(GL_TEXTURE_COORD_ARRAY); 269 | 270 | UseCleanUp(glDisableClientState(GL_TEXTURE_COORD_ARRAY)); 271 | UseCleanUp(glDisableClientState(GL_VERTEX_ARRAY)); 272 | UseCleanUp(glBindTexture(GL_TEXTURE_2D, 0)); 273 | } 274 | 275 | //------------------------------------- 276 | void 277 | resize_GL(SWindowData *window_data) { 278 | if (window_data->is_initialized) { 279 | #if defined(_WIN32) || defined(WIN32) 280 | 281 | SWindowData_Win *window_data_ex = (SWindowData_Win *) window_data->specific; 282 | wglMakeCurrent(window_data_ex->hdc, window_data_ex->hGLRC); 283 | 284 | #elif defined(linux) 285 | 286 | SWindowData_X11 *window_data_ex = (SWindowData_X11 *) window_data->specific; 287 | glXMakeCurrent(window_data_ex->display, window_data_ex->window, window_data_ex->context); 288 | 289 | #endif 290 | 291 | glViewport(0, 0, window_data->window_width, window_data->window_height); 292 | 293 | glMatrixMode(GL_PROJECTION); 294 | glLoadIdentity(); 295 | glOrtho(0, window_data->window_width, window_data->window_height, 0, 2048, -2048); 296 | 297 | glClear(GL_COLOR_BUFFER_BIT); 298 | } 299 | } 300 | 301 | //------------------------------------- 302 | void 303 | redraw_GL(SWindowData *window_data, const void *pixels) { 304 | #if defined(_WIN32) || defined(WIN32) 305 | 306 | SWindowData_Win *window_data_ex = (SWindowData_Win *) window_data->specific; 307 | GLenum format = BGRA; 308 | 309 | wglMakeCurrent(window_data_ex->hdc, window_data_ex->hGLRC); 310 | 311 | #elif defined(linux) 312 | 313 | SWindowData_X11 *window_data_ex = (SWindowData_X11 *) window_data->specific; 314 | GLenum format = BGRA; 315 | 316 | glXMakeCurrent(window_data_ex->display, window_data_ex->window, window_data_ex->context); 317 | 318 | #endif 319 | 320 | float x, y, w, h; 321 | 322 | x = (float) window_data->dst_offset_x; 323 | y = (float) window_data->dst_offset_y; 324 | w = (float) window_data->dst_offset_x + window_data->dst_width; 325 | h = (float) window_data->dst_offset_y + window_data->dst_height; 326 | 327 | float vertices[] = { 328 | x, y, 329 | 0, 0, 330 | 331 | w, y, 332 | 1, 0, 333 | 334 | x, h, 335 | 0, 1, 336 | 337 | w, h, 338 | 1, 1, 339 | }; 340 | 341 | glClear(GL_COLOR_BUFFER_BIT); 342 | 343 | UseCleanUp(glBindTexture(GL_TEXTURE_2D, window_data_ex->text_id)); 344 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, window_data->buffer_width, window_data->buffer_height, 0, format, GL_UNSIGNED_BYTE, pixels); 345 | //glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, window_data->buffer_width, window_data->buffer_height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 346 | 347 | UseCleanUp(glEnableClientState(GL_VERTEX_ARRAY)); 348 | UseCleanUp(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); 349 | glVertexPointer(2, GL_FLOAT, 4 * sizeof(float), vertices); 350 | glTexCoordPointer(2, GL_FLOAT, 4 * sizeof(float), vertices + 2); 351 | 352 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 353 | 354 | UseCleanUp(glDisableClientState(GL_TEXTURE_COORD_ARRAY)); 355 | UseCleanUp(glDisableClientState(GL_VERTEX_ARRAY)); 356 | UseCleanUp(glBindTexture(GL_TEXTURE_2D, 0)); 357 | 358 | #if defined(_WIN32) || defined(WIN32) 359 | SwapBuffers(window_data_ex->hdc); 360 | #elif defined(linux) 361 | glXSwapBuffers(window_data_ex->display, window_data_ex->window); 362 | #endif 363 | } 364 | 365 | //------------------------------------- 366 | void 367 | set_target_fps_aux() { 368 | // Assuming the monitor refresh rate is 60 hz 369 | int interval = (int) ((60.0 * g_time_for_frame) + 0.5); 370 | 371 | #if defined(_WIN32) || defined(WIN32) 372 | 373 | if (SwapIntervalEXT != 0x0) { 374 | bool success = SwapIntervalEXT(interval); 375 | if (GetSwapIntervalEXT != 0x0) { 376 | int currentInterval = GetSwapIntervalEXT(); 377 | if (interval != currentInterval) { 378 | fprintf(stderr, "Cannot set target swap interval. Current swap interval is %d\n", currentInterval); 379 | } 380 | } 381 | else if (success == false) { 382 | fprintf(stderr, "Cannot set target swap interval.\n"); 383 | } 384 | g_use_hardware_sync = true; 385 | } 386 | 387 | #elif defined(linux) 388 | #define kGLX_SWAP_INTERVAL_EXT 0x20F1 389 | #define kGLX_MAX_SWAP_INTERVAL_EXT 0x20F2 390 | 391 | if (SwapIntervalEXT != 0x0) { 392 | Display *dpy = glXGetCurrentDisplay(); 393 | GLXDrawable drawable = glXGetCurrentDrawable(); 394 | unsigned int currentInterval, maxInterval; 395 | 396 | SwapIntervalEXT(dpy, drawable, interval); 397 | glXQueryDrawable(dpy, drawable, kGLX_SWAP_INTERVAL_EXT, ¤tInterval); 398 | if (interval != (int)currentInterval) { 399 | glXQueryDrawable(dpy, drawable, kGLX_MAX_SWAP_INTERVAL_EXT, &maxInterval); 400 | fprintf(stderr, "Cannot set target swap interval. Current swap interval is %d (max: %d)\n", currentInterval, maxInterval); 401 | } 402 | g_use_hardware_sync = true; 403 | } 404 | 405 | #endif 406 | } 407 | 408 | #endif 409 | -------------------------------------------------------------------------------- /src/gl/MiniFB_GL.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(USE_OPENGL_API) 4 | 5 | #include 6 | 7 | bool create_GL_context(SWindowData *window_data); 8 | void destroy_GL_context(SWindowData *window_data); 9 | void init_GL(SWindowData *window_data); 10 | void redraw_GL(SWindowData *window_data, const void *pixels); 11 | void resize_GL(SWindowData *window_data); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/ios/WindowData_IOS.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | @class iOSViewDelegate; 8 | 9 | typedef struct Vertex { 10 | float x, y, z, w; 11 | } Vertex; 12 | 13 | typedef struct { 14 | iOSViewDelegate *view_delegate; 15 | Vertex vertices[4]; 16 | } SWindowData_IOS; 17 | -------------------------------------------------------------------------------- /src/ios/iOSMiniFB.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #include 4 | 5 | #include "iOSViewController.h" 6 | #include "iOSViewDelegate.h" 7 | #include "WindowData_IOS.h" 8 | #include 9 | #include 10 | #include 11 | 12 | //------------------------------------- 13 | SWindowData * 14 | create_window_data(unsigned width, unsigned height) { 15 | SWindowData *window_data; 16 | 17 | window_data = malloc(sizeof(SWindowData)); 18 | if(window_data == 0x0) { 19 | NSLog(@"Cannot allocate window data"); 20 | return 0x0; 21 | } 22 | memset(window_data, 0, sizeof(SWindowData)); 23 | 24 | SWindowData_IOS *window_data_ios = malloc(sizeof(SWindowData_IOS)); 25 | if(window_data_ios == 0x0) { 26 | free(window_data); 27 | NSLog(@"Cannot allocate ios window data"); 28 | return 0x0; 29 | } 30 | memset((void *) window_data_ios, 0, sizeof(SWindowData_IOS)); 31 | 32 | window_data->specific = window_data_ios; 33 | 34 | float scale = [UIScreen mainScreen].scale; 35 | 36 | window_data->window_width = [UIScreen mainScreen].bounds.size.width * scale; 37 | window_data->window_height = [UIScreen mainScreen].bounds.size.height * scale; 38 | 39 | calc_dst_factor(window_data, width, height); 40 | 41 | window_data->buffer_width = width; 42 | window_data->buffer_height = height; 43 | window_data->buffer_stride = width * 4; 44 | 45 | window_data->draw_buffer = malloc(width * height * 4); 46 | if (!window_data->draw_buffer) { 47 | free(window_data_ios); 48 | free(window_data); 49 | NSLog(@"Unable to create draw buffer"); 50 | return 0x0; 51 | } 52 | 53 | return window_data; 54 | } 55 | 56 | //------------------------------------- 57 | struct mfb_window * 58 | mfb_open_ex(const char *title, unsigned width, unsigned height, unsigned flags) { 59 | UIWindow *window; 60 | NSArray *windows; 61 | size_t numWindows; 62 | 63 | kUnused(title); 64 | kUnused(flags); 65 | 66 | @autoreleasepool { 67 | SWindowData *window_data = create_window_data(width, height); 68 | if (window_data == 0x0) { 69 | return 0x0; 70 | } 71 | 72 | windows = [[UIApplication sharedApplication] windows]; 73 | numWindows = [windows count]; 74 | if(numWindows > 0) { 75 | window = [windows objectAtIndex:0]; 76 | } 77 | else { 78 | // Notice that you need to set "Launch Screen File" in: 79 | // project > executable > general 80 | // to get the real size with [UIScreen mainScreen].bounds]. 81 | window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 82 | NSLog(@"UIApplication has no window. We create one (%f, %f).", [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height); 83 | } 84 | 85 | if([window.rootViewController isKindOfClass:[iOSViewController class]] == false) { 86 | iOSViewController *controller = [[iOSViewController alloc] initWithWindowData:window_data]; 87 | [window setRootViewController:controller]; 88 | #if !__has_feature(objc_arc) 89 | [controller release]; 90 | #endif 91 | controller = (iOSViewController *) window.rootViewController; 92 | } 93 | else { 94 | ((iOSViewController *) window.rootViewController)->window_data = window_data; 95 | } 96 | [window makeKeyAndVisible]; 97 | 98 | window_data->is_initialized = true; 99 | return (struct mfb_window *) window_data; 100 | } 101 | } 102 | 103 | //------------------------------------- 104 | static void 105 | destroy_window_data(SWindowData *window_data) { 106 | if(window_data == 0x0) 107 | return; 108 | 109 | @autoreleasepool { 110 | SWindowData_IOS *window_data_ios = (SWindowData_IOS *) window_data->specific; 111 | if(window_data_ios != 0x0) { 112 | memset((void *) window_data_ios, 0, sizeof(SWindowData_IOS)); 113 | free(window_data_ios); 114 | } 115 | memset(window_data, 0, sizeof(SWindowData)); 116 | free(window_data); 117 | } 118 | } 119 | 120 | //------------------------------------- 121 | mfb_update_state 122 | mfb_update_ex(struct mfb_window *window, void *buffer, unsigned width, unsigned height) { 123 | if(window == 0x0) { 124 | return STATE_INVALID_WINDOW; 125 | } 126 | 127 | SWindowData *window_data = (SWindowData *) window; 128 | if(window_data->close) { 129 | destroy_window_data(window_data); 130 | return STATE_EXIT; 131 | } 132 | 133 | if(buffer == 0x0) { 134 | return STATE_INVALID_BUFFER; 135 | } 136 | 137 | SWindowData_IOS *window_data_ios = (SWindowData_IOS *) window_data->specific; 138 | 139 | if(window_data->buffer_width != width || window_data->buffer_height != height) { 140 | window_data->buffer_width = width; 141 | window_data->buffer_stride = width * 4; 142 | window_data->buffer_height = height; 143 | window_data->draw_buffer = realloc(window_data->draw_buffer, window_data->buffer_stride * window_data->buffer_height); 144 | 145 | [window_data_ios->view_delegate resizeTextures]; 146 | } 147 | 148 | memcpy(window_data->draw_buffer, buffer, window_data->buffer_width * window_data->buffer_height * 4); 149 | 150 | return STATE_OK; 151 | } 152 | 153 | //------------------------------------- 154 | mfb_update_state 155 | mfb_update_events(struct mfb_window *window) { 156 | if(window == 0x0) { 157 | return STATE_INVALID_WINDOW; 158 | } 159 | 160 | return STATE_OK; 161 | } 162 | 163 | //------------------------------------- 164 | extern double g_time_for_frame; 165 | 166 | bool 167 | mfb_wait_sync(struct mfb_window *window) { 168 | if(window == 0x0) { 169 | return false; 170 | } 171 | 172 | return true; 173 | } 174 | 175 | //------------------------------------- 176 | bool 177 | mfb_set_viewport(struct mfb_window *window, unsigned offset_x, unsigned offset_y, unsigned width, unsigned height) { 178 | if(window == 0x0) { 179 | return false; 180 | } 181 | 182 | SWindowData *window_data = (SWindowData *) window; 183 | 184 | if(offset_x + width > window_data->window_width) { 185 | return false; 186 | } 187 | if(offset_y + height > window_data->window_height) { 188 | return false; 189 | } 190 | 191 | window_data->dst_offset_x = offset_x; 192 | window_data->dst_offset_y = offset_y; 193 | window_data->dst_width = width; 194 | window_data->dst_height = height; 195 | calc_dst_factor(window_data, window_data->window_width, window_data->window_height); 196 | 197 | float x1 = ((float) offset_x / window_data->window_width) * 2.0f - 1.0f; 198 | float x2 = (((float) offset_x + width) / window_data->window_width) * 2.0f - 1.0f; 199 | float y1 = ((float) offset_y / window_data->window_height) * 2.0f - 1.0f; 200 | float y2 = (((float) offset_y + height) / window_data->window_height) * 2.0f - 1.0f; 201 | 202 | SWindowData_IOS *window_data_ios = (SWindowData_IOS *) window_data->specific; 203 | 204 | window_data_ios->vertices[0].x = x1; 205 | window_data_ios->vertices[0].y = y1; 206 | 207 | window_data_ios->vertices[1].x = x1; 208 | window_data_ios->vertices[1].y = y2; 209 | 210 | window_data_ios->vertices[2].x = x2; 211 | window_data_ios->vertices[2].y = y1; 212 | 213 | window_data_ios->vertices[3].x = x2; 214 | window_data_ios->vertices[3].y = y2; 215 | 216 | return true; 217 | } 218 | 219 | //------------------------------------- 220 | extern double g_timer_frequency; 221 | extern double g_timer_resolution; 222 | 223 | uint64_t 224 | mfb_timer_tick() { 225 | static mach_timebase_info_data_t timebase = { 0 }; 226 | 227 | if (timebase.denom == 0) { 228 | (void) mach_timebase_info(&timebase); 229 | } 230 | 231 | uint64_t time = mach_absolute_time(); 232 | 233 | //return (time * s_timebase_info.numer) / s_timebase_info.denom; 234 | 235 | // Perform the arithmetic at 128-bit precision to avoid the overflow! 236 | uint64_t high = (time >> 32) * timebase.numer; 237 | uint64_t highRem = ((high % timebase.denom) << 32) / timebase.denom; 238 | uint64_t low = (time & 0xFFFFFFFFull) * timebase.numer / timebase.denom; 239 | high /= timebase.denom; 240 | 241 | return (high << 32) + highRem + low; 242 | } 243 | 244 | //------------------------------------- 245 | void 246 | mfb_timer_init() { 247 | g_timer_frequency = 1e+9; 248 | g_timer_resolution = 1.0 / g_timer_frequency; 249 | } 250 | 251 | //------------------------------------- 252 | void 253 | mfb_get_monitor_scale(struct mfb_window *window, float *scale_x, float *scale_y) { 254 | (void) window; 255 | float scale = 1.0f; 256 | 257 | scale = [[UIScreen mainScreen] scale]; 258 | 259 | if (scale_x) { 260 | *scale_x = scale; 261 | if(*scale_x == 0) { 262 | *scale_x = 1; 263 | } 264 | } 265 | 266 | if (scale_y) { 267 | *scale_y = scale; 268 | if (*scale_y == 0) { 269 | *scale_y = 1; 270 | } 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /src/ios/iOSView.h: -------------------------------------------------------------------------------- 1 | #import 2 | #include "WindowData.h" 3 | 4 | @interface iOSView : MTKView 5 | { 6 | @public SWindowData *window_data; 7 | } 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /src/ios/iOSView.m: -------------------------------------------------------------------------------- 1 | #include "iOSView.h" 2 | #include 3 | 4 | //------------------------------------- 5 | @implementation iOSView 6 | 7 | //------------------------------------- 8 | - (BOOL) canBecomeFirstResponder { 9 | return YES; 10 | } 11 | 12 | //------------------------------------- 13 | - (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 14 | kUnused(event); 15 | 16 | if(window_data != 0x0) { 17 | CGPoint point; 18 | int buttonNumber = MOUSE_BTN_0; 19 | for(UITouch *touch in touches) { 20 | point = [touch locationInView:self]; 21 | window_data->mouse_pos_x = point.x; 22 | window_data->mouse_pos_y = point.y; 23 | window_data->mouse_button_status[buttonNumber & 0x07] = true; 24 | kCall(mouse_btn_func, buttonNumber, 0, true); 25 | ++buttonNumber; 26 | } 27 | } 28 | } 29 | 30 | //------------------------------------- 31 | - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { 32 | kUnused(event); 33 | 34 | if(window_data != 0x0) { 35 | CGPoint point; 36 | int buttonNumber = MOUSE_BTN_0; 37 | for(UITouch *touch in touches) { 38 | point = [touch locationInView:self]; 39 | window_data->mouse_pos_x = point.x; 40 | window_data->mouse_pos_y = point.y; 41 | window_data->mouse_button_status[buttonNumber & 0x07] = true; 42 | kCall(mouse_move_func, point.x, point.y); 43 | ++buttonNumber; 44 | } 45 | } 46 | } 47 | 48 | //------------------------------------- 49 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 50 | kUnused(event); 51 | 52 | if(window_data != 0x0) { 53 | CGPoint point; 54 | int buttonNumber = MOUSE_BTN_0; 55 | for(UITouch *touch in touches) { 56 | point = [touch locationInView:self]; 57 | window_data->mouse_pos_x = point.x; 58 | window_data->mouse_pos_y = point.y; 59 | window_data->mouse_button_status[buttonNumber & 0x07] = false; 60 | kCall(mouse_btn_func, buttonNumber, 0, false); 61 | ++buttonNumber; 62 | } 63 | } 64 | } 65 | 66 | //------------------------------------- 67 | - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { 68 | kUnused(event); 69 | 70 | if(window_data != 0x0) { 71 | CGPoint point; 72 | int buttonNumber = MOUSE_BTN_0; 73 | for(UITouch *touch in touches) { 74 | point = [touch locationInView:self]; 75 | window_data->mouse_pos_x = point.x; 76 | window_data->mouse_pos_y = point.y; 77 | window_data->mouse_button_status[buttonNumber & 0x07] = false; 78 | kCall(mouse_btn_func, buttonNumber, 0, false); 79 | ++buttonNumber; 80 | } 81 | } 82 | } 83 | 84 | @end 85 | -------------------------------------------------------------------------------- /src/ios/iOSViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // iOSViewController.h 3 | // MiniFB 4 | // 5 | // Created by Carlos Aragones on 22/04/2020. 6 | // Copyright © 2020 Carlos Aragones. All rights reserved. 7 | // 8 | 9 | #import 10 | #include "WindowData.h" 11 | 12 | @interface iOSViewController : UIViewController 13 | { 14 | @public SWindowData *window_data; 15 | } 16 | 17 | - (id) initWithWindowData:(SWindowData *) windowData; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /src/ios/iOSViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // iOSViewController.m 3 | // MiniFB 4 | // 5 | // Created by Carlos Aragones on 22/04/2020. 6 | // Copyright © 2020 Carlos Aragones. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "iOSViewController.h" 12 | #import "iOSViewDelegate.h" 13 | #import "iOSView.h" 14 | #include "WindowData_IOS.h" 15 | 16 | //------------------------------------- 17 | @implementation iOSViewController 18 | { 19 | iOSView *metal_view; 20 | //iOSViewDelegate *view_delegate; 21 | } 22 | 23 | //------------------------------------- 24 | - (id) initWithWindowData:(SWindowData *) windowData { 25 | self = [super init]; 26 | if (self) { 27 | window_data = windowData; 28 | } 29 | return self; 30 | } 31 | 32 | //------------------------------------- 33 | - (void) loadView { 34 | iOSView *view = [[iOSView alloc] initWithFrame:[UIScreen mainScreen].bounds]; 35 | // Probably the window was created automatically by an storyboard or similar 36 | if(window_data == 0x0) { 37 | NSLog(@"WindowData is null!"); 38 | } 39 | view->window_data = window_data; 40 | view.userInteractionEnabled = true; 41 | 42 | [self setView:view]; 43 | #if !__has_feature(objc_arc) 44 | [view release]; 45 | #endif 46 | } 47 | 48 | //------------------------------------- 49 | - (void) viewDidLoad 50 | { 51 | [super viewDidLoad]; 52 | 53 | metal_view = (iOSView *) self.view; 54 | metal_view.device = MTLCreateSystemDefaultDevice(); 55 | metal_view.backgroundColor = UIColor.blackColor; 56 | 57 | if(!metal_view.device) { 58 | NSLog(@"Metal is not supported on this device"); 59 | self.view = [[UIView alloc] initWithFrame:self.view.frame]; 60 | return; 61 | } 62 | 63 | SWindowData_IOS *window_data_ios = (SWindowData_IOS *) window_data->specific; 64 | window_data_ios->view_delegate = [[iOSViewDelegate alloc] initWithMetalKitView:metal_view windowData:window_data]; 65 | [window_data_ios->view_delegate mtkView:metal_view drawableSizeWillChange:metal_view.bounds.size]; 66 | 67 | metal_view.delegate = window_data_ios->view_delegate; 68 | } 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /src/ios/iOSViewDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // Renderer.h 3 | // MiniFB 4 | // 5 | // Created by Carlos Aragones on 22/04/2020. 6 | // Copyright © 2020 Carlos Aragones. All rights reserved. 7 | // 8 | 9 | #import 10 | #include "WindowData.h" 11 | 12 | // Our platform independent renderer class. 13 | // Implements the MTKViewDelegate protocol which allows it to accept per-frame 14 | // update and drawable resize callbacks. 15 | @interface iOSViewDelegate : NSObject 16 | { 17 | } 18 | 19 | -(nonnull instancetype) initWithMetalKitView:(nonnull MTKView *) view windowData:(nonnull SWindowData *) windowData; 20 | - (void) resizeTextures; 21 | 22 | @end 23 | 24 | -------------------------------------------------------------------------------- /src/ios/iOSViewDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // Renderer.m 3 | // MiniFB 4 | // 5 | // Created by Carlos Aragones on 22/04/2020. 6 | // Copyright © 2020 Carlos Aragones. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | #import "iOSViewDelegate.h" 13 | #include "WindowData_IOS.h" 14 | #include 15 | #include 16 | #include 17 | 18 | //------------------------------------- 19 | #define kShader(inc, src) @inc#src 20 | 21 | //------------------------------------- 22 | enum { MaxBuffersInFlight = 3 }; // Number of textures in flight (tripple buffered) 23 | 24 | //-- 25 | NSString *g_shader_src = kShader( 26 | "#include \n", 27 | using namespace metal; 28 | 29 | //------------- 30 | struct VertexOutput { 31 | float4 pos [[position]]; 32 | float2 texcoord; 33 | }; 34 | 35 | //------------- 36 | struct Vertex { 37 | float4 position [[position]]; 38 | }; 39 | 40 | //------------- 41 | vertex VertexOutput 42 | vertFunc(unsigned int vID[[vertex_id]], const device Vertex *pos [[ buffer(0) ]]) { 43 | VertexOutput out; 44 | 45 | out.pos = pos[vID].position; 46 | 47 | out.texcoord.x = (float) (vID / 2); 48 | out.texcoord.y = 1.0 - (float) (vID % 2); 49 | 50 | return out; 51 | } 52 | 53 | //------------- 54 | fragment float4 55 | fragFunc(VertexOutput input [[stage_in]], texture2d colorTexture [[ texture(0) ]]) { 56 | constexpr sampler textureSampler(mag_filter::nearest, min_filter::nearest); 57 | 58 | // Sample the texture to obtain a color 59 | const half4 colorSample = colorTexture.sample(textureSampler, input.texcoord); 60 | 61 | // We return the color of the texture 62 | return float4(colorSample); 63 | }; 64 | ); 65 | 66 | //------------------------------------- 67 | @implementation iOSViewDelegate { 68 | SWindowData *window_data; 69 | SWindowData_IOS *window_data_ios; 70 | 71 | id metal_device; 72 | id metal_library; 73 | 74 | dispatch_semaphore_t semaphore; 75 | id command_queue; 76 | 77 | id pipeline_state; 78 | id texture_buffer; 79 | 80 | uint8_t current_buffer; 81 | } 82 | 83 | //------------------------------------- 84 | -(nonnull instancetype) initWithMetalKitView:(nonnull MTKView *) view windowData:(nonnull SWindowData *) windowData { 85 | self = [super init]; 86 | if (self) { 87 | window_data = windowData; 88 | window_data_ios = (SWindowData_IOS *) windowData->specific; 89 | 90 | view.colorPixelFormat = MTLPixelFormatBGRA8Unorm; 91 | view.sampleCount = 1; 92 | 93 | metal_device = view.device; 94 | 95 | // Used for syncing the CPU and GPU 96 | semaphore = dispatch_semaphore_create(MaxBuffersInFlight); 97 | 98 | // Setup command queue 99 | command_queue = [metal_device newCommandQueue]; 100 | 101 | [self _createShaders]; 102 | [self _createAssets]; 103 | } 104 | 105 | return self; 106 | } 107 | 108 | //------------------------------------- 109 | - (bool) _createShaders { 110 | NSError *error = 0x0; 111 | 112 | metal_library = [metal_device newLibraryWithSource:g_shader_src 113 | options:[[MTLCompileOptions alloc] init] 114 | error:&error 115 | ]; 116 | if (error || !metal_library) { 117 | NSLog(@"Unable to create shaders %@", error); 118 | return false; 119 | } 120 | 121 | id vertex_shader_func = [metal_library newFunctionWithName:@"vertFunc"]; 122 | id fragment_shader_func = [metal_library newFunctionWithName:@"fragFunc"]; 123 | 124 | if (!vertex_shader_func) { 125 | NSLog(@"Unable to get vertFunc!\n"); 126 | return false; 127 | } 128 | 129 | if (!fragment_shader_func) { 130 | NSLog(@"Unable to get fragFunc!\n"); 131 | return false; 132 | } 133 | 134 | // Create a reusable pipeline state 135 | MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; 136 | pipelineStateDescriptor.label = @"MiniFB_pipeline"; 137 | pipelineStateDescriptor.vertexFunction = vertex_shader_func; 138 | pipelineStateDescriptor.fragmentFunction = fragment_shader_func; 139 | pipelineStateDescriptor.colorAttachments[0].pixelFormat = 80; //bgra8Unorm; 140 | 141 | pipeline_state = [metal_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error]; 142 | if (!pipeline_state) { 143 | NSLog(@"Failed to created pipeline state, error %@", error); 144 | return false; 145 | } 146 | 147 | return true; 148 | } 149 | 150 | //------------------------------------- 151 | - (void) _createAssets { 152 | static Vertex s_vertices[4] = { 153 | {-1.0, -1.0, 0, 1}, 154 | {-1.0, 1.0, 0, 1}, 155 | { 1.0, -1.0, 0, 1}, 156 | { 1.0, 1.0, 0, 1}, 157 | }; 158 | memcpy(window_data_ios->vertices, s_vertices, sizeof(s_vertices)); 159 | 160 | MTLTextureDescriptor *td; 161 | td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm 162 | width:window_data->buffer_width 163 | height:window_data->buffer_height 164 | mipmapped:false]; 165 | 166 | // Create the texture from the device by using the descriptor 167 | texture_buffer = [metal_device newTextureWithDescriptor:td]; 168 | } 169 | 170 | //------------------------------------- 171 | - (void) resizeTextures { 172 | MTLTextureDescriptor *td; 173 | td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm 174 | width:window_data->buffer_width 175 | height:window_data->buffer_height 176 | mipmapped:false]; 177 | 178 | // Create the texture from the device by using the descriptor 179 | [texture_buffer release]; 180 | texture_buffer = [metal_device newTextureWithDescriptor:td]; 181 | } 182 | 183 | //------------------------------------- 184 | - (void) drawInMTKView:(nonnull MTKView *) view { 185 | // Wait to ensure only MaxBuffersInFlight number of frames are getting proccessed 186 | // by any stage in the Metal pipeline (App, Metal, Drivers, GPU, etc) 187 | dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 188 | 189 | current_buffer = (current_buffer + 1) % MaxBuffersInFlight; 190 | 191 | // Create a new command buffer for each render pass to the current drawable 192 | id commandBuffer = [command_queue commandBuffer]; 193 | commandBuffer.label = @"minifb_command_buffer"; 194 | 195 | // Add completion hander which signals semaphore when Metal and the GPU has fully 196 | // finished processing the commands we're encoding this frame. This indicates when the 197 | // dynamic buffers filled with our vertices, that we're writing to this frame, will no longer 198 | // be needed by Metal and the GPU, meaning we can overwrite the buffer contents without 199 | // corrupting the rendering. 200 | __block dispatch_semaphore_t block_sema = semaphore; 201 | [commandBuffer addCompletedHandler:^(id buffer) { 202 | (void)buffer; 203 | dispatch_semaphore_signal(block_sema); 204 | }]; 205 | 206 | // Copy the bytes from our data object into the texture 207 | MTLRegion region = { { 0, 0, 0 }, { window_data->buffer_width, window_data->buffer_height, 1 } }; 208 | [texture_buffer replaceRegion:region mipmapLevel:0 withBytes:window_data->draw_buffer bytesPerRow:window_data->buffer_stride]; 209 | 210 | // Delay getting the currentRenderPassDescriptor until absolutely needed. This avoids 211 | // holding onto the drawable and blocking the display pipeline any longer than necessary 212 | MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor; 213 | if (renderPassDescriptor != nil) { 214 | //renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0); 215 | 216 | // Create a render command encoder so we can render into something 217 | id renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; 218 | renderEncoder.label = @"minifb_command_encoder"; 219 | 220 | // Set render command encoder state 221 | [renderEncoder setRenderPipelineState:pipeline_state]; 222 | [renderEncoder setVertexBytes:window_data_ios->vertices length:sizeof(window_data_ios->vertices) atIndex:0]; 223 | 224 | //[renderEncoder setFragmentTexture:texture_buffers[current_buffer] atIndex:0]; 225 | [renderEncoder setFragmentTexture:texture_buffer atIndex:0]; 226 | 227 | // Draw the vertices of our quads 228 | [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; 229 | 230 | // We're done encoding commands 231 | [renderEncoder endEncoding]; 232 | 233 | // Schedule a present once the framebuffer is complete using the current drawable 234 | [commandBuffer presentDrawable:view.currentDrawable]; 235 | } 236 | 237 | // Finalize rendering here & push the command buffer to the GPU 238 | [commandBuffer commit]; 239 | } 240 | 241 | //------------------------------------- 242 | - (void) mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size { 243 | (void) view; 244 | // Respond to drawable size or orientation changes here 245 | float scale = [UIScreen mainScreen].scale; 246 | 247 | window_data->window_width = size.width * scale; 248 | window_data->window_height = size.height * scale; 249 | resize_dst(window_data, size.width, size.height); 250 | 251 | kCall(resize_func, size.width, size.height); 252 | } 253 | 254 | @end 255 | -------------------------------------------------------------------------------- /src/macosx/OSXView.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #include "WindowData.h" 4 | 5 | @interface OSXView : NSView 6 | { 7 | @public SWindowData *window_data; 8 | #if defined(USE_METAL_API) 9 | @private NSTrackingArea *tracking_area; 10 | #endif 11 | } 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /src/macosx/OSXView.m: -------------------------------------------------------------------------------- 1 | #import "OSXView.h" 2 | #import "OSXWindow.h" 3 | #import "WindowData_OSX.h" 4 | #include 5 | 6 | //------------------------------------- 7 | @implementation OSXView 8 | 9 | #if defined(USE_METAL_API) 10 | 11 | //------------------------------------- 12 | - (void)updateTrackingAreas { 13 | if(tracking_area != nil) { 14 | [self removeTrackingArea:tracking_area]; 15 | [tracking_area release]; 16 | } 17 | 18 | int opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways); 19 | tracking_area = [[NSTrackingArea alloc] initWithRect:[self bounds] 20 | options:opts 21 | owner:self 22 | userInfo:nil]; 23 | [self addTrackingArea:tracking_area]; 24 | } 25 | 26 | #else 27 | 28 | //------------------------------------- 29 | - (NSRect)resizeRect { 30 | const CGFloat resizeBoxSize = 16.0; 31 | const CGFloat contentViewPadding = 5.5; 32 | 33 | NSRect contentViewRect = [[self window] contentRectForFrameRect:[[self window] frame]]; 34 | NSRect resizeRect = NSMakeRect( 35 | NSMaxX(contentViewRect) + contentViewPadding, 36 | NSMinY(contentViewRect) - resizeBoxSize - contentViewPadding, 37 | resizeBoxSize, 38 | resizeBoxSize 39 | ); 40 | 41 | return resizeRect; 42 | } 43 | 44 | //------------------------------------- 45 | - (void)drawRect:(NSRect)rect { 46 | (void)rect; 47 | 48 | if(window_data == 0x0) 49 | return; 50 | 51 | SWindowData_OSX *window_data_osx = (SWindowData_OSX *) window_data->specific; 52 | if (!window_data_osx || !window_data_osx->window || !window_data->draw_buffer) 53 | return; 54 | 55 | CGContextRef context = [[NSGraphicsContext currentContext] CGContext]; 56 | 57 | CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); 58 | CGDataProviderRef provider = CGDataProviderCreateWithData(0x0, 59 | window_data->draw_buffer, 60 | window_data->buffer_width * window_data->buffer_height * 4, 61 | 0x0 62 | ); 63 | 64 | CGImageRef img = CGImageCreate(window_data->buffer_width 65 | , window_data->buffer_height 66 | , 8 67 | , 32 68 | , window_data->buffer_width * 4 69 | , space 70 | , kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little 71 | , provider 72 | , 0x0 73 | , false 74 | , kCGRenderingIntentDefault 75 | ); 76 | 77 | const CGFloat components[] = {0.0f, 0.0f, 0.0f, 1.0f}; 78 | const CGColorRef black = CGColorCreate(space, components); 79 | 80 | CGColorSpaceRelease(space); 81 | CGDataProviderRelease(provider); 82 | 83 | if(window_data->dst_offset_x != 0 || window_data->dst_offset_y != 0 || window_data->dst_width != window_data->window_width || window_data->dst_height != window_data->window_height) { 84 | CGContextSetFillColorWithColor(context, black); 85 | CGContextFillRect(context, rect); 86 | } 87 | 88 | // TODO: Sometimes there is a crash here 89 | CGContextDrawImage(context, 90 | CGRectMake(window_data->dst_offset_x, window_data->dst_offset_y, window_data->dst_width, window_data->dst_height), 91 | img 92 | ); 93 | 94 | CGImageRelease(img); 95 | } 96 | 97 | #endif 98 | 99 | //------------------------------------- 100 | - (BOOL)acceptsFirstMouse:(NSEvent *)event { 101 | (void)event; 102 | return YES; 103 | } 104 | 105 | //------------------------------------- 106 | - (void)mouseDown:(NSEvent*)event { 107 | (void)event; 108 | if(window_data != 0x0) { 109 | window_data->mouse_button_status[MOUSE_BTN_1] = true; 110 | kCall(mouse_btn_func, MOUSE_BTN_1, window_data->mod_keys, true); 111 | } 112 | } 113 | 114 | //------------------------------------- 115 | - (void)mouseUp:(NSEvent*)event { 116 | (void)event; 117 | if(window_data != 0x0) { 118 | window_data->mouse_button_status[MOUSE_BTN_1] = false; 119 | kCall(mouse_btn_func, MOUSE_BTN_1, window_data->mod_keys, false); 120 | } 121 | } 122 | 123 | //------------------------------------- 124 | - (void)rightMouseDown:(NSEvent*)event { 125 | (void)event; 126 | if(window_data != 0x0) { 127 | window_data->mouse_button_status[MOUSE_BTN_2] = true; 128 | kCall(mouse_btn_func, MOUSE_BTN_2, window_data->mod_keys, true); 129 | } 130 | } 131 | 132 | //------------------------------------- 133 | - (void)rightMouseUp:(NSEvent*)event { 134 | (void)event; 135 | if(window_data != 0x0) { 136 | window_data->mouse_button_status[MOUSE_BTN_2] = false; 137 | kCall(mouse_btn_func, MOUSE_BTN_2, window_data->mod_keys, false); 138 | } 139 | } 140 | 141 | //------------------------------------- 142 | - (void)otherMouseDown:(NSEvent *)event { 143 | (void)event; 144 | if(window_data != 0x0) { 145 | window_data->mouse_button_status[[event buttonNumber] & 0x07] = true; 146 | kCall(mouse_btn_func, [event buttonNumber], window_data->mod_keys, true); 147 | } 148 | } 149 | 150 | //------------------------------------- 151 | - (void)otherMouseUp:(NSEvent *)event { 152 | (void)event; 153 | if(window_data != 0x0) { 154 | window_data->mouse_button_status[[event buttonNumber] & 0x07] = false; 155 | kCall(mouse_btn_func, [event buttonNumber], window_data->mod_keys, false); 156 | } 157 | } 158 | 159 | //------------------------------------- 160 | - (void)scrollWheel:(NSEvent *)event { 161 | if(window_data != 0x0) { 162 | window_data->mouse_wheel_x = [event deltaX]; 163 | window_data->mouse_wheel_y = [event deltaY]; 164 | kCall(mouse_wheel_func, window_data->mod_keys, window_data->mouse_wheel_x, window_data->mouse_wheel_y); 165 | } 166 | } 167 | 168 | //------------------------------------- 169 | - (void)mouseDragged:(NSEvent *)event { 170 | [self mouseMoved:event]; 171 | } 172 | 173 | //------------------------------------- 174 | - (void)rightMouseDragged:(NSEvent *)event { 175 | [self mouseMoved:event]; 176 | } 177 | 178 | //------------------------------------- 179 | - (void)otherMouseDragged:(NSEvent *)event { 180 | [self mouseMoved:event]; 181 | } 182 | 183 | //------------------------------------- 184 | - (void)mouseMoved:(NSEvent *)event { 185 | if(window_data != 0x0) { 186 | NSPoint point = [event locationInWindow]; 187 | //NSPoint localPoint = [self convertPoint:point fromView:nil]; 188 | window_data->mouse_pos_x = point.x; 189 | #if defined(USE_INVERTED_Y_ON_MACOS) 190 | window_data->mouse_pos_y = point.y; 191 | #else 192 | window_data->mouse_pos_y = window_data->window_height - point.y; 193 | #endif 194 | kCall(mouse_move_func, window_data->mouse_pos_x, window_data->mouse_pos_y); 195 | } 196 | } 197 | 198 | //------------------------------------- 199 | - (void)mouseExited:(NSEvent *)event { 200 | (void)event; 201 | //printf("mouse exit\n"); 202 | } 203 | 204 | //------------------------------------- 205 | - (void)mouseEntered:(NSEvent *)event { 206 | (void)event; 207 | //printf("mouse enter\n"); 208 | } 209 | 210 | //------------------------------------- 211 | - (BOOL)canBecomeKeyView { 212 | return YES; 213 | } 214 | 215 | //------------------------------------- 216 | - (NSView *)nextValidKeyView { 217 | return self; 218 | } 219 | 220 | //------------------------------------- 221 | - (NSView *)previousValidKeyView { 222 | return self; 223 | } 224 | 225 | //------------------------------------- 226 | - (BOOL)acceptsFirstResponder { 227 | return YES; 228 | } 229 | 230 | //------------------------------------- 231 | - (void)viewDidMoveToWindow { 232 | } 233 | 234 | //------------------------------------- 235 | - (void)dealloc { 236 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 237 | [super dealloc]; 238 | } 239 | 240 | #pragma mark NSTextInputClient 241 | 242 | //------------------------------------- 243 | // [Binding Keystrokes] 244 | //------------------------------------- 245 | 246 | // Invokes the action specified by the given selector. 247 | //------------------------------------- 248 | - (void)doCommandBySelector:(nonnull SEL)selector { 249 | kUnused(selector); 250 | } 251 | 252 | //------------------------------------- 253 | // [Storing Text] 254 | //------------------------------------- 255 | 256 | // Returns an attributed string derived from the given range in the receiver's text storage. 257 | //------------------------------------- 258 | - (nullable NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(nullable NSRangePointer)actualRange { 259 | kUnused(range); 260 | kUnused(actualRange); 261 | return nil; 262 | } 263 | 264 | // Inserts the given string into the receiver, replacing the specified content. 265 | //------------------------------------- 266 | - (void)insertText:(nonnull id)string replacementRange:(NSRange)replacementRange { 267 | kUnused(replacementRange); 268 | 269 | if(window_data != 0x0) { 270 | NSString *characters; 271 | NSUInteger codepoint; 272 | 273 | if ([string isKindOfClass:[NSAttributedString class]]) 274 | characters = [string string]; 275 | else 276 | characters = (NSString*) string; 277 | 278 | NSRange range = NSMakeRange(0, [characters length]); 279 | while (range.length) { 280 | codepoint = 0; 281 | if ([characters getBytes:&codepoint 282 | maxLength:sizeof(codepoint) 283 | usedLength:NULL 284 | encoding:NSUTF32StringEncoding // NSUTF8StringEncoding 285 | options:0 286 | range:range 287 | remainingRange:&range]) { 288 | 289 | if ((codepoint & 0xff00) == 0xf700) 290 | continue; 291 | 292 | kCall(char_input_func, codepoint); 293 | } 294 | } 295 | } 296 | } 297 | 298 | //------------------------------------- 299 | // [Getting Character Coordinates] 300 | //------------------------------------- 301 | 302 | // Returns the index of the character whose bounding rectangle includes the given point. 303 | //------------------------------------- 304 | - (NSUInteger)characterIndexForPoint:(NSPoint)point { 305 | kUnused(point); 306 | return 0; 307 | } 308 | 309 | // Returns the first logical boundary rectangle for characters in the given range. 310 | //------------------------------------- 311 | - (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(nullable NSRangePointer)actualRange { 312 | kUnused(range); 313 | kUnused(actualRange); 314 | return NSMakeRect(0.0, 0.0, 0.0, 0.0); 315 | } 316 | 317 | //------------------------------------- 318 | // [Handling Marked Text] 319 | //------------------------------------- 320 | 321 | //------------------------------------- 322 | static const NSRange kEmptyRange = { NSNotFound, 0 }; 323 | 324 | // Returns a Boolean value indicating whether the receiver has marked text. 325 | //------------------------------------- 326 | - (BOOL)hasMarkedText { 327 | return false; 328 | } 329 | 330 | // Returns the range of the marked text. 331 | //------------------------------------- 332 | - (NSRange)markedRange { 333 | return kEmptyRange; 334 | } 335 | 336 | // Returns the range of selected text. 337 | //------------------------------------- 338 | - (NSRange)selectedRange { 339 | return kEmptyRange; 340 | } 341 | 342 | // Replaces a specified range in the receiver’s text storage with the given string and sets the selection. 343 | //------------------------------------- 344 | - (void)setMarkedText:(nonnull id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange { 345 | kUnused(string); 346 | kUnused(selectedRange); 347 | kUnused(replacementRange); 348 | } 349 | 350 | // Unmarks the marked text. 351 | //------------------------------------- 352 | - (void)unmarkText { 353 | } 354 | 355 | // Returns an array of attribute names recognized by the receiver. 356 | //------------------------------------- 357 | - (nonnull NSArray *)validAttributesForMarkedText { 358 | return [NSArray array]; 359 | } 360 | //---- 361 | 362 | @end 363 | -------------------------------------------------------------------------------- /src/macosx/OSXViewDelegate.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(USE_METAL_API) 4 | 5 | #import 6 | #include "WindowData_OSX.h" 7 | 8 | // Number of textures in flight (tripple buffered) 9 | enum { MaxBuffersInFlight = 3 }; 10 | 11 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 12 | 13 | @interface OSXViewDelegate : NSViewController 14 | { 15 | @public SWindowData *window_data; 16 | @public SWindowData_OSX *window_data_osx; 17 | 18 | id metal_device; 19 | id metal_library; 20 | 21 | dispatch_semaphore_t semaphore; // Used for syncing with CPU/GPU 22 | id command_queue; 23 | 24 | id pipeline_state; 25 | id texture_buffers[MaxBuffersInFlight]; 26 | 27 | int current_buffer; 28 | } 29 | 30 | - (id) initWithWindowData:(SWindowData *) windowData; 31 | - (void) resizeTextures; 32 | 33 | @end 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/macosx/OSXViewDelegate.m: -------------------------------------------------------------------------------- 1 | #include "OSXViewDelegate.h" 2 | 3 | #if defined(USE_METAL_API) 4 | 5 | #import 6 | 7 | extern double g_time_for_frame; 8 | extern bool g_use_hardware_sync; 9 | //-- 10 | bool g_target_fps_changed = true; 11 | 12 | //------------------------------------- 13 | void 14 | set_target_fps_aux() { 15 | g_target_fps_changed = true; 16 | } 17 | 18 | //------------------------------------- 19 | #define kShader(inc, src) @inc#src 20 | 21 | NSString *g_shader_src = kShader( 22 | "#include \n", 23 | using namespace metal; 24 | 25 | //------------- 26 | struct VertexOutput { 27 | float4 pos [[position]]; 28 | float2 texcoord; 29 | }; 30 | 31 | //------------- 32 | struct Vertex { 33 | float4 position [[position]]; 34 | }; 35 | 36 | //------------- 37 | vertex VertexOutput 38 | vertFunc(unsigned int vID[[vertex_id]], const device Vertex *pos [[ buffer(0) ]]) { 39 | VertexOutput out; 40 | 41 | out.pos = pos[vID].position; 42 | 43 | out.texcoord.x = (float) (vID / 2); 44 | out.texcoord.y = 1.0 - (float) (vID % 2); 45 | 46 | return out; 47 | } 48 | 49 | //------------- 50 | fragment float4 51 | fragFunc(VertexOutput input [[stage_in]], texture2d colorTexture [[ texture(0) ]]) { 52 | constexpr sampler textureSampler(mag_filter::nearest, min_filter::nearest); 53 | 54 | // Sample the texture to obtain a color 55 | const half4 colorSample = colorTexture.sample(textureSampler, input.texcoord); 56 | 57 | // We return the color of the texture 58 | return float4(colorSample); 59 | }; 60 | ); 61 | 62 | //------------------------------------- 63 | @implementation OSXViewDelegate 64 | 65 | //------------------------------------- 66 | - (id) initWithWindowData:(SWindowData *) windowData { 67 | self = [super init]; 68 | if (self) { 69 | window_data = windowData; 70 | window_data_osx = (SWindowData_OSX *) windowData->specific; 71 | 72 | metal_device = MTLCreateSystemDefaultDevice(); 73 | if (!metal_device) { 74 | NSLog(@"Metal is not supported on this device"); 75 | return 0x0; 76 | } 77 | 78 | // Used for syncing the CPU and GPU 79 | semaphore = dispatch_semaphore_create(MaxBuffersInFlight); 80 | 81 | // Setup command queue 82 | command_queue = [metal_device newCommandQueue]; 83 | 84 | // MacOS Mojave is ignoring view.preferredFramesPerSecond 85 | // MacOS Big Sur is ignoring commandBuffer:presentDrawable:afterMinimumDuration: 86 | //id commandBuffer = [command_queue commandBuffer]; 87 | //if ([commandBuffer respondsToSelector:@selector(presentDrawable:afterMinimumDuration:)]) { 88 | // g_use_hardware_sync = true; 89 | //} 90 | 91 | [self _createShaders]; 92 | [self _createAssets]; 93 | } 94 | return self; 95 | } 96 | 97 | //------------------------------------- 98 | - (bool) _createShaders { 99 | NSError *error = 0x0; 100 | 101 | metal_library = [metal_device newLibraryWithSource:g_shader_src 102 | options:[[MTLCompileOptions alloc] init] 103 | error:&error 104 | ]; 105 | if (error || !metal_library) { 106 | NSLog(@"Unable to create shaders %@", error); 107 | return false; 108 | } 109 | 110 | id vertex_shader_func = [metal_library newFunctionWithName:@"vertFunc"]; 111 | id fragment_shader_func = [metal_library newFunctionWithName:@"fragFunc"]; 112 | 113 | if (!vertex_shader_func) { 114 | NSLog(@"Unable to get vertFunc!\n"); 115 | return false; 116 | } 117 | 118 | if (!fragment_shader_func) { 119 | NSLog(@"Unable to get fragFunc!\n"); 120 | return false; 121 | } 122 | 123 | // Create a reusable pipeline state 124 | MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; 125 | pipelineStateDescriptor.label = @"MiniFB_pipeline"; 126 | pipelineStateDescriptor.vertexFunction = vertex_shader_func; 127 | pipelineStateDescriptor.fragmentFunction = fragment_shader_func; 128 | pipelineStateDescriptor.colorAttachments[0].pixelFormat = 80; //bgra8Unorm; 129 | 130 | pipeline_state = [metal_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error]; 131 | if (!pipeline_state) { 132 | NSLog(@"Failed to created pipeline state, error %@", error); 133 | return false; 134 | } 135 | 136 | return true; 137 | } 138 | 139 | //------------------------------------- 140 | - (void) _createAssets { 141 | static Vertex s_vertices[4] = { 142 | {-1.0, -1.0, 0, 1}, 143 | {-1.0, 1.0, 0, 1}, 144 | { 1.0, -1.0, 0, 1}, 145 | { 1.0, 1.0, 0, 1}, 146 | }; 147 | memcpy(window_data_osx->metal.vertices, s_vertices, sizeof(s_vertices)); 148 | 149 | MTLTextureDescriptor *td; 150 | td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm 151 | width:window_data->buffer_width 152 | height:window_data->buffer_height 153 | mipmapped:false]; 154 | 155 | // Create the texture from the device by using the descriptor 156 | for (size_t i = 0; i < MaxBuffersInFlight; ++i) { 157 | texture_buffers[i] = [metal_device newTextureWithDescriptor:td]; 158 | } 159 | } 160 | 161 | //------------------------------------- 162 | - (void) resizeTextures { 163 | MTLTextureDescriptor *td; 164 | td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm 165 | width:window_data->buffer_width 166 | height:window_data->buffer_height 167 | mipmapped:false]; 168 | 169 | // Create the texture from the device by using the descriptor 170 | for (size_t i = 0; i < MaxBuffersInFlight; ++i) { 171 | [texture_buffers[i] release]; 172 | texture_buffers[i] = [metal_device newTextureWithDescriptor:td]; 173 | } 174 | } 175 | 176 | //------------------------------------- 177 | - (void) drawInMTKView:(nonnull MTKView *) view { 178 | if (g_target_fps_changed) { 179 | // MacOS is ignoring this :( 180 | if (g_time_for_frame == 0) { 181 | // Contrary to what is stated in the documentation, 182 | // 0 means that it does not update. Like pause. 183 | view.preferredFramesPerSecond = 9999; 184 | } 185 | else { 186 | view.preferredFramesPerSecond = (int) (1.0 / g_time_for_frame); 187 | } 188 | g_target_fps_changed = false; 189 | } 190 | 191 | // Wait to ensure only MaxBuffersInFlight number of frames are getting proccessed 192 | // by any stage in the Metal pipeline (App, Metal, Drivers, GPU, etc) 193 | dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 194 | 195 | current_buffer = (current_buffer + 1) % MaxBuffersInFlight; 196 | 197 | // Create a new command buffer for each render pass to the current drawable 198 | id commandBuffer = [command_queue commandBuffer]; 199 | commandBuffer.label = @"minifb_command_buffer"; 200 | 201 | // Add completion hander which signals semaphore when Metal and the GPU has fully 202 | // finished processing the commands we're encoding this frame. This indicates when the 203 | // dynamic buffers filled with our vertices, that we're writing to this frame, will no longer 204 | // be needed by Metal and the GPU, meaning we can overwrite the buffer contents without 205 | // corrupting the rendering. 206 | __block dispatch_semaphore_t block_sema = semaphore; 207 | [commandBuffer addCompletedHandler:^(id buffer) { 208 | (void)buffer; 209 | dispatch_semaphore_signal(block_sema); 210 | }]; 211 | 212 | // Copy the bytes from our data object into the texture 213 | MTLRegion region = { { 0, 0, 0 }, { window_data->buffer_width, window_data->buffer_height, 1 } }; 214 | [texture_buffers[current_buffer] replaceRegion:region mipmapLevel:0 withBytes:window_data->draw_buffer bytesPerRow:window_data->buffer_stride]; 215 | 216 | // Delay getting the currentRenderPassDescriptor until absolutely needed. This avoids 217 | // holding onto the drawable and blocking the display pipeline any longer than necessary 218 | MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor; 219 | if (renderPassDescriptor != nil) { 220 | renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0); 221 | 222 | // Create a render command encoder so we can render into something 223 | id renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; 224 | renderEncoder.label = @"minifb_command_encoder"; 225 | 226 | // Set render command encoder state 227 | [renderEncoder setRenderPipelineState:pipeline_state]; 228 | [renderEncoder setVertexBytes:window_data_osx->metal.vertices length:sizeof(window_data_osx->metal.vertices) atIndex:0]; 229 | 230 | [renderEncoder setFragmentTexture:texture_buffers[current_buffer] atIndex:0]; 231 | 232 | // Draw the vertices of our quads 233 | [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; 234 | 235 | // We're done encoding commands 236 | [renderEncoder endEncoding]; 237 | 238 | // Schedule a present once the framebuffer is complete using the current drawable 239 | //if ([commandBuffer respondsToSelector:@selector(presentDrawable:afterMinimumDuration:)]) { 240 | // // MacOS Big Sur is ignoring this 241 | // [commandBuffer presentDrawable:view.currentDrawable afterMinimumDuration:g_time_for_frame]; 242 | //} 243 | //else { 244 | [commandBuffer presentDrawable:view.currentDrawable]; 245 | //} 246 | } 247 | 248 | // Finalize rendering here & push the command buffer to the GPU 249 | [commandBuffer commit]; 250 | } 251 | 252 | //------------------------------------- 253 | - (void) mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size { 254 | (void)view; 255 | (void)size; 256 | // resize 257 | } 258 | 259 | @end 260 | 261 | #endif 262 | -------------------------------------------------------------------------------- /src/macosx/OSXWindow.h: -------------------------------------------------------------------------------- 1 | #import 2 | #include 3 | 4 | @interface OSXWindow : NSWindow 5 | { 6 | NSView *childContentView; 7 | @public SWindowData *window_data; 8 | } 9 | 10 | - (id)initWithContentRect:(NSRect)contentRect 11 | styleMask:(NSWindowStyleMask)windowStyle 12 | backing:(NSBackingStoreType)bufferingType 13 | defer:(BOOL)deferCreation 14 | windowData:(SWindowData *) windowData; 15 | 16 | - (void) removeWindowData; 17 | @end 18 | -------------------------------------------------------------------------------- /src/macosx/OSXWindow.m: -------------------------------------------------------------------------------- 1 | #import "OSXWindow.h" 2 | #import "OSXView.h" 3 | #include "WindowData_OSX.h" 4 | #include 5 | #include 6 | 7 | @implementation OSXWindow 8 | 9 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 10 | 11 | - (id)initWithContentRect:(NSRect)contentRect 12 | styleMask:(NSWindowStyleMask)windowStyle 13 | backing:(NSBackingStoreType)bufferingType 14 | defer:(BOOL)deferCreation 15 | windowData:(SWindowData *) windowData 16 | { 17 | self = [super 18 | initWithContentRect:contentRect 19 | styleMask:windowStyle 20 | backing:bufferingType 21 | defer:deferCreation]; 22 | 23 | if (self) 24 | { 25 | [self setOpaque:YES]; 26 | [self setBackgroundColor:[NSColor clearColor]]; 27 | 28 | self.delegate = self; 29 | 30 | self->window_data = windowData; 31 | OSXView *view = (OSXView *) self->childContentView.superview; 32 | view->window_data = windowData; 33 | } 34 | return self; 35 | } 36 | 37 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 38 | 39 | - (void) removeWindowData { 40 | self->window_data = 0x0; 41 | OSXView *view = (OSXView *) self->childContentView.superview; 42 | view->window_data = 0x0; 43 | } 44 | 45 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 46 | 47 | - (void)dealloc 48 | { 49 | [[NSNotificationCenter defaultCenter] 50 | removeObserver:self]; 51 | [super dealloc]; 52 | } 53 | 54 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 55 | 56 | - (void)setContentSize:(NSSize)newSize 57 | { 58 | NSSize sizeDelta = newSize; 59 | NSSize childBoundsSize = [childContentView bounds].size; 60 | sizeDelta.width -= childBoundsSize.width; 61 | sizeDelta.height -= childBoundsSize.height; 62 | 63 | OSXView *frameView = [super contentView]; 64 | NSSize newFrameSize = [frameView bounds].size; 65 | newFrameSize.width += sizeDelta.width; 66 | newFrameSize.height += sizeDelta.height; 67 | 68 | [super setContentSize:newFrameSize]; 69 | } 70 | 71 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 72 | 73 | - (void)flagsChanged:(NSEvent *)event 74 | { 75 | if(window_data == 0x0) 76 | return; 77 | 78 | const uint32_t flags = [event modifierFlags]; 79 | uint32_t mod_keys = 0, mod_keys_aux = 0; 80 | 81 | //NSEventModifierFlagHelp = 1 << 22, 82 | //NSEventModifierFlagFunction = 1 << 23, 83 | if(flags & NSEventModifierFlagCapsLock) { 84 | mod_keys |= KB_MOD_CAPS_LOCK; 85 | } 86 | if(flags & NSEventModifierFlagShift) { 87 | mod_keys |= KB_MOD_SHIFT; 88 | } 89 | if(flags & NSEventModifierFlagControl) { 90 | mod_keys |= KB_MOD_CONTROL; 91 | } 92 | if(flags & NSEventModifierFlagOption) { 93 | mod_keys |= KB_MOD_ALT; 94 | } 95 | if(flags & NSEventModifierFlagCommand) { 96 | mod_keys |= KB_MOD_SUPER; 97 | } 98 | if(flags & NSEventModifierFlagNumericPad) { 99 | mod_keys |= KB_MOD_NUM_LOCK; 100 | } 101 | 102 | if(mod_keys != window_data->mod_keys) { 103 | short int key_code = g_keycodes[[event keyCode] & 0x1ff]; 104 | if(key_code != KB_KEY_UNKNOWN) { 105 | mod_keys_aux = mod_keys ^ window_data->mod_keys; 106 | if(mod_keys_aux & KB_MOD_CAPS_LOCK) { 107 | window_data->key_status[key_code] = (mod_keys & KB_MOD_CAPS_LOCK) != 0; 108 | kCall(keyboard_func, key_code, mod_keys, window_data->key_status[key_code]); 109 | } 110 | if(mod_keys_aux & KB_MOD_SHIFT) { 111 | window_data->key_status[key_code] = (mod_keys & KB_MOD_SHIFT) != 0; 112 | kCall(keyboard_func, key_code, mod_keys, window_data->key_status[key_code]); 113 | } 114 | if(mod_keys_aux & KB_MOD_CONTROL) { 115 | window_data->key_status[key_code] = (mod_keys & KB_MOD_CONTROL) != 0; 116 | kCall(keyboard_func, key_code, mod_keys, window_data->key_status[key_code]); 117 | } 118 | if(mod_keys_aux & KB_MOD_ALT) { 119 | window_data->key_status[key_code] = (mod_keys & KB_MOD_ALT) != 0; 120 | kCall(keyboard_func, key_code, mod_keys, window_data->key_status[key_code]); 121 | } 122 | if(mod_keys_aux & KB_MOD_SUPER) { 123 | window_data->key_status[key_code] = (mod_keys & KB_MOD_SUPER) != 0; 124 | kCall(keyboard_func, key_code, mod_keys, window_data->key_status[key_code]); 125 | } 126 | if(mod_keys_aux & KB_MOD_NUM_LOCK) { 127 | window_data->key_status[key_code] = (mod_keys & KB_MOD_NUM_LOCK) != 0; 128 | kCall(keyboard_func, key_code, mod_keys, window_data->key_status[key_code]); 129 | } 130 | } 131 | } 132 | window_data->mod_keys = mod_keys; 133 | 134 | [super flagsChanged:event]; 135 | } 136 | 137 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 138 | 139 | - (void)keyDown:(NSEvent *)event 140 | { 141 | if(window_data != 0x0) { 142 | short int key_code = g_keycodes[[event keyCode] & 0x1ff]; 143 | window_data->key_status[key_code] = true; 144 | kCall(keyboard_func, key_code, window_data->mod_keys, true); 145 | } 146 | [childContentView.superview interpretKeyEvents:@[event]]; 147 | } 148 | 149 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 150 | 151 | - (void)keyUp:(NSEvent *)event 152 | { 153 | if(window_data != 0x0) { 154 | short int key_code = g_keycodes[[event keyCode] & 0x1ff]; 155 | window_data->key_status[key_code] = false; 156 | kCall(keyboard_func, key_code, window_data->mod_keys, false); 157 | } 158 | } 159 | 160 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 161 | 162 | - (void)mainWindowChanged:(NSNotification *)notification 163 | { 164 | kUnused(notification); 165 | 166 | if(window_data != 0x0) { 167 | if(window_data->is_active == true) { 168 | window_data->is_active = false; 169 | kCall(active_func, false); 170 | } 171 | } 172 | } 173 | 174 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 175 | 176 | - (void)setContentView:(NSView *)aView 177 | { 178 | if ([childContentView isEqualTo:aView]) { 179 | return; 180 | } 181 | 182 | NSRect bounds = [self frame]; 183 | bounds.origin = NSZeroPoint; 184 | 185 | OSXView *frameView = [super contentView]; 186 | if (!frameView) 187 | { 188 | frameView = [[[OSXView alloc] initWithFrame:bounds] autorelease]; 189 | 190 | [super setContentView:frameView]; 191 | } 192 | 193 | if (childContentView) 194 | { 195 | [childContentView removeFromSuperview]; 196 | } 197 | childContentView = aView; 198 | [childContentView setFrame:[self contentRectForFrameRect:bounds]]; 199 | [childContentView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; 200 | [frameView addSubview:childContentView]; 201 | } 202 | 203 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 204 | 205 | - (NSView *)contentView 206 | { 207 | return childContentView; 208 | } 209 | 210 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 211 | 212 | - (BOOL)canBecomeKeyWindow 213 | { 214 | return YES; 215 | } 216 | 217 | - (void)windowDidBecomeKey:(NSNotification *)notification 218 | { 219 | kUnused(notification); 220 | if(window_data != 0x0) { 221 | window_data->is_active = true; 222 | kCall(active_func, true); 223 | } 224 | } 225 | 226 | - (void)windowDidResignKey:(NSNotification *)notification 227 | { 228 | kUnused(notification); 229 | if(window_data) { 230 | window_data->is_active = false; 231 | kCall(active_func, false); 232 | } 233 | } 234 | 235 | - (BOOL)windowShouldClose:(NSWindow *) window 236 | { 237 | bool destroy = false; 238 | if (!window_data) { 239 | destroy = true; 240 | } else { 241 | // Obtain a confirmation of close 242 | if (!window_data->close_func || window_data->close_func((struct mfb_window*)window_data)) { 243 | destroy = true; 244 | } 245 | } 246 | 247 | return destroy; 248 | } 249 | 250 | - (void)windowWillClose:(NSNotification *)notification { 251 | kUnused(notification); 252 | if(window_data) { 253 | window_data->close = true; 254 | } 255 | } 256 | 257 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 258 | 259 | - (BOOL)canBecomeMainWindow 260 | { 261 | return YES; 262 | } 263 | 264 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 265 | 266 | - (NSRect)contentRectForFrameRect:(NSRect)windowFrame 267 | { 268 | windowFrame.origin = NSZeroPoint; 269 | return NSInsetRect(windowFrame, 0, 0); 270 | } 271 | 272 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 273 | 274 | + (NSRect)frameRectForContentRect:(NSRect)windowContentRect styleMask:(NSWindowStyleMask)windowStyle 275 | { 276 | kUnused(windowStyle); 277 | return NSInsetRect(windowContentRect, 0, 0); 278 | } 279 | 280 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 281 | 282 | - (void)willClose 283 | { 284 | if(window_data != 0x0) { 285 | window_data->close = true; 286 | } 287 | } 288 | 289 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 290 | 291 | - (void)windowDidResize:(NSNotification *)notification { 292 | kUnused(notification); 293 | if(window_data != 0x0) { 294 | CGSize size = [self contentRectForFrameRect:[self frame]].size; 295 | 296 | window_data->window_width = size.width; 297 | window_data->window_height = size.height; 298 | resize_dst(window_data, size.width, size.height); 299 | 300 | kCall(resize_func, size.width, size.height); 301 | } 302 | } 303 | 304 | @end 305 | -------------------------------------------------------------------------------- /src/macosx/WindowData_OSX.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #if defined(USE_METAL_API) 7 | #include 8 | #endif 9 | 10 | @class OSXWindow; 11 | @class OSXViewDelegate; 12 | 13 | typedef struct Vertex { 14 | float x, y, z, w; 15 | } Vertex; 16 | 17 | typedef struct { 18 | OSXWindow *window; 19 | OSXViewDelegate *viewController; 20 | struct mfb_timer *timer; 21 | 22 | #if defined(USE_METAL_API) 23 | struct { 24 | Vertex vertices[4]; 25 | } metal; 26 | #endif 27 | } SWindowData_OSX; 28 | -------------------------------------------------------------------------------- /src/wayland/WindowData_Way.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct wl_display; 7 | struct wl_registry; 8 | struct wl_compositor; 9 | struct wl_shell; 10 | struct wl_seat; 11 | struct wl_keyboard; 12 | struct wl_pointer; 13 | struct wl_callback; 14 | struct wl_shm; 15 | struct wl_shm_pool; 16 | struct wl_surface; 17 | struct wl_shell_surface; 18 | struct wl_buffer; 19 | 20 | typedef struct 21 | { 22 | struct wl_display *display; 23 | struct wl_registry *registry; 24 | struct wl_compositor *compositor; 25 | struct xdg_wm_base *shell; 26 | struct wl_seat *seat; 27 | struct wl_keyboard *keyboard; 28 | 29 | struct wl_pointer *pointer; 30 | struct wl_cursor_theme *cursor_theme; 31 | struct wl_cursor *default_cursor; 32 | struct wl_surface *cursor_surface; 33 | 34 | struct wl_shm *shm; 35 | struct wl_shm_pool *shm_pool; 36 | struct wl_surface *surface; 37 | struct xdg_surface *shell_surface; 38 | struct xdg_toplevel *toplevel; 39 | 40 | uint32_t seat_version; 41 | uint32_t shm_format; 42 | uint32_t *shm_ptr; 43 | 44 | int fd; 45 | 46 | struct mfb_timer *timer; 47 | } SWindowData_Way; 48 | -------------------------------------------------------------------------------- /src/wayland/generated/xdg-shell-protocol.c: -------------------------------------------------------------------------------- 1 | /* Generated by wayland-scanner 1.22.0 */ 2 | 3 | /* 4 | * Copyright © 2008-2013 Kristian Høgsberg 5 | * Copyright © 2013 Rafael Antognolli 6 | * Copyright © 2013 Jasper St. Pierre 7 | * Copyright © 2010-2013 Intel Corporation 8 | * Copyright © 2015-2017 Samsung Electronics Co., Ltd 9 | * Copyright © 2015-2017 Red Hat Inc. 10 | * 11 | * Permission is hereby granted, free of charge, to any person obtaining a 12 | * copy of this software and associated documentation files (the "Software"), 13 | * to deal in the Software without restriction, including without limitation 14 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 15 | * and/or sell copies of the Software, and to permit persons to whom the 16 | * Software is furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice (including the next 19 | * paragraph) shall be included in all copies or substantial portions of the 20 | * Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 25 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 | * DEALINGS IN THE SOFTWARE. 29 | */ 30 | 31 | #include 32 | #include 33 | #include "wayland-util.h" 34 | 35 | extern const struct wl_interface wl_output_interface; 36 | extern const struct wl_interface wl_seat_interface; 37 | extern const struct wl_interface wl_surface_interface; 38 | extern const struct wl_interface xdg_popup_interface; 39 | extern const struct wl_interface xdg_positioner_interface; 40 | extern const struct wl_interface xdg_surface_interface; 41 | extern const struct wl_interface xdg_toplevel_interface; 42 | 43 | static const struct wl_interface *xdg_shell_types[] = { 44 | NULL, 45 | NULL, 46 | NULL, 47 | NULL, 48 | &xdg_positioner_interface, 49 | &xdg_surface_interface, 50 | &wl_surface_interface, 51 | &xdg_toplevel_interface, 52 | &xdg_popup_interface, 53 | &xdg_surface_interface, 54 | &xdg_positioner_interface, 55 | &xdg_toplevel_interface, 56 | &wl_seat_interface, 57 | NULL, 58 | NULL, 59 | NULL, 60 | &wl_seat_interface, 61 | NULL, 62 | &wl_seat_interface, 63 | NULL, 64 | NULL, 65 | &wl_output_interface, 66 | &wl_seat_interface, 67 | NULL, 68 | &xdg_positioner_interface, 69 | NULL, 70 | }; 71 | 72 | static const struct wl_message xdg_wm_base_requests[] = { 73 | { "destroy", "", xdg_shell_types + 0 }, 74 | { "create_positioner", "n", xdg_shell_types + 4 }, 75 | { "get_xdg_surface", "no", xdg_shell_types + 5 }, 76 | { "pong", "u", xdg_shell_types + 0 }, 77 | }; 78 | 79 | static const struct wl_message xdg_wm_base_events[] = { 80 | { "ping", "u", xdg_shell_types + 0 }, 81 | }; 82 | 83 | WL_EXPORT const struct wl_interface xdg_wm_base_interface = { 84 | "xdg_wm_base", 6, 85 | 4, xdg_wm_base_requests, 86 | 1, xdg_wm_base_events, 87 | }; 88 | 89 | static const struct wl_message xdg_positioner_requests[] = { 90 | { "destroy", "", xdg_shell_types + 0 }, 91 | { "set_size", "ii", xdg_shell_types + 0 }, 92 | { "set_anchor_rect", "iiii", xdg_shell_types + 0 }, 93 | { "set_anchor", "u", xdg_shell_types + 0 }, 94 | { "set_gravity", "u", xdg_shell_types + 0 }, 95 | { "set_constraint_adjustment", "u", xdg_shell_types + 0 }, 96 | { "set_offset", "ii", xdg_shell_types + 0 }, 97 | { "set_reactive", "3", xdg_shell_types + 0 }, 98 | { "set_parent_size", "3ii", xdg_shell_types + 0 }, 99 | { "set_parent_configure", "3u", xdg_shell_types + 0 }, 100 | }; 101 | 102 | WL_EXPORT const struct wl_interface xdg_positioner_interface = { 103 | "xdg_positioner", 6, 104 | 10, xdg_positioner_requests, 105 | 0, NULL, 106 | }; 107 | 108 | static const struct wl_message xdg_surface_requests[] = { 109 | { "destroy", "", xdg_shell_types + 0 }, 110 | { "get_toplevel", "n", xdg_shell_types + 7 }, 111 | { "get_popup", "n?oo", xdg_shell_types + 8 }, 112 | { "set_window_geometry", "iiii", xdg_shell_types + 0 }, 113 | { "ack_configure", "u", xdg_shell_types + 0 }, 114 | }; 115 | 116 | static const struct wl_message xdg_surface_events[] = { 117 | { "configure", "u", xdg_shell_types + 0 }, 118 | }; 119 | 120 | WL_EXPORT const struct wl_interface xdg_surface_interface = { 121 | "xdg_surface", 6, 122 | 5, xdg_surface_requests, 123 | 1, xdg_surface_events, 124 | }; 125 | 126 | static const struct wl_message xdg_toplevel_requests[] = { 127 | { "destroy", "", xdg_shell_types + 0 }, 128 | { "set_parent", "?o", xdg_shell_types + 11 }, 129 | { "set_title", "s", xdg_shell_types + 0 }, 130 | { "set_app_id", "s", xdg_shell_types + 0 }, 131 | { "show_window_menu", "ouii", xdg_shell_types + 12 }, 132 | { "move", "ou", xdg_shell_types + 16 }, 133 | { "resize", "ouu", xdg_shell_types + 18 }, 134 | { "set_max_size", "ii", xdg_shell_types + 0 }, 135 | { "set_min_size", "ii", xdg_shell_types + 0 }, 136 | { "set_maximized", "", xdg_shell_types + 0 }, 137 | { "unset_maximized", "", xdg_shell_types + 0 }, 138 | { "set_fullscreen", "?o", xdg_shell_types + 21 }, 139 | { "unset_fullscreen", "", xdg_shell_types + 0 }, 140 | { "set_minimized", "", xdg_shell_types + 0 }, 141 | }; 142 | 143 | static const struct wl_message xdg_toplevel_events[] = { 144 | { "configure", "iia", xdg_shell_types + 0 }, 145 | { "close", "", xdg_shell_types + 0 }, 146 | { "configure_bounds", "4ii", xdg_shell_types + 0 }, 147 | { "wm_capabilities", "5a", xdg_shell_types + 0 }, 148 | }; 149 | 150 | WL_EXPORT const struct wl_interface xdg_toplevel_interface = { 151 | "xdg_toplevel", 6, 152 | 14, xdg_toplevel_requests, 153 | 4, xdg_toplevel_events, 154 | }; 155 | 156 | static const struct wl_message xdg_popup_requests[] = { 157 | { "destroy", "", xdg_shell_types + 0 }, 158 | { "grab", "ou", xdg_shell_types + 22 }, 159 | { "reposition", "3ou", xdg_shell_types + 24 }, 160 | }; 161 | 162 | static const struct wl_message xdg_popup_events[] = { 163 | { "configure", "iiii", xdg_shell_types + 0 }, 164 | { "popup_done", "", xdg_shell_types + 0 }, 165 | { "repositioned", "3u", xdg_shell_types + 0 }, 166 | }; 167 | 168 | WL_EXPORT const struct wl_interface xdg_popup_interface = { 169 | "xdg_popup", 6, 170 | 3, xdg_popup_requests, 171 | 3, xdg_popup_events, 172 | }; 173 | 174 | -------------------------------------------------------------------------------- /src/windows/WindowData_Win.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | //#define WIN32_LEAN_AND_MEAN 5 | #include 6 | 7 | typedef struct { 8 | HWND window; 9 | WNDCLASS wc; 10 | HDC hdc; 11 | #if defined(USE_OPENGL_API) 12 | HGLRC hGLRC; 13 | uint32_t text_id; 14 | #else 15 | BITMAPINFO *bitmapInfo; 16 | #endif 17 | struct mfb_timer *timer; 18 | bool mouse_inside; 19 | } SWindowData_Win; 20 | -------------------------------------------------------------------------------- /src/x11/WindowData_X11.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #if defined(USE_OPENGL_API) 7 | #include 8 | #endif 9 | 10 | typedef struct { 11 | Window window; 12 | 13 | Display *display; 14 | int screen; 15 | GC gc; 16 | #if defined(USE_OPENGL_API) 17 | GLXContext context; 18 | uint32_t text_id; 19 | #else 20 | XImage *image; 21 | void *image_buffer; 22 | XImage *image_scaler; 23 | uint32_t image_scaler_width; 24 | uint32_t image_scaler_height; 25 | #endif 26 | 27 | struct mfb_timer *timer; 28 | } SWindowData_X11; 29 | -------------------------------------------------------------------------------- /tests/android/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /tests/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 31 5 | ndkVersion '22.1.7171670' 6 | 7 | defaultConfig { 8 | applicationId 'com.example.noise' 9 | minSdkVersion 14 10 | targetSdkVersion 31 11 | } 12 | 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | 20 | externalNativeBuild { 21 | cmake { 22 | version '3.22.1' 23 | path 'src/main/cpp/CMakeLists.txt' 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 13 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /tests/android/app/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.18.1) 2 | project(noise) 3 | 4 | # Set our flags 5 | #-------------------------------------- 6 | #add_compile_options("$<$:-g>") 7 | #add_compile_options("$,-O0,-O2>") 8 | #if(APPLE) 9 | # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -pedantic -Wno-switch -Wno-unused-function -Wno-implicit-fallthrough") 10 | #else() 11 | # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -pedantic -Wno-switch -Wno-unused-function -Wno-implicit-fallthrough -Wno-cast-function-type") 12 | #endif() 13 | #set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}") 14 | #set(CMAKE_OBJC_FLAGS "${CMAKE_C_FLAGS}") 15 | #set(CMAKE_OBJCXX_FLAGS "${CMAKE_CXX_FLAGS}") 16 | 17 | # build native_app_glue as a static lib 18 | add_library(native_app_glue STATIC 19 | ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c 20 | ) 21 | 22 | add_library(minifb STATIC 23 | ${CMAKE_CURRENT_LIST_DIR}/../../../../../../include/MiniFB.h 24 | ${CMAKE_CURRENT_LIST_DIR}/../../../../../../include/MiniFB_cpp.h 25 | ${CMAKE_CURRENT_LIST_DIR}/../../../../../../include/MiniFB_enums.h 26 | 27 | ${CMAKE_CURRENT_LIST_DIR}/../../../../../../src/MiniFB_common.c 28 | ${CMAKE_CURRENT_LIST_DIR}/../../../../../../src/MiniFB_cpp.cpp 29 | ${CMAKE_CURRENT_LIST_DIR}/../../../../../../src/MiniFB_internal.c 30 | ${CMAKE_CURRENT_LIST_DIR}/../../../../../../src/MiniFB_internal.h 31 | ${CMAKE_CURRENT_LIST_DIR}/../../../../../../src/MiniFB_timer.c 32 | ${CMAKE_CURRENT_LIST_DIR}/../../../../../../src/MiniFB_linux.c 33 | ${CMAKE_CURRENT_LIST_DIR}/../../../../../../src/WindowData.h 34 | 35 | ${CMAKE_CURRENT_LIST_DIR}/../../../../../../src/android/AndroidMiniFB.c 36 | ${CMAKE_CURRENT_LIST_DIR}/../../../../../../src/android/WindowData_Android.h 37 | ) 38 | 39 | target_include_directories(minifb PRIVATE 40 | ${ANDROID_NDK}/sources/android/native_app_glue 41 | ${CMAKE_CURRENT_LIST_DIR}/../../../../../../include 42 | ${CMAKE_CURRENT_LIST_DIR}/../../../../../../src 43 | ) 44 | 45 | # now build app's shared lib 46 | add_library(noise SHARED 47 | noise.c 48 | ) 49 | 50 | target_include_directories(noise PRIVATE 51 | ${ANDROID_NDK}/sources/android/native_app_glue 52 | ${CMAKE_CURRENT_LIST_DIR}/../../../../../../include 53 | ) 54 | 55 | # Export ANativeActivity_onCreate(), 56 | # Refer to: https://github.com/android-ndk/ndk/issues/381. 57 | set(CMAKE_SHARED_LINKER_FLAGS 58 | "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate" 59 | ) 60 | 61 | # add lib dependencies 62 | target_link_libraries(noise 63 | android 64 | minifb 65 | native_app_glue 66 | log 67 | m 68 | ) 69 | -------------------------------------------------------------------------------- /tests/android/app/src/main/cpp/noise.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define LOG_TAG "Noise" 8 | #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) 9 | #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) 10 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) 11 | #define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) 12 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 13 | #define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__) 14 | 15 | #define kUnused(var) (void) var; 16 | 17 | #define kTouchIdMask 0xf0000000 18 | #define kTouchPosMask 0x0fffffff 19 | #define kTouchIdShift 28 20 | 21 | #define MIN(a,b) (((a) < (b)) ? (a) : (b)) 22 | #define MAX(a,b) (((a) > (b)) ? (a) : (b)) 23 | 24 | typedef struct { 25 | bool enabled; 26 | int x, y; 27 | } Pos; 28 | 29 | static uint32_t g_width = 200; 30 | static uint32_t g_height = 100; 31 | static uint32_t *g_buffer = 0x0; 32 | static Pos g_positions[16] = {}; 33 | 34 | void 35 | active(struct mfb_window *window, bool isActive) { 36 | LOGI("active: %d", isActive); 37 | } 38 | 39 | void 40 | resize(struct mfb_window *window, int width, int height) { 41 | LOGI("resize: %d, %d", width, height); 42 | g_width = (width >> 1); 43 | g_height = (height >> 1); 44 | g_buffer = realloc(g_buffer, g_width * g_height * 4); 45 | } 46 | 47 | void 48 | keyboard(struct mfb_window *window, mfb_key key, mfb_key_mod mod, bool isPressed) { 49 | LOGI("keyboard:"); 50 | } 51 | 52 | void 53 | char_input(struct mfb_window *window, unsigned int charCode) { 54 | LOGI("char_input:"); 55 | } 56 | 57 | void 58 | mouse_btn(struct mfb_window *window, mfb_mouse_button button, mfb_key_mod mod, bool isPressed) { 59 | int x = (mfb_get_mouse_x(window) & kTouchPosMask) >> 1; 60 | int y = (mfb_get_mouse_y(window) & kTouchPosMask) >> 1; 61 | g_positions[button].enabled = isPressed; 62 | g_positions[button].x = x; 63 | g_positions[button].y = y; 64 | LOGI("mouse_btn: button: id %d=%d, x=%d, y=%d", (int)button, (int) isPressed, x, y); 65 | } 66 | 67 | void 68 | mouse_move(struct mfb_window *window, int x, int y) { 69 | int id = (x & kTouchIdMask) >> kTouchIdShift; 70 | x = (x & kTouchPosMask) >> 1; 71 | y = (y & kTouchPosMask) >> 1; 72 | g_positions[id].enabled = true; 73 | g_positions[id].x = x; 74 | g_positions[id].y = y; 75 | LOGI("mouse_move: %d, %d [%d]", x, y, id); 76 | } 77 | 78 | void 79 | mouse_scroll(struct mfb_window *window, mfb_key_mod mod, float deltaX, float deltaY) { 80 | LOGI("mouse_scroll:"); 81 | } 82 | 83 | // I'm not sure that we can use this function name but it works 84 | //------------------------------------- 85 | int 86 | main(int argc, char *argv[]) { 87 | kUnused(argc); 88 | kUnused(argv); 89 | uint32_t i, noise, carry, seed = 0xbeef; 90 | 91 | struct mfb_window *window = mfb_open("Ignored", g_width, g_height); 92 | if(window == 0x0) 93 | return 0; 94 | 95 | mfb_set_active_callback(window, active); 96 | mfb_set_resize_callback(window, resize); 97 | mfb_set_keyboard_callback(window, keyboard); // not working 98 | mfb_set_char_input_callback(window, char_input); // not working 99 | mfb_set_mouse_button_callback(window, mouse_btn); 100 | mfb_set_mouse_move_callback(window, mouse_move); 101 | mfb_set_mouse_scroll_callback(window, mouse_scroll); // not working 102 | 103 | g_buffer = (uint32_t *) malloc(g_width * g_height * 4); 104 | 105 | mfb_update_state state; 106 | do { 107 | bool isActive = mfb_is_window_active(window); 108 | unsigned width = mfb_get_window_width(window); 109 | unsigned height = mfb_get_window_height(window); 110 | int mouseX = mfb_get_mouse_x(window); 111 | int mouseY = mfb_get_mouse_y(window); 112 | float scrollX = mfb_get_mouse_scroll_x(window); // not working 113 | float scrollY = mfb_get_mouse_scroll_y(window); // not working 114 | const uint8_t *buttons = mfb_get_mouse_button_buffer(window); 115 | const uint8_t *keys = mfb_get_key_buffer(window); // not working 116 | 117 | for (i = 0; i < g_width * g_height; ++i) { 118 | noise = seed; 119 | noise >>= 3; 120 | noise ^= seed; 121 | carry = noise & 1; 122 | noise >>= 1; 123 | seed >>= 1; 124 | seed |= (carry << 30); 125 | noise &= 0xFF; 126 | 127 | // Comment out to test appropriate colour channel 128 | //g_buffer[i] = MFB_RGB(noise, 0, 0); // Test red channel 129 | //g_buffer[i] = MFB_RGB(0, noise, 0); // Test green channel 130 | //g_buffer[i] = MFB_RGB(0, 0, noise); // Test blue channel 131 | g_buffer[i] = MFB_RGB(noise, noise, noise); 132 | } 133 | 134 | for (int p = 0; p < 16; ++p) { 135 | if (g_positions[p].enabled) { 136 | int minX = MAX(g_positions[p].x - 16, 0); 137 | int maxX = MIN(g_positions[p].x + 16, g_width); 138 | int minY = MAX(g_positions[p].y - 16, 0); 139 | int maxY = MIN(g_positions[p].y + 16, g_height); 140 | for(int y=minY; y 2 | 3 | Noise 4 | 5 | -------------------------------------------------------------------------------- /tests/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:7.0.4' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | mavenCentral() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emoon/minifb/1e31ceb2cfe5ad397be288ca3cb4af017250c9ce/tests/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /tests/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Dec 18 16:12:25 CET 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /tests/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=`expr $i + 1` 158 | done 159 | case $i in 160 | 0) set -- ;; 161 | 1) set -- "$args0" ;; 162 | 2) set -- "$args0" "$args1" ;; 163 | 3) set -- "$args0" "$args1" "$args2" ;; 164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=`save "$@"` 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | exec "$JAVACMD" "$@" 184 | -------------------------------------------------------------------------------- /tests/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto init 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto init 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :init 65 | @rem Get command-line arguments, handling Windows variants 66 | 67 | if not "%OS%" == "Windows_NT" goto win9xME_args 68 | 69 | :win9xME_args 70 | @rem Slurp the command line arguments. 71 | set CMD_LINE_ARGS= 72 | set _SKIP=2 73 | 74 | :win9xME_args_slurp 75 | if "x%~1" == "x" goto execute 76 | 77 | set CMD_LINE_ARGS=%* 78 | 79 | :execute 80 | @rem Setup the command line 81 | 82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 83 | 84 | @rem Execute Gradle 85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 86 | 87 | :end 88 | @rem End local scope for the variables with windows NT shell 89 | if "%ERRORLEVEL%"=="0" goto mainEnd 90 | 91 | :fail 92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 93 | rem the _cmd.exe /c_ return code! 94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 95 | exit /b 1 96 | 97 | :mainEnd 98 | if "%OS%"=="Windows_NT" endlocal 99 | 100 | :omega 101 | -------------------------------------------------------------------------------- /tests/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /tests/fullscreen.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define kUnused(var) (void) var 6 | 7 | #define WIDTH 960 8 | #define HEIGHT 640 9 | static unsigned int g_buffer[WIDTH * HEIGHT]; 10 | static bool g_active = true; 11 | 12 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 13 | 14 | 15 | int 16 | main() 17 | { 18 | int noise, carry, seed = 0xbeef; 19 | 20 | struct mfb_window *window = mfb_open_ex("full screen auto", WIDTH, HEIGHT, WF_FULLSCREEN); 21 | if (!window) 22 | return 0; 23 | 24 | mfb_set_viewport_best_fit(window, WIDTH, HEIGHT); 25 | 26 | do { 27 | int i; 28 | mfb_update_state state; 29 | 30 | if(g_active) 31 | { 32 | for (i = 0; i < WIDTH * HEIGHT; ++i) 33 | { 34 | noise = seed; 35 | noise >>= 3; 36 | noise ^= seed; 37 | carry = noise & 1; 38 | noise >>= 1; 39 | seed >>= 1; 40 | seed |= (carry << 30); 41 | noise &= 0xFF; 42 | g_buffer[i] = MFB_RGB(noise, noise, noise); 43 | } 44 | 45 | state = mfb_update(window, g_buffer); 46 | } 47 | else { 48 | state = mfb_update_events(window); 49 | } 50 | if (state != STATE_OK) { 51 | window = 0x0; 52 | break; 53 | } 54 | } while(mfb_wait_sync(window)); 55 | 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /tests/hidpi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define DIMEN_LOW 512 7 | static unsigned int g_buffer_low[DIMEN_LOW * DIMEN_LOW]; 8 | 9 | #define DIMEN_HIGH (2*DIMEN_LOW) 10 | static unsigned int g_buffer_high[DIMEN_HIGH * DIMEN_HIGH]; 11 | 12 | void 13 | pretty_square(unsigned int *p, int dimen) { 14 | memset(p, 127, dimen * dimen * 4); 15 | const int one_half_dimen = dimen / 2; 16 | const int one_quarter_dimen = one_half_dimen / 2; 17 | const int three_quarter_dimen = one_half_dimen + one_quarter_dimen; 18 | for (int x = one_quarter_dimen; x < three_quarter_dimen; x++) 19 | for (int y = one_quarter_dimen; y < three_quarter_dimen; y++) 20 | p[y * dimen + x] = (x & 1) ? MFB_ARGB(0xff, 223, 0, (255 * (x - one_quarter_dimen)) / one_half_dimen) : MFB_ARGB(0xff, 0, 0, 0); 21 | } 22 | 23 | int 24 | main() { 25 | pretty_square(g_buffer_low, DIMEN_LOW); 26 | pretty_square(g_buffer_high, DIMEN_HIGH); 27 | 28 | struct mfb_window *window_low = mfb_open("LowRes", DIMEN_LOW, DIMEN_LOW); 29 | struct mfb_window *window_high = mfb_open("HighRes", DIMEN_HIGH / 2, DIMEN_HIGH / 2); 30 | 31 | while (window_high || window_low) { 32 | if (window_low) 33 | if (mfb_update_ex(window_low, g_buffer_low, DIMEN_LOW, DIMEN_LOW) != STATE_OK) 34 | window_low = NULL; 35 | 36 | if (window_high) 37 | if (mfb_update_ex(window_high, g_buffer_high, DIMEN_HIGH, DIMEN_HIGH) != STATE_OK) 38 | window_high = NULL; 39 | 40 | if (window_high) mfb_wait_sync(window_high); 41 | else if(window_low) mfb_wait_sync(window_low); 42 | } 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /tests/input_events.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define kUnused(var) (void) var 6 | 7 | #define WIDTH 800 8 | #define HEIGHT 600 9 | static unsigned int g_buffer[WIDTH * HEIGHT]; 10 | static bool g_active = true; 11 | 12 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 13 | 14 | static void 15 | active(struct mfb_window *window, bool isActive) { 16 | const char *window_title = ""; 17 | if(window) { 18 | window_title = (const char *) mfb_get_user_data(window); 19 | } 20 | fprintf(stdout, "%s > active: %d\n", window_title, isActive); 21 | g_active = isActive; 22 | } 23 | 24 | static void 25 | resize(struct mfb_window *window, int width, int height) { 26 | uint32_t x = 0; 27 | uint32_t y = 0; 28 | const char *window_title = ""; 29 | if(window) { 30 | window_title = (const char *) mfb_get_user_data(window); 31 | } 32 | 33 | fprintf(stdout, "%s > resize: %d, %d\n", window_title, width, height); 34 | if(width > WIDTH) { 35 | x = (width - WIDTH) >> 1; 36 | width = WIDTH; 37 | } 38 | if(height > HEIGHT) { 39 | y = (height - HEIGHT) >> 1; 40 | height = HEIGHT; 41 | } 42 | mfb_set_viewport(window, x, y, width, height); 43 | } 44 | 45 | static bool 46 | close(struct mfb_window *window) { 47 | const char* window_title = ""; 48 | if (window) { 49 | window_title = (const char*)mfb_get_user_data(window); 50 | } 51 | fprintf(stdout, "%s > close\n", window_title); 52 | return true; // true => confirm close 53 | // false => don't close 54 | } 55 | 56 | static void 57 | keyboard(struct mfb_window *window, mfb_key key, mfb_key_mod mod, bool isPressed) { 58 | const char *window_title = ""; 59 | if(window) { 60 | window_title = (const char *) mfb_get_user_data(window); 61 | } 62 | fprintf(stdout, "%s > keyboard: key: %s (pressed: %d) [key_mod: %x]\n", window_title, mfb_get_key_name(key), isPressed, mod); 63 | if(key == KB_KEY_ESCAPE) { 64 | mfb_close(window); 65 | } 66 | } 67 | 68 | static void 69 | char_input(struct mfb_window *window, unsigned int charCode) { 70 | const char *window_title = ""; 71 | if(window) { 72 | window_title = (const char *) mfb_get_user_data(window); 73 | } 74 | fprintf(stdout, "%s > charCode: %d\n", window_title, charCode); 75 | } 76 | 77 | static void 78 | mouse_btn(struct mfb_window *window, mfb_mouse_button button, mfb_key_mod mod, bool isPressed) { 79 | const char *window_title = ""; 80 | int x, y; 81 | if(window) { 82 | window_title = (const char *) mfb_get_user_data(window); 83 | } 84 | x = mfb_get_mouse_x(window); 85 | y = mfb_get_mouse_y(window); 86 | fprintf(stdout, "%s > mouse_btn: button: %d (pressed: %d) (at: %d, %d) [key_mod: %x]\n", window_title, button, isPressed, x, y, mod); 87 | } 88 | 89 | static void 90 | mouse_move(struct mfb_window *window, int x, int y) { 91 | kUnused(window); 92 | kUnused(x); 93 | kUnused(y); 94 | const char *window_title = ""; 95 | if(window) { 96 | window_title = mfb_get_user_data(window); 97 | } 98 | fprintf(stdout, "%s > mouse_move: %d, %d\n", window_title, x, y); 99 | } 100 | 101 | static void 102 | mouse_scroll(struct mfb_window *window, mfb_key_mod mod, float deltaX, float deltaY) { 103 | const char *window_title = ""; 104 | if(window) { 105 | window_title = (const char *) mfb_get_user_data(window); 106 | } 107 | fprintf(stdout, "%s > mouse_scroll: x: %f, y: %f [key_mod: %x]\n", window_title, deltaX, deltaY, mod); 108 | } 109 | 110 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 111 | 112 | int 113 | main() 114 | { 115 | int noise, carry, seed = 0xbeef; 116 | 117 | struct mfb_window *window = mfb_open_ex("Input Events Test", WIDTH, HEIGHT, WF_RESIZABLE); 118 | if (!window) 119 | return 0; 120 | 121 | mfb_set_active_callback(window, active); 122 | mfb_set_resize_callback(window, resize); 123 | mfb_set_close_callback(window, close); 124 | mfb_set_keyboard_callback(window, keyboard); 125 | mfb_set_char_input_callback(window, char_input); 126 | mfb_set_mouse_button_callback(window, mouse_btn); 127 | mfb_set_mouse_move_callback(window, mouse_move); 128 | mfb_set_mouse_scroll_callback(window, mouse_scroll); 129 | 130 | mfb_set_user_data(window, (void *) "Input Events Test"); 131 | 132 | do { 133 | int i; 134 | mfb_update_state state; 135 | 136 | if(g_active) 137 | { 138 | for (i = 0; i < WIDTH * HEIGHT; ++i) 139 | { 140 | noise = seed; 141 | noise >>= 3; 142 | noise ^= seed; 143 | carry = noise & 1; 144 | noise >>= 1; 145 | seed >>= 1; 146 | seed |= (carry << 30); 147 | noise &= 0xFF; 148 | g_buffer[i] = MFB_ARGB(0xff, noise, noise, noise); 149 | } 150 | 151 | state = mfb_update(window, g_buffer); 152 | } 153 | else { 154 | state = mfb_update_events(window); 155 | } 156 | if (state != STATE_OK) { 157 | window = 0x0; 158 | break; 159 | } 160 | } while(mfb_wait_sync(window)); 161 | 162 | return 0; 163 | } 164 | -------------------------------------------------------------------------------- /tests/input_events_cpp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define kUnused(var) (void) var; 6 | 7 | #define WIDTH 800 8 | #define HEIGHT 600 9 | static unsigned int g_buffer[WIDTH * HEIGHT]; 10 | 11 | //#define kUseOldFunctions 12 | //#define kUseLambdas 13 | 14 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 15 | 16 | class Events { 17 | public: 18 | void active(struct mfb_window *window, bool isActive) { 19 | const char *window_title = ""; 20 | if(window) { 21 | window_title = (const char *) mfb_get_user_data(window); 22 | } 23 | fprintf(stdout, "%s > active: %d\n", window_title, isActive); 24 | } 25 | 26 | void resize(struct mfb_window *window, int width, int height) { 27 | uint32_t x = 0; 28 | uint32_t y = 0; 29 | const char *window_title = ""; 30 | if(window) { 31 | window_title = (const char *) mfb_get_user_data(window); 32 | } 33 | 34 | fprintf(stdout, "%s > resize: %d, %d\n", window_title, width, height); 35 | if(width > WIDTH) { 36 | x = (width - WIDTH) >> 1; 37 | width = WIDTH; 38 | } 39 | if(height > HEIGHT) { 40 | y = (height - HEIGHT) >> 1; 41 | height = HEIGHT; 42 | } 43 | mfb_set_viewport(window, x, y, width, height); 44 | } 45 | 46 | bool close(struct mfb_window *window) { 47 | const char* window_title = ""; 48 | if (window) { 49 | window_title = (const char*) mfb_get_user_data(window); 50 | } 51 | fprintf(stdout, "%s > close\n", window_title); 52 | return true; // true => confirm close 53 | // false => don't close 54 | } 55 | 56 | void keyboard(struct mfb_window *window, mfb_key key, mfb_key_mod mod, bool isPressed) { 57 | const char *window_title = ""; 58 | if(window) { 59 | window_title = (const char *) mfb_get_user_data(window); 60 | } 61 | fprintf(stdout, "%s > keyboard: key: %s (pressed: %d) [key_mod: %x]\n", window_title, mfb_get_key_name(key), isPressed, mod); 62 | if(key == KB_KEY_ESCAPE) { 63 | mfb_close(window); 64 | } 65 | } 66 | 67 | void char_input(struct mfb_window *window, unsigned int charCode) { 68 | const char *window_title = ""; 69 | if(window) { 70 | window_title = (const char *) mfb_get_user_data(window); 71 | } 72 | fprintf(stdout, "%s > charCode: %d\n", window_title, charCode); 73 | } 74 | 75 | void mouse_button(struct mfb_window *window, mfb_mouse_button button, mfb_key_mod mod, bool isPressed) { 76 | const char *window_title = ""; 77 | int x, y; 78 | 79 | if(window) { 80 | window_title = (const char *) mfb_get_user_data(window); 81 | } 82 | x = mfb_get_mouse_x(window); 83 | y = mfb_get_mouse_y(window); 84 | fprintf(stdout, "%s > mouse_button: button: %d (pressed: %d) (at: %d, %d) [key_mod: %x]\n", window_title, button, isPressed, x, y, mod); 85 | } 86 | 87 | void mouse_move(struct mfb_window *window, int x, int y) { 88 | kUnused(window); 89 | kUnused(x); 90 | kUnused(y); 91 | //const char *window_title = ""; 92 | //if(window) { 93 | // window_title = (const char *) mfb_get_user_data(window); 94 | //} 95 | //fprintf(stdout, "%s > mouse_move: %d, %d\n", window_title, x, y); 96 | } 97 | 98 | void mouse_scroll(struct mfb_window *window, mfb_key_mod mod, float deltaX, float deltaY) { 99 | const char *window_title = ""; 100 | if(window) { 101 | window_title = (const char *) mfb_get_user_data(window); 102 | } 103 | fprintf(stdout, "%s > mouse_scroll: x: %f, y: %f [key_mod: %x]\n", window_title, deltaX, deltaY, mod); 104 | } 105 | }; 106 | 107 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 108 | 109 | int 110 | main() 111 | { 112 | int noise, carry, seed = 0xbeef; 113 | 114 | struct mfb_window *window = mfb_open_ex("Input Events CPP Test", WIDTH, HEIGHT, WF_RESIZABLE); 115 | if (!window) 116 | return 0; 117 | 118 | Events e; 119 | 120 | #if defined(kUseOldFunctions) 121 | 122 | mfb_set_active_callback(window, &e, &Events::active); 123 | mfb_set_resize_callback(window, &e, &Events::resize); 124 | mfb_set_close_callback(window, &e, &Events::close); 125 | mfb_set_keyboard_callback(window, &e, &Events::keyboard); 126 | mfb_set_char_input_callback(window, &e, &Events::char_input); 127 | mfb_set_mouse_button_callback(window, &e, &Events::mouse_button); 128 | mfb_set_mouse_move_callback(window, &e, &Events::mouse_move); 129 | mfb_set_mouse_scroll_callback(window, &e, &Events::mouse_scroll); 130 | 131 | #elif defined(kUseLambdas) 132 | 133 | mfb_set_active_callback([](struct mfb_window *window, bool isActive) { 134 | const char *window_title = ""; 135 | if(window) { 136 | window_title = (const char *) mfb_get_user_data(window); 137 | } 138 | fprintf(stdout, "%s > active: %d (lambda)\n", window_title, isActive); 139 | }, window); 140 | 141 | mfb_set_resize_callback([](struct mfb_window *window, int width, int height) { 142 | uint32_t x = 0; 143 | uint32_t y = 0; 144 | const char *window_title = ""; 145 | if(window) { 146 | window_title = (const char *) mfb_get_user_data(window); 147 | } 148 | 149 | fprintf(stdout, "%s > resize: %d, %d\n", window_title, width, height); 150 | if(width > WIDTH) { 151 | x = (width - WIDTH) >> 1; 152 | width = WIDTH; 153 | } 154 | if(height > HEIGHT) { 155 | y = (height - HEIGHT) >> 1; 156 | height = HEIGHT; 157 | } 158 | mfb_set_viewport(window, x, y, width, height); 159 | }, window); 160 | 161 | mfb_set_close_callback([](struct mfb_window *window) { 162 | const char* window_title = ""; 163 | if (window) { 164 | window_title = (const char*) mfb_get_user_data(window); 165 | } 166 | fprintf(stdout, "%s > close\n", window_title); 167 | return true; // true => confirm close 168 | // false => don't close 169 | }, window); 170 | 171 | mfb_set_keyboard_callback([](struct mfb_window *window, mfb_key key, mfb_key_mod mod, bool isPressed) { 172 | const char *window_title = ""; 173 | if(window) { 174 | window_title = (const char *) mfb_get_user_data(window); 175 | } 176 | fprintf(stdout, "%s > keyboard: key: %s (pressed: %d) [key_mod: %x]\n", window_title, mfb_get_key_name(key), isPressed, mod); 177 | if(key == KB_KEY_ESCAPE) { 178 | mfb_close(window); 179 | } 180 | }, window); 181 | 182 | mfb_set_char_input_callback([](struct mfb_window *window, unsigned int charCode) { 183 | const char *window_title = ""; 184 | if(window) { 185 | window_title = (const char *) mfb_get_user_data(window); 186 | } 187 | fprintf(stdout, "%s > charCode: %d\n", window_title, charCode); 188 | }, window); 189 | 190 | mfb_set_mouse_button_callback([](struct mfb_window *window, mfb_mouse_button button, mfb_key_mod mod, bool isPressed) { 191 | const char *window_title = ""; 192 | int x, y; 193 | 194 | if(window) { 195 | window_title = (const char *) mfb_get_user_data(window); 196 | } 197 | x = mfb_get_mouse_x(window); 198 | y = mfb_get_mouse_y(window); 199 | fprintf(stdout, "%s > mouse_button: button: %d (pressed: %d) (at: %d, %d) [key_mod: %x]\n", window_title, button, isPressed, x, y, mod); 200 | }, window); 201 | 202 | mfb_set_mouse_move_callback([](struct mfb_window *window, int x, int y) { 203 | kUnused(window); 204 | kUnused(x); 205 | kUnused(y); 206 | //const char *window_title = ""; 207 | //if(window) { 208 | // window_title = (const char *) mfb_get_user_data(window); 209 | //} 210 | //fprintf(stdout, "%s > mouse_move: %d, %d\n", window_title, x, y); 211 | }, window); 212 | 213 | mfb_set_mouse_scroll_callback([](struct mfb_window *window, mfb_key_mod mod, float deltaX, float deltaY) { 214 | const char *window_title = ""; 215 | if(window) { 216 | window_title = (const char *) mfb_get_user_data(window); 217 | } 218 | fprintf(stdout, "%s > mouse_scroll: x: %f, y: %f [key_mod: %x]\n", window_title, deltaX, deltaY, mod); 219 | }, window); 220 | 221 | #else 222 | 223 | using namespace std::placeholders; 224 | 225 | mfb_set_active_callback (std::bind(&Events::active, &e, _1, _2), window); 226 | mfb_set_resize_callback (std::bind(&Events::resize, &e, _1, _2, _3), window); 227 | mfb_set_close_callback (std::bind(&Events::close, &e, _1), window); 228 | mfb_set_keyboard_callback (std::bind(&Events::keyboard, &e, _1, _2, _3, _4), window); 229 | mfb_set_char_input_callback (std::bind(&Events::char_input, &e, _1, _2), window); 230 | mfb_set_mouse_button_callback(std::bind(&Events::mouse_button, &e, _1, _2, _3, _4), window); 231 | mfb_set_mouse_move_callback (std::bind(&Events::mouse_move, &e, _1, _2, _3), window); 232 | mfb_set_mouse_scroll_callback(std::bind(&Events::mouse_scroll, &e, _1, _2, _3, _4), window); 233 | 234 | #endif 235 | 236 | mfb_set_user_data(window, (void *) "Input Events CPP Test"); 237 | 238 | do { 239 | int i; 240 | mfb_update_state state; 241 | 242 | for (i = 0; i < WIDTH * HEIGHT; ++i) { 243 | noise = seed; 244 | noise >>= 3; 245 | noise ^= seed; 246 | carry = noise & 1; 247 | noise >>= 1; 248 | seed >>= 1; 249 | seed |= (carry << 30); 250 | noise &= 0xFF; 251 | g_buffer[i] = MFB_ARGB(0xff, noise, noise, noise); 252 | } 253 | 254 | state = mfb_update(window, g_buffer); 255 | if (state != STATE_OK) { 256 | window = 0x0; 257 | break; 258 | } 259 | } while(mfb_wait_sync(window)); 260 | 261 | return 0; 262 | } 263 | -------------------------------------------------------------------------------- /tests/ios/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // MiniFB 4 | // 5 | // Created by Carlos Aragones on 22/04/2020. 6 | // Copyright © 2020 Carlos Aragones. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | { 13 | CADisplayLink *mDisplayLink; 14 | } 15 | 16 | @property (strong, nonatomic) UIWindow *window; 17 | 18 | - (void) OnUpdateFrame; 19 | 20 | @end 21 | 22 | -------------------------------------------------------------------------------- /tests/ios/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // MiniFB 4 | // 5 | // Created by Carlos Aragones on 22/04/2020. 6 | // Copyright © 2020 Carlos Aragones. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #include 11 | 12 | //------------------------------------- 13 | #define kUnused(var) (void) var; 14 | 15 | //------------------------------------- 16 | struct mfb_window *g_window = 0x0; 17 | uint32_t *g_buffer = 0x0; 18 | uint32_t g_width = 0; 19 | uint32_t g_height = 0; 20 | float g_scale = 1; 21 | 22 | //------------------------------------- 23 | @interface AppDelegate () 24 | 25 | @end 26 | 27 | //------------------------------------- 28 | void 29 | mouse_btn(struct mfb_window *window, mfb_mouse_button button, mfb_key_mod mod, bool isPressed) { 30 | kUnused(mod); 31 | NSLog(@"Touch: %d at %d, %d is %d", (int)button - MOUSE_BTN_0, mfb_get_mouse_x(window), mfb_get_mouse_y(window), (int) isPressed); 32 | } 33 | 34 | //------------------------------------- 35 | void 36 | mouse_move(struct mfb_window *window, int x, int y) { 37 | kUnused(window); 38 | NSLog(@"Touch moved %d, %d", x, y); 39 | } 40 | 41 | void 42 | resize(struct mfb_window *window, int width, int height) { 43 | kUnused(window); 44 | g_width = width; 45 | g_height = height; 46 | g_buffer = realloc(g_buffer, g_width * g_height * 4); 47 | NSLog(@"Resize %d, %d", width, height); 48 | } 49 | 50 | //------------------------------------- 51 | @implementation AppDelegate 52 | 53 | //------------------------------------- 54 | - (void) OnUpdateFrame { 55 | static int seed = 0xbeef; 56 | int noise, carry; 57 | int dis = 0; 58 | 59 | if(g_buffer != 0x0) { 60 | uint32_t i = 0; 61 | for (uint32_t y = 0; y < g_height; ++y) { 62 | for (uint32_t x = 0; x < g_width; ++x) { 63 | noise = seed; 64 | noise >>= 3; 65 | noise ^= seed; 66 | carry = noise & 1; 67 | noise >>= 1; 68 | seed >>= 1; 69 | seed |= (carry << 30); 70 | noise &= 0xFF >> dis; 71 | g_buffer[i++] = MFB_RGB(noise, noise, noise); 72 | } 73 | if((y & 0x07) == 0x07) 74 | dis ^= 0x01; 75 | } 76 | } 77 | 78 | mfb_update_state state = mfb_update_ex(g_window, g_buffer, g_width, g_height); 79 | if (state != STATE_OK) { 80 | free(g_buffer); 81 | g_buffer = 0x0; 82 | g_width = 0; 83 | g_height = 0; 84 | } 85 | } 86 | 87 | //------------------------------------- 88 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 89 | // Override point for customization after application launch. 90 | kUnused(application); 91 | kUnused(launchOptions); 92 | 93 | if(g_window == 0x0) { 94 | mfb_get_monitor_scale(0x0, &g_scale, 0x0); 95 | //g_scale = [UIScreen mainScreen].scale; 96 | g_width = [UIScreen mainScreen].bounds.size.width * g_scale; 97 | g_height = [UIScreen mainScreen].bounds.size.height * g_scale; 98 | g_window = mfb_open("noise", g_width, g_height); 99 | if(g_window != 0x0) { 100 | g_width -= 100; 101 | g_height -= 100; 102 | mfb_set_viewport(g_window, 50, 50, g_width, g_height); 103 | g_buffer = malloc(g_width * g_height * 4); 104 | mfb_set_mouse_move_callback(g_window, mouse_move); 105 | mfb_set_mouse_button_callback(g_window, mouse_btn); 106 | mfb_set_resize_callback(g_window, resize); 107 | } 108 | } 109 | 110 | return YES; 111 | } 112 | 113 | //------------------------------------- 114 | - (void) applicationWillResignActive:(UIApplication *)application { 115 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 116 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 117 | kUnused(application); 118 | } 119 | 120 | //------------------------------------- 121 | - (void)applicationDidEnterBackground:(UIApplication *)application { 122 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 123 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 124 | kUnused(application); 125 | [mDisplayLink invalidate]; 126 | } 127 | 128 | //------------------------------------- 129 | - (void)applicationWillEnterForeground:(UIApplication *)application { 130 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 131 | kUnused(application); 132 | } 133 | 134 | //------------------------------------- 135 | - (void)applicationDidBecomeActive:(UIApplication *)application { 136 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 137 | kUnused(application); 138 | 139 | mDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(OnUpdateFrame)]; 140 | [mDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 141 | } 142 | 143 | //------------------------------------- 144 | - (void)applicationWillTerminate:(UIApplication *)application { 145 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 146 | kUnused(application); 147 | 148 | [mDisplayLink invalidate]; 149 | mfb_close(g_window); 150 | } 151 | 152 | @end 153 | -------------------------------------------------------------------------------- /tests/ios/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // MiniFB 4 | // 5 | // Created by Carlos Aragones on 22/04/2020. 6 | // Copyright © 2020 Carlos Aragones. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/multiple_windows.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #define _USE_MATH_DEFINES 5 | #include 6 | 7 | #define kPI 3.14159265358979323846f 8 | #define kUnused(var) (void) var; 9 | 10 | #define WIDTH_A 800 11 | #define HEIGHT_A 600 12 | static unsigned int g_buffer_a[WIDTH_A * HEIGHT_A]; 13 | 14 | #define WIDTH_B 320 15 | #define HEIGHT_B 240 16 | static unsigned int g_buffer_b[WIDTH_B * HEIGHT_B]; 17 | 18 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 19 | 20 | void 21 | active(struct mfb_window *window, bool isActive) { 22 | const char *window_title = ""; 23 | if(window) { 24 | window_title = (const char *) mfb_get_user_data(window); 25 | } 26 | fprintf(stdout, "%s > active: %d\n", window_title, isActive); 27 | } 28 | 29 | void 30 | resize(struct mfb_window *window, int width, int height) { 31 | const char *window_title = ""; 32 | if(window) { 33 | window_title = (const char *) mfb_get_user_data(window); 34 | } 35 | 36 | fprintf(stdout, "%s > resize: %d, %d\n", window_title, width, height); 37 | } 38 | 39 | void 40 | keyboard(struct mfb_window *window, mfb_key key, mfb_key_mod mod, bool isPressed) { 41 | const char *window_title = ""; 42 | if(window) { 43 | window_title = (const char *) mfb_get_user_data(window); 44 | } 45 | fprintf(stdout, "%s > keyboard: key: %s (pressed: %d) [key_mod: %x]\n", window_title, mfb_get_key_name(key), isPressed, mod); 46 | if(key == KB_KEY_ESCAPE) { 47 | mfb_close(window); 48 | } 49 | } 50 | 51 | void 52 | char_input(struct mfb_window *window, unsigned int charCode) { 53 | const char *window_title = ""; 54 | if(window) { 55 | window_title = (const char *) mfb_get_user_data(window); 56 | } 57 | fprintf(stdout, "%s > charCode: %d\n", window_title, charCode); 58 | } 59 | 60 | void 61 | mouse_btn(struct mfb_window *window, mfb_mouse_button button, mfb_key_mod mod, bool isPressed) { 62 | const char *window_title = ""; 63 | if(window) { 64 | window_title = (const char *) mfb_get_user_data(window); 65 | } 66 | fprintf(stdout, "%s > mouse_btn: button: %d (pressed: %d) [key_mod: %x]\n", window_title, button, isPressed, mod); 67 | } 68 | 69 | void 70 | mouse_move(struct mfb_window *window, int x, int y) { 71 | kUnused(window); 72 | kUnused(x); 73 | kUnused(y); 74 | // const char *window_title = ""; 75 | // if(window) { 76 | // window_t(const char *) itle = mfb_get_user_data(window); 77 | // } 78 | //fprintf(stdout, "%s > mouse_move: %d, %d\n", window_title, x, y); 79 | } 80 | 81 | void 82 | mouse_scroll(struct mfb_window *window, mfb_key_mod mod, float deltaX, float deltaY) { 83 | const char *window_title = ""; 84 | if(window) { 85 | window_title = (const char *) mfb_get_user_data(window); 86 | } 87 | fprintf(stdout, "%s > mouse_scroll: x: %f, y: %f [key_mod: %x]\n", window_title, deltaX, deltaY, mod); 88 | } 89 | 90 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 91 | 92 | int 93 | main() 94 | { 95 | int noise, carry, seed = 0xbeef; 96 | 97 | struct mfb_window *window_a = mfb_open_ex("Multiple Windows Test", WIDTH_A, HEIGHT_A, WF_RESIZABLE); 98 | if (!window_a) 99 | return 0; 100 | 101 | mfb_set_active_callback(window_a, active); 102 | mfb_set_resize_callback(window_a, resize); 103 | mfb_set_keyboard_callback(window_a, keyboard); 104 | mfb_set_char_input_callback(window_a, char_input); 105 | mfb_set_mouse_button_callback(window_a, mouse_btn); 106 | mfb_set_mouse_move_callback(window_a, mouse_move); 107 | mfb_set_mouse_scroll_callback(window_a, mouse_scroll); 108 | 109 | mfb_set_user_data(window_a, (void *) "Window A"); 110 | mfb_set_viewport(window_a, 25, 25, WIDTH_A-50, HEIGHT_A-50); 111 | 112 | //-- 113 | struct mfb_window *window_b = mfb_open_ex("Secondary Window", WIDTH_B, HEIGHT_B, WF_RESIZABLE); 114 | if (!window_b) 115 | return 0; 116 | 117 | mfb_set_active_callback(window_b, active); 118 | mfb_set_resize_callback(window_b, resize); 119 | mfb_set_keyboard_callback(window_b, keyboard); 120 | mfb_set_char_input_callback(window_b, char_input); 121 | mfb_set_mouse_button_callback(window_b, mouse_btn); 122 | mfb_set_mouse_move_callback(window_b, mouse_move); 123 | mfb_set_mouse_scroll_callback(window_b, mouse_scroll); 124 | 125 | mfb_set_user_data(window_b, (void *) "Window B"); 126 | 127 | // Generate pallete for plasma effect 128 | uint32_t pallete[512]; 129 | float inc = 90.0f / 64.0f; 130 | for(uint32_t c=0; c<64; ++c) { 131 | int32_t col = (int32_t) ((255.0f * sinf(c * inc * kPI / 180.0f)) + 0.5f); 132 | pallete[64*0 + c] = MFB_ARGB(255, col, 0, 0); 133 | pallete[64*1 + c] = MFB_ARGB(255, 255, col, 0); 134 | pallete[64*2 + c] = MFB_ARGB(255, 255-col, 255, 0); 135 | pallete[64*3 + c] = MFB_ARGB(255, 0, 255, col); 136 | pallete[64*4 + c] = MFB_ARGB(255, 0, 255-col, 255); 137 | pallete[64*5 + c] = MFB_ARGB(255, col, 0, 255); 138 | pallete[64*6 + c] = MFB_ARGB(255, 255, 0, 255-col); 139 | pallete[64*7 + c] = MFB_ARGB(255, 255-col, 0, 0); 140 | } 141 | 142 | mfb_set_target_fps(10); 143 | 144 | //-- 145 | float time = 0; 146 | for (;;) 147 | { 148 | int i, x, y; 149 | float dx, dy, time_x, time_y; 150 | int index; 151 | 152 | mfb_update_state state_a, state_b; 153 | 154 | if(window_a != 0x0) { 155 | for (i = 0; i < WIDTH_A * HEIGHT_A; ++i) 156 | { 157 | noise = seed; 158 | noise >>= 3; 159 | noise ^= seed; 160 | carry = noise & 1; 161 | noise >>= 1; 162 | seed >>= 1; 163 | seed |= (carry << 30); 164 | noise &= 0xFF; 165 | g_buffer_a[i] = MFB_ARGB(255, noise, noise, noise); 166 | } 167 | 168 | //-- 169 | state_a = mfb_update(window_a, g_buffer_a); 170 | if (state_a != STATE_OK) { 171 | window_a = 0x0; 172 | } 173 | } 174 | 175 | //-- 176 | if(window_b != 0x0) { 177 | time_x = sinf(time * kPI / 180.0f); 178 | time_y = cosf(time * kPI / 180.0f); 179 | i = 0; 180 | for(y=0; y 2 | #include 3 | #include 4 | #include 5 | 6 | static uint32_t g_width = 800; 7 | static uint32_t g_height = 600; 8 | static uint32_t *g_buffer = 0x0; 9 | 10 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 11 | 12 | void 13 | resize(struct mfb_window *window, int width, int height) { 14 | (void) window; 15 | g_width = width; 16 | g_height = height; 17 | g_buffer = realloc(g_buffer, g_width * g_height * 4); 18 | } 19 | 20 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 21 | 22 | int 23 | main() 24 | { 25 | uint32_t i, noise, carry, seed = 0xbeef; 26 | 27 | struct mfb_window *window = mfb_open_ex("Noise Test", g_width, g_height, WF_RESIZABLE); 28 | if (!window) 29 | return 0; 30 | 31 | g_buffer = (uint32_t *) malloc(g_width * g_height * 4); 32 | mfb_set_resize_callback(window, resize); 33 | 34 | mfb_set_viewport(window, 50, 50, g_width - 50 - 50, g_height - 50 - 50); 35 | resize(window, g_width - 100, g_height - 100); // to resize buffer 36 | 37 | mfb_update_state state; 38 | do { 39 | for (i = 0; i < g_width * g_height; ++i) { 40 | noise = seed; 41 | noise >>= 3; 42 | noise ^= seed; 43 | carry = noise & 1; 44 | noise >>= 1; 45 | seed >>= 1; 46 | seed |= (carry << 30); 47 | noise &= 0xFF; 48 | g_buffer[i] = MFB_ARGB(0xff, noise, noise, noise); 49 | } 50 | 51 | state = mfb_update_ex(window, g_buffer, g_width, g_height); 52 | if (state != STATE_OK) { 53 | window = 0x0; 54 | break; 55 | } 56 | } while(mfb_wait_sync(window)); 57 | 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /tests/timer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static uint32_t g_width = 800; 7 | static uint32_t g_height = 600; 8 | static uint32_t *g_buffer = 0x0; 9 | 10 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 11 | 12 | void 13 | resize(struct mfb_window *window, int width, int height) { 14 | (void) window; 15 | g_width = width; 16 | g_height = height; 17 | g_buffer = realloc(g_buffer, g_width * g_height * 4); 18 | } 19 | 20 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 21 | 22 | int 23 | main() 24 | { 25 | uint32_t i, noise, carry, seed = 0xbeef; 26 | 27 | struct mfb_window *window = mfb_open_ex("Timer Test", g_width, g_height, WF_RESIZABLE); 28 | if (!window) 29 | return 0; 30 | 31 | g_buffer = (uint32_t *) malloc(g_width * g_height * 4); 32 | mfb_set_resize_callback(window, resize); 33 | 34 | mfb_set_viewport(window, 50, 50, g_width - 50 - 50, g_height - 50 - 50); 35 | resize(window, g_width - 100, g_height - 100); // to resize buffer 36 | 37 | struct mfb_timer *timer = mfb_timer_create(); 38 | mfb_update_state state; 39 | do { 40 | mfb_timer_now(timer); 41 | for (i = 0; i < g_width * g_height; ++i) { 42 | noise = seed; 43 | noise >>= 3; 44 | noise ^= seed; 45 | carry = noise & 1; 46 | noise >>= 1; 47 | seed >>= 1; 48 | seed |= (carry << 30); 49 | noise &= 0xFF; 50 | g_buffer[i] = MFB_ARGB(0xff, noise, noise, noise); 51 | } 52 | state = mfb_update_ex(window, g_buffer, g_width, g_height); 53 | if (state != STATE_OK) { 54 | window = 0x0; 55 | break; 56 | } 57 | printf("frame time: %f\n", mfb_timer_delta(timer)); 58 | } while(mfb_wait_sync(window)); 59 | 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /tests/web/hidpi.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |

hidpi

13 | 14 | 15 |
16 | 20 | 21 | -------------------------------------------------------------------------------- /tests/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 14 | 15 | 16 |
17 |

MiniFB Web Tests

18 | hidpi 19 | input events 20 | multiple windows 21 | noise 22 | timer 23 |
24 | 25 | -------------------------------------------------------------------------------- /tests/web/input_events.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |

Input Events Test

13 |

Open the development console to see events.

14 | 15 |
16 | 20 | 21 | -------------------------------------------------------------------------------- /tests/web/multiple_windows.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |

Multiple Windows Test

13 | 14 | 15 |
16 | 20 | 21 | -------------------------------------------------------------------------------- /tests/web/noise.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |

Noise

13 | 14 |
15 | 19 | 20 | -------------------------------------------------------------------------------- /tests/web/timer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |

Timer

13 |

Open the development console to see log output.

14 | 15 |
16 | 20 | 21 | -------------------------------------------------------------------------------- /tundra.lua: -------------------------------------------------------------------------------- 1 | require "tundra.syntax.glob" 2 | local native = require('tundra.native') 3 | 4 | local win32 = { 5 | Env = { 6 | GENERATE_PDB = "1", 7 | CCOPTS = { 8 | "/FS", 9 | "/W4", 10 | "/WX", "/I.", "/D_CRT_SECURE_NO_WARNINGS", 11 | { "/Od"; Config = "*-*-debug" }, 12 | { "/O2"; Config = "*-*-release" }, 13 | }, 14 | 15 | PROGOPTS = { 16 | "/INCREMENTAL:NO"-- Disable incremental linking. It doesn't work properly in our use case (nearly all code in libs) and causes log spam. 17 | }, 18 | 19 | }, 20 | } 21 | 22 | local macosx = { 23 | Env = { 24 | CCOPTS = { 25 | "-Wpedantic", "-Werror", "-Wall", 26 | { "-O0", "-g"; Config = "*-*-debug" }, 27 | { "-O3"; Config = "*-*-release" }, 28 | }, 29 | }, 30 | 31 | Frameworks = { "Cocoa" }, 32 | } 33 | 34 | local x11 = { 35 | Env = { 36 | CPPPATH = { "/usr/include", }, 37 | CCOPTS = { 38 | "-Wpedantic", "-Werror", "-Wall", 39 | { "-O0", "-g"; Config = "*-*-debug" }, 40 | { "-O3"; Config = "*-*-release" }, 41 | }, 42 | }, 43 | } 44 | 45 | 46 | Build { 47 | IdeGenerationHints = { 48 | Msvc = { 49 | PlatformMappings = { 50 | ['win32-msvc'] = 'Win32', 51 | ['win32-msvc'] = 'Win64', 52 | }, 53 | FullMappings = { 54 | ['win32-msvc-debug-default'] = { Config='Debug', Platform='Win32' }, 55 | ['win32-msvc-production-default'] = { Config='Production', Platform='Win32' }, 56 | ['win32-msvc-release-default'] = { Config='Release', Platform='Win32' }, 57 | ['win64-msvc-debug-default'] = { Config='Debug', Platform='Win64' }, 58 | ['win64-msvc-production-default'] = { Config='Production', Platform='Win64' }, 59 | ['win64-msvc-release-default'] = { Config='Release', Platform='Win64' }, 60 | }, 61 | }, 62 | MsvcSolutions = { ['minfb.sln'] = { } }, 63 | }, 64 | 65 | Configs = { 66 | Config { Name = "win32-msvc", Inherit = win32, Tools = { "msvc" }, SupportedHosts = { "windows" }, }, 67 | Config { Name = "win64-msvc", Inherit = win32, Tools = { "msvc" }, SupportedHosts = { "windows" }, }, 68 | Config { Name = "macosx-clang", Inherit = macosx, Tools = { "clang-osx" }, SupportedHosts = { "macosx" },}, 69 | Config { Name = "x11-gcc", Inherit = x11, Tools = { "gcc" }, SupportedHosts = { "linux", "freebsd" },}, 70 | Config { Name = "wayland-gcc", Inherit = x11, Tools = { "gcc" }, SupportedHosts = { "linux" },}, 71 | -- Config { Name = "x11-clang", Inherit = x11, Tools = { "clang" }, SupportedHosts = { "linux", "freebsd" },}, 72 | }, 73 | 74 | Units = { 75 | "units.lua", 76 | }, 77 | } 78 | -------------------------------------------------------------------------------- /units.lua: -------------------------------------------------------------------------------- 1 | 2 | StaticLibrary { 3 | Name = "minifb", 4 | 5 | Env = { CPPPATH = { "include", }, }, 6 | 7 | Sources = FGlob { 8 | Dir = "src", 9 | Extensions = { ".cpp", ".c", ".h", ".s", ".m" }, 10 | Filters = { 11 | { Pattern = "[/\\]windows[/\\]"; Config = { "win32-*", "win64-*" } }, 12 | { Pattern = "[/\\]macosx[/\\]"; Config = "mac*-*" }, 13 | { Pattern = "[/\\]x11[/\\]"; Config = { "x11-*" } }, 14 | { Pattern = "[/\\]wayland[/\\]"; Config = { "wayland-*" } }, 15 | }, 16 | 17 | Recursive = true, 18 | }, 19 | 20 | Propagate = { 21 | Libs = { 22 | "user32.lib"; Config = "win32-*", 23 | "ws2_32.lib"; Config = "win32-*", 24 | "gdi32.lib"; Config = "win32-*", 25 | }, 26 | 27 | Frameworks = { "Cocoa" }, 28 | }, 29 | } 30 | 31 | Program { 32 | 33 | Name = "noise", 34 | 35 | Env = { CPPPATH = { "include", }, }, 36 | 37 | Depends = { "minifb" }, 38 | Sources = { "tests/noise.c" }, 39 | 40 | Libs = { 41 | { "X11"; Config = "x11-*" }, 42 | { "wayland-client", "wayland-cursor"; Config = "wayland-*" }, 43 | }, 44 | } 45 | 46 | Default "noise" 47 | --------------------------------------------------------------------------------