Initial commit

This commit is contained in:
Brandon Dyck 2021-11-23 20:24:38 -07:00
commit 6120807f92
42 changed files with 7688 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
zig-cache/
zig-out/

32
build.zig Normal file
View File

@ -0,0 +1,32 @@
const std = @import("std");
const minifb = @import("lib/minifb/build.zig");
pub fn build(b: *std.build.Builder) void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{});
// Standard release options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const mode = b.standardReleaseOptions();
const exe = b.addExecutable("minifb-zig", "src/main.zig");
exe.setTarget(target);
exe.setBuildMode(mode);
exe.addPackagePath("minifb", "lib/minifb/src/minifb.zig");
minifb.link(b, exe);
exe.install();
const run_cmd = exe.run();
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_cmd.addArgs(args);
}
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
}

72
lib/minifb/build.zig Normal file
View File

@ -0,0 +1,72 @@
const std = @import("std");
// fn thisDir() []const u8 {
// return std.fs.path.dirname(@src().file) orelse ".";
// }
pub fn build(b: *std.build.Builder) void {
// TODO Use mode to set debug flags in MiniFB
const mode = b.standardReleaseOptions();
const target = b.standardTargetOptions(.{});
var main_tests = b.addTest("src/minifb.zig");
main_tests.setBuildMode(mode);
main_tests.setTarget(target);
link(b, main_test);
const test_step = b.step("test", "Run library tests");
test_step.dependOn(&main_tests.step);
}
pub fn link(b: *std.build.Builder, step: *std.build.LibExeObjStep) void {
const lib = buildLibrary(b, step);
step.linkLibrary(lib);
}
fn fromHere(allocator: *std.mem.Allocator, path: []const u8) []const u8 {
const here = std.fs.path.dirname(@src().file) orelse ".";
return std.fs.path.join(allocator, &.{here, path}) catch unreachable;
}
fn buildLibrary(b: *std.build.Builder, step: *std.build.LibExeObjStep) *std.build.LibExeObjStep {
// var main_abs = std.fs.path.join(b.allocator, &.{thisDir(), "src/minifb.zig"}) catch unreachable;
const lib = b.addStaticLibrary("minifb", fromHere(b.allocator, "src/minifb.zig"));
lib.setBuildMode(step.build_mode);
var sources = std.ArrayList([]const u8) .init(b.allocator);
for ([_][]const u8{
"upstream/src/MiniFB_common.c",
"upstream/src/MiniFB_internal.c",
//"upstream/src/MiniFB_internal.h",
"upstream/src/MiniFB_timer.c",
//"upstream/src/WindowData.h",
//"upstream/src/windows/WindowData_Win.h",
"upstream/src/windows/WinMiniFB.c",
}) |path| {
// const abs_path = std.fs.path.join(b.allocator, &.{ thisDir(), path}) catch unreachable;
sources.append(fromHere(b.allocator, path)) catch unreachable;
}
// TODO More than Windows
lib.addCSourceFiles(sources.items, &[_][]const u8{
"-Wall",
"-Wextra",
"-pedantic",
"-Wno-switch",
"-Wno-unused-function",
"-Wno-implicit-fallthrough",
"-std=c11",
});
const include_abs = fromHere(b.allocator, "upstream/include");
const src_abs = fromHere(b.allocator, "upstream/src");
lib.addIncludeDir(include_abs);
lib.addIncludeDir(src_abs);
step.addIncludeDir(include_abs);
step.addIncludeDir(src_abs);
lib.linkSystemLibraryName("gdi32");
lib.linkLibC();
lib.install();
return lib;
}

45
lib/minifb/src/minifb.zig Normal file
View File

@ -0,0 +1,45 @@
const std = @import("std");
const minifb_c = @cImport({
@cInclude("MiniFB.h");
@cInclude("MiniFB_enums.h");
});
const testing = std.testing;
pub const Window = struct{
cwin: *minifb_c.mfb_window,
pub const UpdateError = error {
InvalidWindow,
InvalidBuffer,
InternalError,
};
pub const State = enum {ok, exit};
pub fn open(title: [*:0]const u8, width: u32, height: u32) !Window {
const cwin: ?*minifb_c.mfb_window = minifb_c.mfb_open(@as([*c]const u8, title), width, height);
if (cwin) |value| {
return Window {.cwin=value};
} else {
return error.ItBroke;
}
}
pub fn waitSync(self: Window) bool {
return minifb_c.mfb_wait_sync(self.cwin);
}
pub fn update(self:Window, buffer: []u32) UpdateError!State {
const rawState = minifb_c.mfb_update(self.cwin, buffer.ptr);
switch (rawState) {
.STATE_OK => return State.ok,
.STATE_EXIT => return State.exit,
.STATE_INVALID_WINDOW => return UpdateError.InvalidWindow,
.STATE_INVALID_BUFFER => return UpdateError.InvalidBuffer,
else => return UpdateError.InternalError,
}
}
};

View File

@ -0,0 +1,91 @@
#ifndef _MINIFB_H_
#define _MINIFB_H_
#include "MiniFB_enums.h"
#ifdef __cplusplus
extern "C" {
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define MFB_RGB(r, g, b) (((uint32_t) r) << 16) | (((uint32_t) g) << 8) | ((uint32_t) b)
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Create a window that is used to display the buffer sent into the mfb_update function, returns 0 if fails
struct mfb_window * mfb_open(const char *title, unsigned width, unsigned height);
struct mfb_window * mfb_open_ex(const char *title, unsigned width, unsigned height, unsigned flags);
// Update the display
// Input buffer is assumed to be a 32-bit buffer of the size given in the open call
// Will return a negative status if something went wrong or the user want to exit
// Also updates the window events
mfb_update_state mfb_update(struct mfb_window *window, void *buffer);
mfb_update_state mfb_update_ex(struct mfb_window *window, void *buffer, unsigned width, unsigned height);
// Only updates the window events
mfb_update_state mfb_update_events(struct mfb_window *window);
// Close the window
void mfb_close(struct mfb_window *window);
// Set user data
void mfb_set_user_data(struct mfb_window *window, void *user_data);
void * mfb_get_user_data(struct mfb_window *window);
// Set viewport (useful when resize)
bool mfb_set_viewport(struct mfb_window *window, unsigned offset_x, unsigned offset_y, unsigned width, unsigned height);
// DPI
// [Deprecated]: Probably a better name will be mfb_get_monitor_scale
void mfb_get_monitor_dpi(struct mfb_window *window, float *dpi_x, float *dpi_y);
// Use this instead
void mfb_get_monitor_scale(struct mfb_window *window, float *scale_x, float *scale_y);
// Callbacks
void mfb_set_active_callback(struct mfb_window *window, mfb_active_func callback);
void mfb_set_resize_callback(struct mfb_window *window, mfb_resize_func callback);
void mfb_set_keyboard_callback(struct mfb_window *window, mfb_keyboard_func callback);
void mfb_set_char_input_callback(struct mfb_window *window, mfb_char_input_func callback);
void mfb_set_mouse_button_callback(struct mfb_window *window, mfb_mouse_button_func callback);
void mfb_set_mouse_move_callback(struct mfb_window *window, mfb_mouse_move_func callback);
void mfb_set_mouse_scroll_callback(struct mfb_window *window, mfb_mouse_scroll_func callback);
// Getters
const char * mfb_get_key_name(mfb_key key);
bool mfb_is_window_active(struct mfb_window *window);
unsigned mfb_get_window_width(struct mfb_window *window);
unsigned mfb_get_window_height(struct mfb_window *window);
int mfb_get_mouse_x(struct mfb_window *window); // Last mouse pos X
int mfb_get_mouse_y(struct mfb_window *window); // Last mouse pos Y
float mfb_get_mouse_scroll_x(struct mfb_window *window); // Mouse wheel X as a sum. When you call this function it resets.
float mfb_get_mouse_scroll_y(struct mfb_window *window); // Mouse wheel Y as a sum. When you call this function it resets.
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)
const uint8_t * mfb_get_key_buffer(struct mfb_window *window); // One byte for every key. Press (1), Release 0.
// FPS
void mfb_set_target_fps(uint32_t fps);
unsigned mfb_get_target_fps();
bool mfb_wait_sync(struct mfb_window *window);
// Timer
struct mfb_timer * mfb_timer_create(void);
void mfb_timer_destroy(struct mfb_timer *tmr);
void mfb_timer_reset(struct mfb_timer *tmr);
double mfb_timer_now(struct mfb_timer *tmr);
double mfb_timer_delta(struct mfb_timer *tmr);
double mfb_timer_get_frequency(void);
double mfb_timer_get_resolution(void);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef __cplusplus
}
#include "MiniFB_cpp.h"
#endif
#endif

View File

@ -0,0 +1,136 @@
#pragma once
#if defined(__cplusplus)
#include <functional>
#include "MiniFB.h"
template <class T>
void mfb_set_active_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, bool));
template <class T>
void mfb_set_resize_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, int, int));
template <class T>
void mfb_set_keyboard_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, mfb_key, mfb_key_mod, bool));
template <class T>
void mfb_set_char_input_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, unsigned int));
template <class T>
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));
template <class T>
void mfb_set_mouse_move_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, int, int));
template <class T>
void mfb_set_mouse_scroll_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, mfb_key_mod, float, float));
//-------------------------------------
// To avoid clumsy hands
//-------------------------------------
class mfb_stub {
mfb_stub() : m_window(0x0) {}
template <class T>
friend void mfb_set_active_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, bool));
template <class T>
friend void mfb_set_resize_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, int, int));
template <class T>
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));
template <class T>
friend void mfb_set_keyboard_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, mfb_key, mfb_key_mod, bool));
template <class T>
friend void mfb_set_char_input_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, unsigned int));
template <class T>
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));
template <class T>
friend void mfb_set_mouse_move_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, int, int));
template <class T>
friend void mfb_set_mouse_scroll_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *, mfb_key_mod, float, float));
static mfb_stub *GetInstance(struct mfb_window *window);
static void active_stub(struct mfb_window *window, bool isActive);
static void resize_stub(struct mfb_window *window, int width, int height);
static void keyboard_stub(struct mfb_window *window, mfb_key key, mfb_key_mod mod, bool isPressed);
static void char_input_stub(struct mfb_window *window, unsigned int);
static void mouse_btn_stub(struct mfb_window *window, mfb_mouse_button button, mfb_key_mod mod, bool isPressed);
static void mouse_move_stub(struct mfb_window *window, int x, int y);
static void scroll_stub(struct mfb_window *window, mfb_key_mod mod, float deltaX, float deltaY);
struct mfb_window *m_window;
std::function<void(struct mfb_window *window, bool)> m_active;
std::function<void(struct mfb_window *window, int, int)> m_resize;
std::function<void(struct mfb_window *window, mfb_key, mfb_key_mod, bool)> m_keyboard;
std::function<void(struct mfb_window *window, unsigned int)> m_char_input;
std::function<void(struct mfb_window *window, mfb_mouse_button, mfb_key_mod, bool)> m_mouse_btn;
std::function<void(struct mfb_window *window, int, int)> m_mouse_move;
std::function<void(struct mfb_window *window, mfb_key_mod, float, float)> m_scroll;
};
//-------------------------------------
template <class T>
inline void mfb_set_active_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *window, bool)) {
using namespace std::placeholders;
mfb_stub *stub = mfb_stub::GetInstance(window);
stub->m_active = std::bind(method, obj, _1, _2);
mfb_set_active_callback(window, mfb_stub::active_stub);
}
template <class T>
inline void mfb_set_resize_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *window, int, int)) {
using namespace std::placeholders;
mfb_stub *stub = mfb_stub::GetInstance(window);
stub->m_resize = std::bind(method, obj, _1, _2, _3);
mfb_set_resize_callback(window, mfb_stub::resize_stub);
}
template <class T>
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)) {
using namespace std::placeholders;
mfb_stub *stub = mfb_stub::GetInstance(window);
stub->m_keyboard = std::bind(method, obj, _1, _2, _3, _4);
mfb_set_keyboard_callback(window, mfb_stub::keyboard_stub);
}
template <class T>
inline void mfb_set_char_input_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *window, unsigned int)) {
using namespace std::placeholders;
mfb_stub *stub = mfb_stub::GetInstance(window);
stub->m_char_input = std::bind(method, obj, _1, _2);
mfb_set_char_input_callback(window, mfb_stub::char_input_stub);
}
template <class T>
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)) {
using namespace std::placeholders;
mfb_stub *stub = mfb_stub::GetInstance(window);
stub->m_mouse_btn = std::bind(method, obj, _1, _2, _3, _4);
mfb_set_mouse_button_callback(window, mfb_stub::mouse_btn_stub);
}
template <class T>
inline void mfb_set_mouse_move_callback(struct mfb_window *window, T *obj, void (T::*method)(struct mfb_window *window, int, int)) {
using namespace std::placeholders;
mfb_stub *stub = mfb_stub::GetInstance(window);
stub->m_mouse_move = std::bind(method, obj, _1, _2, _3);
mfb_set_mouse_move_callback(window, mfb_stub::mouse_move_stub);
}
template <class T>
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)) {
using namespace std::placeholders;
mfb_stub *stub = mfb_stub::GetInstance(window);
stub->m_scroll = std::bind(method, obj, _1, _2, _3, _4);
mfb_set_mouse_scroll_callback(window, mfb_stub::scroll_stub);
}
#endif

View File

@ -0,0 +1,185 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
// Enums
typedef enum {
STATE_OK = 0,
STATE_EXIT = -1,
STATE_INVALID_WINDOW = -2,
STATE_INVALID_BUFFER = -3,
STATE_INTERNAL_ERROR = -4,
} mfb_update_state;
typedef enum {
MOUSE_BTN_0, // No mouse button
MOUSE_BTN_1,
MOUSE_BTN_2,
MOUSE_BTN_3,
MOUSE_BTN_4,
MOUSE_BTN_5,
MOUSE_BTN_6,
MOUSE_BTN_7,
} mfb_mouse_button;
#define MOUSE_LEFT MOUSE_BTN_1
#define MOUSE_RIGHT MOUSE_BTN_2
#define MOUSE_MIDDLE MOUSE_BTN_3
typedef enum {
KB_KEY_UNKNOWN = -1,
KB_KEY_SPACE = 32,
KB_KEY_APOSTROPHE = 39,
KB_KEY_COMMA = 44,
KB_KEY_MINUS = 45,
KB_KEY_PERIOD = 46,
KB_KEY_SLASH = 47,
KB_KEY_0 = 48,
KB_KEY_1 = 49,
KB_KEY_2 = 50,
KB_KEY_3 = 51,
KB_KEY_4 = 52,
KB_KEY_5 = 53,
KB_KEY_6 = 54,
KB_KEY_7 = 55,
KB_KEY_8 = 56,
KB_KEY_9 = 57,
KB_KEY_SEMICOLON = 59,
KB_KEY_EQUAL = 61,
KB_KEY_A = 65,
KB_KEY_B = 66,
KB_KEY_C = 67,
KB_KEY_D = 68,
KB_KEY_E = 69,
KB_KEY_F = 70,
KB_KEY_G = 71,
KB_KEY_H = 72,
KB_KEY_I = 73,
KB_KEY_J = 74,
KB_KEY_K = 75,
KB_KEY_L = 76,
KB_KEY_M = 77,
KB_KEY_N = 78,
KB_KEY_O = 79,
KB_KEY_P = 80,
KB_KEY_Q = 81,
KB_KEY_R = 82,
KB_KEY_S = 83,
KB_KEY_T = 84,
KB_KEY_U = 85,
KB_KEY_V = 86,
KB_KEY_W = 87,
KB_KEY_X = 88,
KB_KEY_Y = 89,
KB_KEY_Z = 90,
KB_KEY_LEFT_BRACKET = 91,
KB_KEY_BACKSLASH = 92,
KB_KEY_RIGHT_BRACKET = 93,
KB_KEY_GRAVE_ACCENT = 96,
KB_KEY_WORLD_1 = 161,
KB_KEY_WORLD_2 = 162,
KB_KEY_ESCAPE = 256,
KB_KEY_ENTER = 257,
KB_KEY_TAB = 258,
KB_KEY_BACKSPACE = 259,
KB_KEY_INSERT = 260,
KB_KEY_DELETE = 261,
KB_KEY_RIGHT = 262,
KB_KEY_LEFT = 263,
KB_KEY_DOWN = 264,
KB_KEY_UP = 265,
KB_KEY_PAGE_UP = 266,
KB_KEY_PAGE_DOWN = 267,
KB_KEY_HOME = 268,
KB_KEY_END = 269,
KB_KEY_CAPS_LOCK = 280,
KB_KEY_SCROLL_LOCK = 281,
KB_KEY_NUM_LOCK = 282,
KB_KEY_PRINT_SCREEN = 283,
KB_KEY_PAUSE = 284,
KB_KEY_F1 = 290,
KB_KEY_F2 = 291,
KB_KEY_F3 = 292,
KB_KEY_F4 = 293,
KB_KEY_F5 = 294,
KB_KEY_F6 = 295,
KB_KEY_F7 = 296,
KB_KEY_F8 = 297,
KB_KEY_F9 = 298,
KB_KEY_F10 = 299,
KB_KEY_F11 = 300,
KB_KEY_F12 = 301,
KB_KEY_F13 = 302,
KB_KEY_F14 = 303,
KB_KEY_F15 = 304,
KB_KEY_F16 = 305,
KB_KEY_F17 = 306,
KB_KEY_F18 = 307,
KB_KEY_F19 = 308,
KB_KEY_F20 = 309,
KB_KEY_F21 = 310,
KB_KEY_F22 = 311,
KB_KEY_F23 = 312,
KB_KEY_F24 = 313,
KB_KEY_F25 = 314,
KB_KEY_KP_0 = 320,
KB_KEY_KP_1 = 321,
KB_KEY_KP_2 = 322,
KB_KEY_KP_3 = 323,
KB_KEY_KP_4 = 324,
KB_KEY_KP_5 = 325,
KB_KEY_KP_6 = 326,
KB_KEY_KP_7 = 327,
KB_KEY_KP_8 = 328,
KB_KEY_KP_9 = 329,
KB_KEY_KP_DECIMAL = 330,
KB_KEY_KP_DIVIDE = 331,
KB_KEY_KP_MULTIPLY = 332,
KB_KEY_KP_SUBTRACT = 333,
KB_KEY_KP_ADD = 334,
KB_KEY_KP_ENTER = 335,
KB_KEY_KP_EQUAL = 336,
KB_KEY_LEFT_SHIFT = 340,
KB_KEY_LEFT_CONTROL = 341,
KB_KEY_LEFT_ALT = 342,
KB_KEY_LEFT_SUPER = 343,
KB_KEY_RIGHT_SHIFT = 344,
KB_KEY_RIGHT_CONTROL = 345,
KB_KEY_RIGHT_ALT = 346,
KB_KEY_RIGHT_SUPER = 347,
KB_KEY_MENU = 348
} mfb_key;
#define KB_KEY_LAST KB_KEY_MENU
typedef enum {
KB_MOD_SHIFT = 0x0001,
KB_MOD_CONTROL = 0x0002,
KB_MOD_ALT = 0x0004,
KB_MOD_SUPER = 0x0008,
KB_MOD_CAPS_LOCK = 0x0010,
KB_MOD_NUM_LOCK = 0x0020
} mfb_key_mod;
typedef enum {
WF_RESIZABLE = 0x01,
WF_FULLSCREEN = 0x02,
WF_FULLSCREEN_DESKTOP = 0x04,
WF_BORDERLESS = 0x08,
WF_ALWAYS_ON_TOP = 0x10,
} mfb_window_flags;
// Opaque pointer
struct mfb_window;
struct mfb_timer;
// Event callbacks
typedef void(*mfb_active_func)(struct mfb_window *window, bool isActive);
typedef void(*mfb_resize_func)(struct mfb_window *window, int width, int height);
typedef void(*mfb_keyboard_func)(struct mfb_window *window, mfb_key key, mfb_key_mod mod, bool isPressed);
typedef void(*mfb_char_input_func)(struct mfb_window *window, unsigned int code);
typedef void(*mfb_mouse_button_func)(struct mfb_window *window, mfb_mouse_button button, mfb_key_mod mod, bool isPressed);
typedef void(*mfb_mouse_move_func)(struct mfb_window *window, int x, int y);
typedef void(*mfb_mouse_scroll_func)(struct mfb_window *window, mfb_key_mod mod, float deltaX, float deltaY);

View File

@ -0,0 +1,7 @@
#pragma once
#include "MiniFB_enums.h"
void user_implemented_init(struct mfb_window *window);
void user_implemented_update(struct mfb_window *window);

View File

@ -0,0 +1,598 @@
#include <MiniFB.h>
#include "WindowData.h"
#include "MiniFB_internal.h"
//-------------------------------------
short int g_keycodes[512] = { 0 };
//-------------------------------------
//-------------------------------------
struct mfb_window *
mfb_open(const char *title, unsigned width, unsigned height) {
return mfb_open_ex(title, width, height, 0);
}
//-------------------------------------
mfb_update_state
mfb_update(struct mfb_window *window, void *buffer) {
if (window == 0x0) {
return STATE_INVALID_WINDOW;
}
SWindowData *window_data = (SWindowData *) window;
return mfb_update_ex(window, buffer, window_data->buffer_width, window_data->buffer_height);
}
//-------------------------------------
void
mfb_set_active_callback(struct mfb_window *window, mfb_active_func callback) {
if(window != 0x0) {
SWindowData *window_data = (SWindowData *) window;
window_data->active_func = callback;
}
}
//-------------------------------------
void
mfb_set_resize_callback(struct mfb_window *window, mfb_resize_func callback) {
if(window != 0x0) {
SWindowData *window_data = (SWindowData *) window;
window_data->resize_func = callback;
}
}
//-------------------------------------
void
mfb_set_keyboard_callback(struct mfb_window *window, mfb_keyboard_func callback) {
if(window != 0x0) {
SWindowData *window_data = (SWindowData *) window;
window_data->keyboard_func = callback;
}
}
//-------------------------------------
void
mfb_set_char_input_callback(struct mfb_window *window, mfb_char_input_func callback) {
if(window != 0x0) {
SWindowData *window_data = (SWindowData *) window;
window_data->char_input_func = callback;
}
}
//-------------------------------------
void
mfb_set_mouse_button_callback(struct mfb_window *window, mfb_mouse_button_func callback) {
if(window != 0x0) {
SWindowData *window_data = (SWindowData *) window;
window_data->mouse_btn_func = callback;
}
}
//-------------------------------------
void
mfb_set_mouse_move_callback(struct mfb_window *window, mfb_mouse_move_func callback) {
if(window != 0x0) {
SWindowData *window_data = (SWindowData *) window;
window_data->mouse_move_func = callback;
}
}
//-------------------------------------
void
mfb_set_mouse_scroll_callback(struct mfb_window *window, mfb_mouse_scroll_func callback) {
if(window != 0x0) {
SWindowData *window_data = (SWindowData *) window;
window_data->mouse_wheel_func = callback;
}
}
//-------------------------------------
void
mfb_set_user_data(struct mfb_window *window, void *user_data) {
if(window != 0x0) {
SWindowData *window_data = (SWindowData *) window;
window_data->user_data = user_data;
}
}
//-------------------------------------
void *
mfb_get_user_data(struct mfb_window *window) {
if(window != 0x0) {
SWindowData *window_data = (SWindowData *) window;
return window_data->user_data;
}
return 0x0;
}
// [Deprecated]
//-------------------------------------
void
mfb_get_monitor_dpi(struct mfb_window *window, float *dpi_x, float *dpi_y) {
mfb_get_monitor_scale(window, dpi_x, dpi_y);
}
//-------------------------------------
void
mfb_close(struct mfb_window *window) {
if(window != 0x0) {
SWindowData *window_data = (SWindowData *) window;
window_data->close = true;
}
}
//-------------------------------------
void
keyboard_default(struct mfb_window *window, mfb_key key, mfb_key_mod mod, bool isPressed) {
kUnused(mod);
kUnused(isPressed);
if (key == KB_KEY_ESCAPE) {
SWindowData *window_data = (SWindowData *) window;
window_data->close = true;
}
}
//-------------------------------------
bool
mfb_is_window_active(struct mfb_window *window) {
if(window != 0x0) {
SWindowData *window_data = (SWindowData *) window;
return window_data->is_active;
}
return false;
}
//-------------------------------------
unsigned
mfb_get_window_width(struct mfb_window *window) {
if(window != 0x0) {
SWindowData *window_data = (SWindowData *) window;
return window_data->window_width;
}
return 0;
}
//-------------------------------------
unsigned
mfb_get_window_height(struct mfb_window *window) {
if(window != 0x0) {
SWindowData *window_data = (SWindowData *) window;
return window_data->window_height;
}
return 0;
}
//-------------------------------------
int
mfb_get_mouse_x(struct mfb_window *window) {
if(window != 0x0) {
SWindowData *window_data = (SWindowData *) window;
return window_data->mouse_pos_x;
}
return 0;
}
//-------------------------------------
int
mfb_get_mouse_y(struct mfb_window *window) {
if(window != 0x0) {
SWindowData *window_data = (SWindowData *) window;
return window_data->mouse_pos_y;
}
return 0;
}
//-------------------------------------
float
mfb_get_mouse_scroll_x(struct mfb_window *window) {
if(window != 0x0) {
SWindowData *window_data = (SWindowData *) window;
return window_data->mouse_wheel_x;
}
return 0;
}
//-------------------------------------
float
mfb_get_mouse_scroll_y(struct mfb_window *window) {
if(window != 0x0) {
SWindowData *window_data = (SWindowData *) window;
return window_data->mouse_wheel_y;
}
return 0;
}
//-------------------------------------
const uint8_t *
mfb_get_mouse_button_buffer(struct mfb_window *window) {
if(window != 0x0) {
SWindowData *window_data = (SWindowData *) window;
return window_data->mouse_button_status;
}
return 0;
}
//-------------------------------------
const uint8_t *
mfb_get_key_buffer(struct mfb_window *window) {
if(window != 0x0) {
SWindowData *window_data = (SWindowData *) window;
return window_data->key_status;
}
return 0;
}
//-------------------------------------
const char *
mfb_get_key_name(mfb_key key) {
switch (key)
{
case KB_KEY_SPACE:
return "Space";
case KB_KEY_APOSTROPHE:
return "Apostrophe";
case KB_KEY_COMMA:
return "Comma";
case KB_KEY_MINUS:
return "Minus";
case KB_KEY_PERIOD:
return "Period";
case KB_KEY_SLASH:
return "Slash";
case KB_KEY_0:
return "0";
case KB_KEY_1:
return "1";
case KB_KEY_2:
return "2";
case KB_KEY_3:
return "3";
case KB_KEY_4:
return "4";
case KB_KEY_5:
return "5";
case KB_KEY_6:
return "6";
case KB_KEY_7:
return "7";
case KB_KEY_8:
return "8";
case KB_KEY_9:
return "9";
case KB_KEY_SEMICOLON:
return "Semicolon";
case KB_KEY_EQUAL:
return "Equal";
case KB_KEY_A:
return "A";
case KB_KEY_B:
return "B";
case KB_KEY_C:
return "C";
case KB_KEY_D:
return "D";
case KB_KEY_E:
return "E";
case KB_KEY_F:
return "F";
case KB_KEY_G:
return "G";
case KB_KEY_H:
return "H";
case KB_KEY_I:
return "I";
case KB_KEY_J:
return "J";
case KB_KEY_K:
return "K";
case KB_KEY_L:
return "L";
case KB_KEY_M:
return "M";
case KB_KEY_N:
return "N";
case KB_KEY_O:
return "O";
case KB_KEY_P:
return "P";
case KB_KEY_Q:
return "Q";
case KB_KEY_R:
return "R";
case KB_KEY_S:
return "S";
case KB_KEY_T:
return "T";
case KB_KEY_U:
return "U";
case KB_KEY_V:
return "V";
case KB_KEY_W:
return "W";
case KB_KEY_X:
return "X";
case KB_KEY_Y:
return "Y";
case KB_KEY_Z:
return "Z";
case KB_KEY_LEFT_BRACKET:
return "Left_Bracket";
case KB_KEY_BACKSLASH:
return "Backslash";
case KB_KEY_RIGHT_BRACKET:
return "Right_Bracket";
case KB_KEY_GRAVE_ACCENT:
return "Grave_Accent";
case KB_KEY_WORLD_1:
return "World_1";
case KB_KEY_WORLD_2:
return "World_2";
case KB_KEY_ESCAPE:
return "Escape";
case KB_KEY_ENTER:
return "Enter";
case KB_KEY_TAB:
return "Tab";
case KB_KEY_BACKSPACE:
return "Backspace";
case KB_KEY_INSERT:
return "Insert";
case KB_KEY_DELETE:
return "Delete";
case KB_KEY_RIGHT:
return "Right";
case KB_KEY_LEFT:
return "Left";
case KB_KEY_DOWN:
return "Down";
case KB_KEY_UP:
return "Up";
case KB_KEY_PAGE_UP:
return "Page_Up";
case KB_KEY_PAGE_DOWN:
return "Page_Down";
case KB_KEY_HOME:
return "Home";
case KB_KEY_END:
return "End";
case KB_KEY_CAPS_LOCK:
return "Caps_Lock";
case KB_KEY_SCROLL_LOCK:
return "Scroll_Lock";
case KB_KEY_NUM_LOCK:
return "Num_Lock";
case KB_KEY_PRINT_SCREEN:
return "Print_Screen";
case KB_KEY_PAUSE:
return "Pause";
case KB_KEY_F1:
return "F1";
case KB_KEY_F2:
return "F2";
case KB_KEY_F3:
return "F3";
case KB_KEY_F4:
return "F4";
case KB_KEY_F5:
return "F5";
case KB_KEY_F6:
return "F6";
case KB_KEY_F7:
return "F7";
case KB_KEY_F8:
return "F8";
case KB_KEY_F9:
return "F9";
case KB_KEY_F10:
return "F10";
case KB_KEY_F11:
return "F11";
case KB_KEY_F12:
return "F12";
case KB_KEY_F13:
return "F13";
case KB_KEY_F14:
return "F14";
case KB_KEY_F15:
return "F15";
case KB_KEY_F16:
return "F16";
case KB_KEY_F17:
return "F17";
case KB_KEY_F18:
return "F18";
case KB_KEY_F19:
return "F19";
case KB_KEY_F20:
return "F20";
case KB_KEY_F21:
return "F21";
case KB_KEY_F22:
return "F22";
case KB_KEY_F23:
return "F23";
case KB_KEY_F24:
return "F24";
case KB_KEY_F25:
return "F25";
case KB_KEY_KP_0:
return "KP_0";
case KB_KEY_KP_1:
return "KP_1";
case KB_KEY_KP_2:
return "KP_2";
case KB_KEY_KP_3:
return "KP_3";
case KB_KEY_KP_4:
return "KP_4";
case KB_KEY_KP_5:
return "KP_5";
case KB_KEY_KP_6:
return "KP_6";
case KB_KEY_KP_7:
return "KP_7";
case KB_KEY_KP_8:
return "KP_8";
case KB_KEY_KP_9:
return "KP_9";
case KB_KEY_KP_DECIMAL:
return "KP_Decimal";
case KB_KEY_KP_DIVIDE:
return "KP_Divide";
case KB_KEY_KP_MULTIPLY:
return "KP_Multiply";
case KB_KEY_KP_SUBTRACT:
return "KP_Subtract";
case KB_KEY_KP_ADD:
return "KP_Add";
case KB_KEY_KP_ENTER:
return "KP_Enter";
case KB_KEY_KP_EQUAL:
return "KP_Equal";
case KB_KEY_LEFT_SHIFT:
return "Left_Shift";
case KB_KEY_LEFT_CONTROL:
return "Left_Control";
case KB_KEY_LEFT_ALT:
return "Left_Alt";
case KB_KEY_LEFT_SUPER:
return "Left_Super";
case KB_KEY_RIGHT_SHIFT:
return "Right_Shift";
case KB_KEY_RIGHT_CONTROL:
return "Right_Control";
case KB_KEY_RIGHT_ALT:
return "Right_Alt";
case KB_KEY_RIGHT_SUPER:
return "Right_Super";
case KB_KEY_MENU:
return "Menu";
case KB_KEY_UNKNOWN:
return "Unknown";
}
return "Unknown";
}

View File

@ -0,0 +1,69 @@
#include <MiniFB_cpp.h>
#include <MiniFB_enums.h>
#include <vector>
//-------------------------------------
mfb_stub *
mfb_stub::GetInstance(struct mfb_window *window) {
static std::vector<mfb_stub *> s_instances;
for(mfb_stub *instance : s_instances) {
if(instance->m_window == window) {
return instance;
}
}
s_instances.push_back(new mfb_stub);
s_instances.back()->m_window = window;
return s_instances.back();
}
//-------------------------------------
void
mfb_stub::active_stub(struct mfb_window *window, bool isActive) {
mfb_stub *stub = mfb_stub::GetInstance(window);
stub->m_active(window, isActive);
}
//-------------------------------------
void
mfb_stub::resize_stub(struct mfb_window *window, int width, int height) {
mfb_stub *stub = mfb_stub::GetInstance(window);
stub->m_resize(window, width, height);
}
//-------------------------------------
void
mfb_stub::keyboard_stub(struct mfb_window *window, mfb_key key, mfb_key_mod mod, bool isPressed) {
mfb_stub *stub = mfb_stub::GetInstance(window);
stub->m_keyboard(window, key, mod, isPressed);
}
//-------------------------------------
void
mfb_stub::char_input_stub(struct mfb_window *window, unsigned int code) {
mfb_stub *stub = mfb_stub::GetInstance(window);
stub->m_char_input(window, code);
}
//-------------------------------------
void
mfb_stub::mouse_btn_stub(struct mfb_window *window, mfb_mouse_button button, mfb_key_mod mod, bool isPressed) {
mfb_stub *stub = mfb_stub::GetInstance(window);
stub->m_mouse_btn(window, button, mod, isPressed);
}
//-------------------------------------
void
mfb_stub::mouse_move_stub(struct mfb_window *window, int x, int y) {
mfb_stub *stub = mfb_stub::GetInstance(window);
stub->m_mouse_move(window, x, y);
}
//-------------------------------------
void
mfb_stub::scroll_stub(struct mfb_window *window, mfb_key_mod mod, float deltaX, float deltaY) {
mfb_stub *stub = mfb_stub::GetInstance(window);
stub->m_scroll(window, mod, deltaX, deltaY);
}

View File

@ -0,0 +1,113 @@
#include "MiniFB_internal.h"
#include <stdint.h>
//#define kUseBilinearInterpolation
#if defined(kUseBilinearInterpolation)
//-------------------------------------
static uint32_t
interpolate(uint32_t *srcImage, uint32_t x, uint32_t y, uint32_t srcOffsetX, uint32_t srcOffsetY, uint32_t srcWidth, uint32_t srcHeight, uint32_t srcPitch) {
uint32_t incX = x + 1 < srcWidth ? 1 : 0;
uint32_t incY = y + 1 < srcHeight ? srcPitch : 0;
uint8_t *p00 = (uint8_t *) &srcImage[(srcOffsetX >> 16)];
uint8_t *p01 = (uint8_t *) &srcImage[(srcOffsetX >> 16) + incX];
uint8_t *p10 = (uint8_t *) &srcImage[(srcOffsetX >> 16) + incY];
uint8_t *p11 = (uint8_t *) &srcImage[(srcOffsetX >> 16) + incY + incX];
uint32_t wx2 = srcOffsetX & 0xffff;
uint32_t wy2 = srcOffsetY & 0xffff;
uint32_t wx1 = 0x10000 - wx2;
uint32_t wy1 = 0x10000 - wy2;
uint32_t w1 = ((uint64_t) wx1 * wy1) >> 16;
uint32_t w2 = ((uint64_t) wx2 * wy1) >> 16;
uint32_t w3 = ((uint64_t) wx1 * wy2) >> 16;
uint32_t w4 = ((uint64_t) wx2 * wy2) >> 16;
// If you don't have uint64_t
//uint32_t b = (((p00[0] * wx1 + p01[0] * wx2) >> 16) * wy1 + ((p10[0] * wx1 + p11[0] * wx2) >> 16) * wy2) >> 16;
//uint32_t g = (((p00[1] * wx1 + p01[1] * wx2) >> 16) * wy1 + ((p10[1] * wx1 + p11[1] * wx2) >> 16) * wy2) >> 16;
//uint32_t r = (((p00[2] * wx1 + p01[2] * wx2) >> 16) * wy1 + ((p10[2] * wx1 + p11[2] * wx2) >> 16) * wy2) >> 16;
//uint32_t a = (((p00[3] * wx1 + p01[3] * wx2) >> 16) * wy1 + ((p10[3] * wx1 + p11[3] * wx2) >> 16) * wy2) >> 16;
uint32_t b = ((p00[0] * w1 + p01[0] * w2) + (p10[0] * w3 + p11[0] * w4)) >> 16;
uint32_t g = ((p00[1] * w1 + p01[1] * w2) + (p10[1] * w3 + p11[1] * w4)) >> 16;
uint32_t r = ((p00[2] * w1 + p01[2] * w2) + (p10[2] * w3 + p11[2] * w4)) >> 16;
uint32_t a = ((p00[3] * w1 + p01[3] * w2) + (p10[3] * w3 + p11[3] * w4)) >> 16;
return (a << 24) + (r << 16) + (g << 8) + b;
}
#endif
// Only for 32 bits images
//-------------------------------------
void
stretch_image(uint32_t *srcImage, uint32_t srcX, uint32_t srcY, uint32_t srcWidth, uint32_t srcHeight, uint32_t srcPitch,
uint32_t *dstImage, uint32_t dstX, uint32_t dstY, uint32_t dstWidth, uint32_t dstHeight, uint32_t dstPitch) {
uint32_t x, y;
uint32_t srcOffsetX, srcOffsetY;
if(srcImage == 0x0 || dstImage == 0x0)
return;
srcImage += srcX + srcY * srcPitch;
dstImage += dstX + dstY * dstPitch;
const uint32_t deltaX = (srcWidth << 16) / dstWidth;
const uint32_t deltaY = (srcHeight << 16) / dstHeight;
srcOffsetY = 0;
for(y=0; y<dstHeight; ++y) {
srcOffsetX = 0;
for(x=0; x<dstWidth; ++x) {
#if defined(kUseBilinearInterpolation)
dstImage[x] = interpolate(srcImage, x+srcX, y+srcY, srcOffsetX, srcOffsetY, srcWidth, srcHeight, srcPitch);
#else
dstImage[x] = srcImage[srcOffsetX >> 16];
#endif
srcOffsetX += deltaX;
}
srcOffsetY += deltaY;
if(srcOffsetY >= 0x10000) {
srcImage += (srcOffsetY >> 16) * srcPitch;
srcOffsetY &= 0xffff;
}
dstImage += dstPitch;
}
}
//-------------------------------------
void
calc_dst_factor(SWindowData *window_data, uint32_t width, uint32_t height) {
if (window_data->dst_width == 0) {
window_data->dst_width = width;
}
window_data->factor_x = (float) window_data->dst_offset_x / (float) width;
window_data->factor_width = (float) window_data->dst_width / (float) width;
if (window_data->dst_height == 0) {
window_data->dst_height = height;
}
window_data->factor_y = (float) window_data->dst_offset_y / (float) height;
window_data->factor_height = (float) window_data->dst_height / (float) height;
}
//-------------------------------------
void
resize_dst(SWindowData *window_data, uint32_t width, uint32_t height) {
window_data->dst_offset_x = (uint32_t) (width * window_data->factor_x);
window_data->dst_offset_y = (uint32_t) (height * window_data->factor_y);
window_data->dst_width = (uint32_t) (width * window_data->factor_width);
window_data->dst_height = (uint32_t) (height * window_data->factor_height);
}
#if !defined(USE_OPENGL_API) && !defined(USE_METAL_API)
//-------------------------------------
void
set_target_fps_aux() {
}
#endif

View File

@ -0,0 +1,27 @@
#pragma once
#include <MiniFB.h>
#include "WindowData.h"
#define kCall(func, ...) if(window_data && window_data->func) window_data->func((struct mfb_window *) window_data, __VA_ARGS__);
#define kUnused(var) (void) var;
typedef struct mfb_timer {
int64_t start_time;
int64_t delta_counter;
uint64_t time;
} mfb_timer;
#if defined(__cplusplus)
extern "C" {
#endif
extern short int g_keycodes[512];
void keyboard_default(struct mfb_window *window, mfb_key key, mfb_key_mod mod, bool isPressed);
void calc_dst_factor(SWindowData *window_data, uint32_t width, uint32_t height);
void resize_dst(SWindowData *window_data, uint32_t width, uint32_t height);
void set_target_fps_aux();
#if defined(__cplusplus)
}
#endif

View File

@ -0,0 +1,37 @@
#if defined(__linux__)
#include <time.h>
#include <MiniFB.h>
extern double g_timer_frequency;
extern double g_timer_resolution;
#define kClock CLOCK_MONOTONIC
//#define kClock CLOCK_REALTIME
uint64_t
mfb_timer_tick() {
struct timespec time;
if (clock_gettime(kClock, &time) != 0) {
return 0.0;
}
return time.tv_sec * 1e+9 + time.tv_nsec;
}
void
mfb_timer_init() {
struct timespec res;
if (clock_getres(kClock, &res) != 0) {
g_timer_frequency = 1e+9;
}
else {
g_timer_frequency = res.tv_sec + res.tv_nsec * 1e+9;
}
g_timer_resolution = 1.0 / g_timer_frequency;
}
#endif

View File

@ -0,0 +1,115 @@
#include <MiniFB.h>
#include "MiniFB_internal.h"
#include <stdlib.h>
//-------------------------------------
double g_timer_frequency;
double g_timer_resolution;
double g_time_for_frame = 1.0 / 60.0;
bool g_use_hardware_sync = false;
//-------------------------------------
extern uint64_t mfb_timer_tick(void);
extern void mfb_timer_init(void);
//-------------------------------------
void
mfb_set_target_fps(uint32_t fps) {
if(fps == 0) {
g_time_for_frame = 0;
}
else {
g_time_for_frame = 1.0 / fps;
}
set_target_fps_aux();
}
//-------------------------------------
unsigned
mfb_get_target_fps() {
if (g_time_for_frame == 0) {
return 0;
}
else {
return 1.0 / g_time_for_frame;
}
}
//-------------------------------------
struct mfb_timer *
mfb_timer_create() {
static int once = 1;
mfb_timer *tmr;
if(once) {
once = 0;
mfb_timer_init();
}
tmr = malloc(sizeof(mfb_timer));
mfb_timer_reset(tmr);
return tmr;
}
//-------------------------------------
void
mfb_timer_destroy(struct mfb_timer *tmr) {
if(tmr != 0x0) {
free(tmr);
}
}
//-------------------------------------
void
mfb_timer_reset(struct mfb_timer *tmr) {
if(tmr == 0x0)
return;
tmr->start_time = mfb_timer_tick();
tmr->delta_counter = tmr->start_time;
tmr->time = 0;
}
//-------------------------------------
double
mfb_timer_now(struct mfb_timer *tmr) {
uint64_t counter;
if(tmr == 0x0)
return 0.0;
counter = mfb_timer_tick();
tmr->time += (counter - tmr->start_time);
tmr->start_time = counter;
return tmr->time * g_timer_resolution;
}
//-------------------------------------
double
mfb_timer_delta(struct mfb_timer *tmr) {
int64_t counter;
uint64_t delta;
if(tmr == 0x0)
return 0.0;
counter = mfb_timer_tick();
delta = (counter - tmr->delta_counter);
tmr->delta_counter = counter;
return delta * g_timer_resolution;
}
//-------------------------------------
double
mfb_timer_get_frequency() {
return g_timer_frequency;
}
//-------------------------------------
double
mfb_timer_get_resolution() {
return g_timer_resolution;
}

View File

@ -0,0 +1,49 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <MiniFB_enums.h>
//-------------------------------------
typedef struct {
void *specific;
void *user_data;
mfb_active_func active_func;
mfb_resize_func resize_func;
mfb_keyboard_func keyboard_func;
mfb_char_input_func char_input_func;
mfb_mouse_button_func mouse_btn_func;
mfb_mouse_move_func mouse_move_func;
mfb_mouse_scroll_func mouse_wheel_func;
uint32_t window_width;
uint32_t window_height;
uint32_t dst_offset_x;
uint32_t dst_offset_y;
uint32_t dst_width;
uint32_t dst_height;
float factor_x;
float factor_y;
float factor_width;
float factor_height;
void *draw_buffer;
uint32_t buffer_width;
uint32_t buffer_height;
uint32_t buffer_stride;
int32_t mouse_pos_x;
int32_t mouse_pos_y;
float mouse_wheel_x;
float mouse_wheel_y;
uint8_t mouse_button_status[8];
uint8_t key_status[512];
uint32_t mod_keys;
bool is_active;
bool is_initialized;
bool close;
} SWindowData;

View File

@ -0,0 +1,473 @@
#include <android_native_app_glue.h>
#include <android/log.h>
#include <jni.h>
//--
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
//--
#include <MiniFB.h>
#include <WindowData.h>
#include "WindowData_Android.h"
#define LOG_TAG "MiniFB"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__)
#define kCall(func, ...) if(window_data && window_data->func) window_data->func((struct mfb_window *) window_data, __VA_ARGS__);
#define kUnused(var) (void) var;
struct android_app *gApplication;
//-------------------------------------
extern void
stretch_image(uint32_t *srcImage, uint32_t srcX, uint32_t srcY, uint32_t srcWidth, uint32_t srcHeight, uint32_t srcPitch,
uint32_t *dstImage, uint32_t dstX, uint32_t dstY, uint32_t dstWidth, uint32_t dstHeight, uint32_t dstPitch);
//-------------------------------------
extern int
main(int argc, char *argv[]);
//-------------------------------------
static void
draw(SWindowData *window_data, ANativeWindow_Buffer *window_buffer) {
if(window_data == 0x0 || window_data->draw_buffer == 0x0 || window_buffer == 0x0)
return;
if((window_data->buffer_width == window_buffer->width) && (window_data->buffer_height == window_buffer->height)) {
if(window_data->buffer_stride == window_buffer->stride*4) {
memcpy(window_buffer->bits, window_data->draw_buffer, window_data->buffer_width * window_data->buffer_height * 4);
}
else {
uint8_t *src = window_data->draw_buffer;
uint32_t *dst = window_buffer->bits;
for(uint32_t y=0; y<window_data->window_height; ++y) {
memcpy(dst, src, window_data->buffer_width * 4);
src += window_data->buffer_stride;
dst += window_buffer->stride;
}
}
}
else {
uint32_t *src = window_data->draw_buffer;
uint32_t *dst = window_buffer->bits;
stretch_image(
src, 0, 0, window_data->buffer_width, window_data->buffer_height, window_data->buffer_width,
dst, 0, 0, window_buffer->width, window_buffer->height, window_buffer->stride
);
}
}
//-------------------------------------
static int32_t
handle_input(struct android_app* app, AInputEvent* event) {
SWindowData *window_data = (SWindowData *) app->userData;
if (window_data->close) {
//destroy_window_data(window_data);
return 0;
}
SWindowData_Android *window_data_android = (SWindowData_Android *) window_data->specific;
int t = AInputEvent_getType(event);
int s = AInputEvent_getSource(event);
LOGV("Event: type= %d, source=%d", t, s);
if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
//if (AInputEvent_getSource(event) == AINPUT_SOURCE_TOUCHSCREEN) {
int action = AMotionEvent_getAction(event);
int type = action & AMOTION_EVENT_ACTION_MASK;
switch(type) {
case AMOTION_EVENT_ACTION_POINTER_DOWN:
case AMOTION_EVENT_ACTION_POINTER_UP:
{
int idx = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
int id = AMotionEvent_getPointerId(event, idx);
int x = AMotionEvent_getX(event, idx);
int y = AMotionEvent_getY(event, idx);
window_data->mouse_pos_x = x | (id << 28);
window_data->mouse_pos_y = y | (id << 28);
window_data->mouse_button_status[id & 0x07] = (action == AMOTION_EVENT_ACTION_POINTER_DOWN);
kCall(mouse_btn_func, id, 0, action == AMOTION_EVENT_ACTION_POINTER_DOWN);
}
break;
case AMOTION_EVENT_ACTION_DOWN:
case AMOTION_EVENT_ACTION_UP:
{
int count = AMotionEvent_getPointerCount(event);
for(int i=0; i < count; ++i) {
int id = AMotionEvent_getPointerId(event, i);
int x = AMotionEvent_getX(event, i);
int y = AMotionEvent_getY(event, i);
window_data->mouse_pos_x = x | (id << 28);
window_data->mouse_pos_y = y | (id << 28);
window_data->mouse_button_status[id & 0x07] = (action == AMOTION_EVENT_ACTION_POINTER_DOWN);
kCall(mouse_btn_func, id, 0, action == AMOTION_EVENT_ACTION_DOWN);
}
}
break;
case AMOTION_EVENT_ACTION_MOVE:
{
int count = AMotionEvent_getPointerCount(event);
for(int i=0; i < count; ++i){
int id = AMotionEvent_getPointerId(event, i);
int x = AMotionEvent_getX(event, i);
int y = AMotionEvent_getY(event, i);
window_data->mouse_pos_x = x | (id << 28);
window_data->mouse_pos_y = y | (id << 28);
window_data->mouse_button_status[id & 0x07] = true;
kCall(mouse_move_func, window_data->mouse_pos_x, window_data->mouse_pos_y);
}
}
break;
default:
LOGV("Touch: event: action=%x, source=%x, type=%d", action, s, t);
break;
}
if(window_data != 0x0) {
window_data->is_active = true;
}
return 1;
}
else
if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) {
LOGV("Key event: action=%d keyCode=%d metaState=0x%x",
AKeyEvent_getAction(event),
AKeyEvent_getKeyCode(event),
AKeyEvent_getMetaState(event));
}
return 0;
}
//-------------------------------------
static void
handle_cmd(struct android_app* app, int32_t cmd) {
static int32_t format = WINDOW_FORMAT_RGBX_8888;
static int sCurrentState = -1;
sCurrentState = cmd;
SWindowData *window_data;
SWindowData_Android *window_data_android;
window_data = (SWindowData *) app->userData;
if(window_data != 0x0) {
window_data_android = (SWindowData_Android *) window_data->specific;
}
LOGV("cmd: %d", cmd);
// Init: 10, 11, 0, 1, 3, 5, 4, 6
// START, RESUME, INPUT_CHANGED, INIT_WINDOW, WINDOW_RESIZED, CONTENT_RECT_CHANGED, WINDOW_REDRAW_NEEDED, GAINED_FOCUS
// Pause: 13, 7, 2, 14, 12
// PAUSE, LOST_FOCUS, TERM_WINDOW, STOP, SAVE_STATE
// Resume: 10, 11, 1, 3, 4, 6
// START, RESUME, INIT_WINDOW, WINDOW_RESIZED, WINDOW_REDRAW_NEEDED, GAINED_FOCUS
// Close: 0, 15
// INPUT_CHANGED, DESTROY
// Lower the shutter: 7, 0
// LOST_FOCUS, INPUT_CHANGED
// Raising the shutter: 6, 1
// GAINED_FOCUS, INIT_WINDOW
// Rotate: 13, 2, 14, 12, 0, 15, 10, 11, 0, 1, 3, 5, 4, 6, 4
// PAUSE, TERM_WINDOW, STOP, SAVE_STATE, (similar to Pause but LOST_FOCUS)
// INPUT_CHANGED, DESTROY, (like Close)
// START, RESUME, INPUT_CHANGED, INIT_WINDOW, WINDOW_RESIZED, CONTENT_RECT_CHANGED, WINDOW_REDRAW_NEEDED, GAINED_FOCUS (like Init)
switch (cmd) {
// The app's activity has been started.
case APP_CMD_START:
break;
// The app's activity has been resumed.
case APP_CMD_RESUME:
break;
// The AInputQueue has changed.
// Upon processing this command, android_app->inputQueue will be updated to the new queue (or NULL).
case APP_CMD_INPUT_CHANGED:
break;
// A new ANativeWindow is ready for use.
// Upon receiving this command, android_app->window will contain the new window surface.
case APP_CMD_INIT_WINDOW:
if (app->window != NULL) {
format = ANativeWindow_getFormat(app->window);
ANativeWindow_setBuffersGeometry(app->window,
ANativeWindow_getWidth(app->window),
ANativeWindow_getHeight(app->window),
format
);
//engine_draw_frame(window_data_android);
}
break;
// The current ANativeWindow has been resized. Please redraw with its new size.
case APP_CMD_WINDOW_RESIZED:
break;
// The content area of the window has changed, such as from the soft input window being shown or hidden.
// You can find the new content rect in android_app::contentRect.
case APP_CMD_CONTENT_RECT_CHANGED:
if(window_data_android != 0x0) {
// This does not work
//int32_t width = window_data_android->app->contentRect.right - window_data_android->app->contentRect.left;
//int32_t height = window_data_android->app->contentRect.bottom - window_data_android->app->contentRect.top;
// TODO: Check the DPI?
if(window_data != 0x0) {
window_data->window_width = ANativeWindow_getWidth(app->window);
window_data->window_height = ANativeWindow_getHeight(app->window);
kCall(resize_func, window_data->window_width, window_data->window_height);
}
}
break;
// The system needs that the current ANativeWindow be redrawn.
// You should redraw the window before handing this to android_app_exec_cmd() in order to avoid transient drawing glitches.
case APP_CMD_WINDOW_REDRAW_NEEDED:
break;
// The app's activity window has gained input focus.
case APP_CMD_GAINED_FOCUS:
if(window_data != 0x0) {
window_data->is_active = true;
}
kCall(active_func, true);
break;
// The app's activity has been paused.
case APP_CMD_PAUSE:
break;
// The app's activity window has lost input focus.
case APP_CMD_LOST_FOCUS:
if(window_data != 0x0) {
window_data->is_active = true;
//engine_draw_frame(window_data_android);
}
kCall(active_func, false);
break;
// The existing ANativeWindow needs to be terminated.
// Upon receiving this command, android_app->window still contains the existing window;
// after calling android_app_exec_cmd it will be set to NULL.
case APP_CMD_TERM_WINDOW:
if(window_data != 0x0) {
window_data->is_active = false;
}
ANativeWindow_setBuffersGeometry(app->window,
ANativeWindow_getWidth(app->window),
ANativeWindow_getHeight(app->window),
format
);
break;
// The app's activity has been stopped.
case APP_CMD_STOP:
break;
// The app should generate a new saved state for itself, to restore from later if needed.
// If you have saved state, allocate it with malloc and place it in android_app.savedState with
// the size in android_app.savedStateSize.
// The will be freed for you later.
case APP_CMD_SAVE_STATE:
break;
// The app's activity is being destroyed, and waiting for the app thread to clean up and exit before proceeding.
case APP_CMD_DESTROY:
if(window_data != 0x0) {
window_data->close = true;
}
break;
// The system is running low on memory. Try to reduce your memory use.
case APP_CMD_LOW_MEMORY:
break;
// The current device configuration has changed.
case APP_CMD_CONFIG_CHANGED:
break;
}
}
//-------------------------------------
void
android_main(struct android_app* app) {
app->onAppCmd = handle_cmd;
app->onInputEvent = handle_input;
gApplication = app;
// Read all pending events.
int ident;
int events;
struct android_poll_source* source;
while(app->window == 0x0) {
while ((ident = ALooper_pollAll(0, NULL, &events, (void **) &source)) >= 0) {
// Process this event.
if (source != NULL) {
source->process(app, source);
}
// Check if we are exiting.
if (app->destroyRequested != 0) {
LOGD("Engine thread destroy requested!");
return;
}
}
}
char cwd[1024];
getcwd(cwd, sizeof(cwd));
char *argv[] = {
cwd,
(char *) app
};
main(2, argv);
}
//-------------------------------------
struct mfb_window *
mfb_open_ex(const char *title, unsigned width, unsigned height, unsigned flags) {
kUnused(title);
kUnused(flags);
SWindowData *window_data = malloc(sizeof(SWindowData));
if (window_data == 0x0) {
return 0x0;
}
memset(window_data, 0, sizeof(SWindowData));
SWindowData_Android *window_data_android = malloc(sizeof(SWindowData_Android));
if(window_data_android == 0x0) {
free(window_data);
return 0x0;
}
memset(window_data_android, 0, sizeof(SWindowData_Android));
window_data->specific = window_data_android;
window_data->is_active = true;
window_data_android->app = gApplication;
window_data_android->timer = mfb_timer_create();
window_data->buffer_width = width;
window_data->buffer_height = height;
window_data->buffer_stride = width * 4;
gApplication->userData = window_data;
if(gApplication->window != 0x0) {
window_data->window_width = ANativeWindow_getWidth(gApplication->window);
window_data->window_height = ANativeWindow_getHeight(gApplication->window);
}
window_data->is_initialized = true;
return (struct mfb_window *) window_data;
}
//-------------------------------------
mfb_update_state
mfb_update_ex(struct mfb_window *window, void *buffer, unsigned width, unsigned height) {
if (window == 0x0) {
return STATE_INVALID_WINDOW;
}
SWindowData *window_data = (SWindowData *) window;
if (window_data->close) {
//destroy_window_data(window_data);
return STATE_EXIT;
}
if (buffer == 0x0) {
return STATE_INVALID_BUFFER;
}
window_data->draw_buffer = buffer;
window_data->buffer_width = width;
window_data->buffer_stride = width * 4;
window_data->buffer_height = height;
SWindowData_Android *window_data_android = (SWindowData_Android *) window_data->specific;
ANativeWindow_Buffer native_buffer;
if (ANativeWindow_lock(window_data_android->app->window, &native_buffer, NULL) < 0) {
LOGE("Unable to lock window buffer");
return STATE_INTERNAL_ERROR;
}
draw(window_data, &native_buffer);
ANativeWindow_unlockAndPost(window_data_android->app->window);
return STATE_OK;
}
//-------------------------------------
extern double g_time_for_frame;
bool
mfb_wait_sync(struct mfb_window *window) {
if (window == 0x0) {
return false;
}
SWindowData *window_data = (SWindowData *) window;
if (window_data->close) {
//destroy_window_data(window_data);
return false;
}
SWindowData_Android *window_data_android = (SWindowData_Android *) window_data->specific;
// Read all pending events.
int ident;
int events;
struct android_poll_source *source;
double current;
while(1) {
// If not animating, we will block forever waiting for events.
// If animating, we loop until all events are read, then continue
// to draw the next frame of animation.
while ((ident = ALooper_pollAll(window_data->is_active ? 0 : -1, NULL, &events, (void **) &source)) >= 0) {
// Process this event.
if (source != NULL) {
source->process(window_data_android->app, source);
}
// Check if we are exiting.
if (window_data_android->app->destroyRequested != 0) {
LOGD("Engine thread destroy requested!");
window_data->is_active = false;
window_data->close = true;
return false;
}
}
current = mfb_timer_now(window_data_android->timer);
if (current >= g_time_for_frame) {
break;
}
}
mfb_timer_reset(window_data_android->timer);
return true;
}
//-------------------------------------
void
mfb_get_monitor_scale(struct mfb_window *window, float *scale_x, float *scale_y) {
kUnused(window);
if(scale_x != 0x0) {
*scale_x = 1.0f;
}
if(scale_y != 0x0) {
*scale_y = 1.0f;
}
}

View File

@ -0,0 +1,9 @@
#pragma once
#include <MiniFB_internal.h>
//-------------------------------------
typedef struct {
struct android_app *app;
struct mfb_timer *timer;
} SWindowData_Android;

View File

@ -0,0 +1,368 @@
#if defined(USE_OPENGL_API)
#include "MiniFB_GL.h"
#include "MiniFB_internal.h"
#if defined(_WIN32) || defined(WIN32)
#include <windows/WindowData_Win.h>
#include <gl/gl.h>
#elif defined(linux)
#include <x11/WindowData_X11.h>
#include <GL/gl.h>
#include <GL/glx.h>
#endif
#include <stdio.h>
#include <stdlib.h>
//#define kUse_Clean_UP
#if defined(kUse_Clean_UP)
#define UseCleanUp(x) x
#else
#define UseCleanUp(x)
#endif
extern double g_time_for_frame;
extern bool g_use_hardware_sync;
//-------------------------------------
#if defined(_WIN32) || defined(WIN32)
bool
setup_pixel_format(HDC hDC) {
int pixelFormat;
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // size
1, // version
PFD_SUPPORT_OPENGL | //
PFD_DRAW_TO_WINDOW | //
PFD_DOUBLEBUFFER, // support double-buffering
PFD_TYPE_RGBA, // color type
24, // preferred color depth
0, 0, 0, 0, 0, 0, // color and shift bits (ignored)
0, // no alpha buffer
0, // alpha bits (ignored)
0, // no accumulation buffer
0, 0, 0, 0, // accum bits (ignored)
24, // depth buffer
8, // no stencil buffer
0, // no auxiliary buffers
PFD_MAIN_PLANE, // main layer
0, // reserved
0, 0, 0, // no layer, visible, damage masks
};
pixelFormat = ChoosePixelFormat(hDC, &pfd);
if (pixelFormat == 0) {
MessageBox(WindowFromDC(hDC), "ChoosePixelFormat failed.", "Error", MB_ICONERROR | MB_OK);
return false;
}
if (SetPixelFormat(hDC, pixelFormat, &pfd) != TRUE) {
MessageBox(WindowFromDC(hDC), "SetPixelFormat failed.", "Error", MB_ICONERROR | MB_OK);
return false;
}
return true;
}
typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int);
typedef int (WINAPI * PFNWGLGETSWAPINTERVALEXTPROC)(void);
PFNWGLSWAPINTERVALEXTPROC SwapIntervalEXT = 0x0;
PFNWGLGETSWAPINTERVALEXTPROC GetSwapIntervalEXT = 0x0;
#elif defined(linux)
bool
setup_pixel_format(SWindowData_X11 *window_data_x11) {
GLint glxAttribs[] = {
GLX_RGBA,
GLX_DOUBLEBUFFER,
GLX_DEPTH_SIZE, 24,
GLX_STENCIL_SIZE, 8,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_DEPTH_SIZE, 24,
GLX_STENCIL_SIZE, 8,
GLX_SAMPLE_BUFFERS, 0,
GLX_SAMPLES, 0,
None
};
XVisualInfo* visualInfo = glXChooseVisual(window_data_x11->display, window_data_x11->screen, glxAttribs);
if (visualInfo == 0) {
fprintf(stderr, "Could not create correct visual window.\n");
XCloseDisplay(window_data_x11->display);
return false;
}
window_data_x11->context = glXCreateContext(window_data_x11->display, visualInfo, NULL, GL_TRUE);
return true;
}
typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int);
PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT = 0x0;
#endif
//-------------------------------------
bool
create_GL_context(SWindowData *window_data) {
#if defined(_WIN32) || defined(WIN32)
SWindowData_Win *window_data_win = (SWindowData_Win *) window_data->specific;
if (setup_pixel_format(window_data_win->hdc) == false)
return false;
window_data_win->hGLRC = wglCreateContext(window_data_win->hdc);
wglMakeCurrent(window_data_win->hdc, window_data_win->hGLRC);
init_GL(window_data);
SwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress("wglSwapIntervalEXT");
GetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC) wglGetProcAddress("wglGetSwapIntervalEXT");
set_target_fps_aux();
return true;
#elif defined(linux)
SWindowData_X11 *window_data_x11 = (SWindowData_X11 *) window_data->specific;
GLint majorGLX, minorGLX = 0;
glXQueryVersion(window_data_x11->display, &majorGLX, &minorGLX);
if (majorGLX <= 1 && minorGLX < 2) {
fprintf(stderr, "GLX 1.2 or greater is required.\n");
XCloseDisplay(window_data_x11->display);
return false;
}
else {
//fprintf(stdout, "GLX version: %d.%d\n", majorGLX, minorGLX);
}
if (setup_pixel_format(window_data_x11) == false)
return false;
glXMakeCurrent(window_data_x11->display, window_data_x11->window, window_data_x11->context);
//fprintf(stdout, "GL Vendor: %s\n", glGetString(GL_VENDOR));
//fprintf(stdout, "GL Renderer: %s\n", glGetString(GL_RENDERER));
//fprintf(stdout, "GL Version: %s\n", glGetString(GL_VERSION));
//fprintf(stdout, "GL Shading Language: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
init_GL(window_data);
SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) glXGetProcAddress("glXSwapIntervalEXT");
set_target_fps_aux();
return true;
#endif
}
//-------------------------------------
void
destroy_GL_context(SWindowData *window_data) {
#if defined(_WIN32) || defined(WIN32)
SWindowData_Win *window_data_win = (SWindowData_Win *) window_data->specific;
if (window_data_win->hGLRC) {
wglMakeCurrent(NULL, NULL);
wglDeleteContext(window_data_win->hGLRC);
window_data_win->hGLRC = 0;
}
#elif defined(linux)
SWindowData_X11 *window_data_x11 = (SWindowData_X11 *) window_data->specific;
glXDestroyContext(window_data_x11->display, window_data_x11->context);
#endif
}
//-------------------------------------
#if defined(RGB)
#undef RGB
#endif
#define TEXTURE0 0x84C0 // [ Core in gl 1.3, gles1 1.0, gles2 2.0, glsc2 2.0, Provided by GL_ARB_multitexture (gl) ]
#define RGB 0x1907 // [ Core in gl 1.0, gles1 1.0, gles2 2.0, glsc2 2.0 ]
#define RGBA 0x1908 // [ Core in gl 1.0, gles1 1.0, gles2 2.0, glsc2 2.0 ]
#define BGR 0x80E0 // [ Core in gl 1.2 ]
#define BGRA 0x80E1 // [ Core in gl 1.2, Provided by GL_ARB_vertex_array_bgra (gl|glcore) ]
//-------------------------------------
void
init_GL(SWindowData *window_data) {
#if defined(_WIN32) || defined(WIN32)
SWindowData_Win *window_data_ex = (SWindowData_Win *) window_data->specific;
#elif defined(linux)
SWindowData_X11 *window_data_ex = (SWindowData_X11 *) window_data->specific;
#endif
glViewport(0, 0, window_data->window_width, window_data->window_height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, window_data->window_width, window_data->window_height, 0, 2048, -2048);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &window_data_ex->text_id);
//glActiveTexture(TEXTURE0);
glBindTexture(GL_TEXTURE_2D, window_data_ex->text_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
UseCleanUp(glDisableClientState(GL_TEXTURE_COORD_ARRAY));
UseCleanUp(glDisableClientState(GL_VERTEX_ARRAY));
UseCleanUp(glBindTexture(GL_TEXTURE_2D, 0));
}
//-------------------------------------
void
resize_GL(SWindowData *window_data) {
if (window_data->is_initialized) {
#if defined(_WIN32) || defined(WIN32)
SWindowData_Win *window_data_ex = (SWindowData_Win *) window_data->specific;
wglMakeCurrent(window_data_ex->hdc, window_data_ex->hGLRC);
#elif defined(linux)
SWindowData_X11 *window_data_ex = (SWindowData_X11 *) window_data->specific;
glXMakeCurrent(window_data_ex->display, window_data_ex->window, window_data_ex->context);
#endif
glViewport(0, 0, window_data->window_width, window_data->window_height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, window_data->window_width, window_data->window_height, 0, 2048, -2048);
glClear(GL_COLOR_BUFFER_BIT);
}
}
//-------------------------------------
void
redraw_GL(SWindowData *window_data, const void *pixels) {
#if defined(_WIN32) || defined(WIN32)
SWindowData_Win *window_data_ex = (SWindowData_Win *) window_data->specific;
GLenum format = BGRA;
wglMakeCurrent(window_data_ex->hdc, window_data_ex->hGLRC);
#elif defined(linux)
SWindowData_X11 *window_data_ex = (SWindowData_X11 *) window_data->specific;
GLenum format = BGRA;
glXMakeCurrent(window_data_ex->display, window_data_ex->window, window_data_ex->context);
#endif
float x, y, w, h;
x = (float) window_data->dst_offset_x;
y = (float) window_data->dst_offset_y;
w = (float) window_data->dst_offset_x + window_data->dst_width;
h = (float) window_data->dst_offset_y + window_data->dst_height;
float vertices[] = {
x, y,
0, 0,
w, y,
1, 0,
x, h,
0, 1,
w, h,
1, 1,
};
glClear(GL_COLOR_BUFFER_BIT);
UseCleanUp(glBindTexture(GL_TEXTURE_2D, window_data_ex->text_id));
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, window_data->buffer_width, window_data->buffer_height, 0, format, GL_UNSIGNED_BYTE, pixels);
//glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, window_data->buffer_width, window_data->buffer_height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
UseCleanUp(glEnableClientState(GL_VERTEX_ARRAY));
UseCleanUp(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
glVertexPointer(2, GL_FLOAT, 4 * sizeof(float), vertices);
glTexCoordPointer(2, GL_FLOAT, 4 * sizeof(float), vertices + 2);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
UseCleanUp(glDisableClientState(GL_TEXTURE_COORD_ARRAY));
UseCleanUp(glDisableClientState(GL_VERTEX_ARRAY));
UseCleanUp(glBindTexture(GL_TEXTURE_2D, 0));
#if defined(_WIN32) || defined(WIN32)
SwapBuffers(window_data_ex->hdc);
#elif defined(linux)
glXSwapBuffers(window_data_ex->display, window_data_ex->window);
#endif
}
//-------------------------------------
void
set_target_fps_aux() {
// Assuming the monitor refresh rate is 60 hz
int interval = (int) ((60.0 * g_time_for_frame) + 0.5);
#if defined(_WIN32) || defined(WIN32)
if (SwapIntervalEXT != 0x0) {
bool success = SwapIntervalEXT(interval);
if (GetSwapIntervalEXT != 0x0) {
int currentInterval = GetSwapIntervalEXT();
if (interval != currentInterval) {
fprintf(stderr, "Cannot set target swap interval. Current swap interval is %d\n", currentInterval);
}
}
else if (success == false) {
fprintf(stderr, "Cannot set target swap interval.\n");
}
g_use_hardware_sync = true;
}
#elif defined(linux)
#define kGLX_SWAP_INTERVAL_EXT 0x20F1
#define kGLX_MAX_SWAP_INTERVAL_EXT 0x20F2
if (SwapIntervalEXT != 0x0) {
Display *dpy = glXGetCurrentDisplay();
GLXDrawable drawable = glXGetCurrentDrawable();
unsigned int currentInterval, maxInterval;
SwapIntervalEXT(dpy, drawable, interval);
glXQueryDrawable(dpy, drawable, kGLX_SWAP_INTERVAL_EXT, &currentInterval);
if (interval != currentInterval) {
glXQueryDrawable(dpy, drawable, kGLX_MAX_SWAP_INTERVAL_EXT, &maxInterval);
fprintf(stderr, "Cannot set target swap interval. Current swap interval is %d (max: %d)\n", currentInterval, maxInterval);
}
g_use_hardware_sync = true;
}
#endif
}
#endif

View File

@ -0,0 +1,13 @@
#pragma once
#if defined(USE_OPENGL_API)
#include <WindowData.h>
bool create_GL_context(SWindowData *window_data);
void destroy_GL_context(SWindowData *window_data);
void init_GL(SWindowData *window_data);
void redraw_GL(SWindowData *window_data, const void *pixels);
void resize_GL(SWindowData *window_data);
#endif

View File

@ -0,0 +1,16 @@
#pragma once
#include <MiniFB_enums.h>
#include <WindowData.h>
#include <MetalKit/MetalKit.h>
@class iOSViewDelegate;
typedef struct Vertex {
float x, y, z, w;
} Vertex;
typedef struct {
iOSViewDelegate *view_delegate;
Vertex vertices[4];
} SWindowData_IOS;

View File

@ -0,0 +1,272 @@
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#include <mach/mach_time.h>
#include "iOSViewController.h"
#include "iOSViewDelegate.h"
#include "WindowData_IOS.h"
#include <MiniFB.h>
#include <MiniFB_internal.h>
#include <WindowData.h>
//-------------------------------------
SWindowData *
create_window_data(unsigned width, unsigned height) {
SWindowData *window_data;
window_data = malloc(sizeof(SWindowData));
if(window_data == 0x0) {
NSLog(@"Cannot allocate window data");
return 0x0;
}
memset(window_data, 0, sizeof(SWindowData));
SWindowData_IOS *window_data_ios = malloc(sizeof(SWindowData_IOS));
if(window_data_ios == 0x0) {
free(window_data);
NSLog(@"Cannot allocate ios window data");
return 0x0;
}
memset((void *) window_data_ios, 0, sizeof(SWindowData_IOS));
window_data->specific = window_data_ios;
float scale = [UIScreen mainScreen].scale;
window_data->window_width = [UIScreen mainScreen].bounds.size.width * scale;
window_data->window_height = [UIScreen mainScreen].bounds.size.height * scale;
calc_dst_factor(window_data, width, height);
window_data->buffer_width = width;
window_data->buffer_height = height;
window_data->buffer_stride = width * 4;
window_data->draw_buffer = malloc(width * height * 4);
if (!window_data->draw_buffer) {
free(window_data_ios);
free(window_data);
NSLog(@"Unable to create draw buffer");
return 0x0;
}
return window_data;
}
//-------------------------------------
struct mfb_window *
mfb_open_ex(const char *title, unsigned width, unsigned height, unsigned flags) {
UIWindow *window;
NSArray *windows;
size_t numWindows;
kUnused(title);
kUnused(flags);
@autoreleasepool {
SWindowData *window_data = create_window_data(width, height);
if (window_data == 0x0) {
return 0x0;
}
windows = [[UIApplication sharedApplication] windows];
numWindows = [windows count];
if(numWindows > 0) {
window = [windows objectAtIndex:0];
}
else {
// Notice that you need to set "Launch Screen File" in:
// project > executable > general
// to get the real size with [UIScreen mainScreen].bounds].
window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
NSLog(@"UIApplication has no window. We create one (%f, %f).", [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
}
if([window.rootViewController isKindOfClass:[iOSViewController class]] == false) {
iOSViewController *controller = [[iOSViewController alloc] initWithWindowData:window_data];
[window setRootViewController:controller];
#if !__has_feature(objc_arc)
[controller release];
#endif
controller = (iOSViewController *) window.rootViewController;
}
else {
((iOSViewController *) window.rootViewController)->window_data = window_data;
}
[window makeKeyAndVisible];
window_data->is_initialized = true;
return (struct mfb_window *) window_data;
}
}
//-------------------------------------
static void
destroy_window_data(SWindowData *window_data) {
if(window_data == 0x0)
return;
@autoreleasepool {
SWindowData_IOS *window_data_ios = (SWindowData_IOS *) window_data->specific;
if(window_data_ios != 0x0) {
memset((void *) window_data_ios, 0, sizeof(SWindowData_IOS));
free(window_data_ios);
}
memset(window_data, 0, sizeof(SWindowData));
free(window_data);
}
}
//-------------------------------------
mfb_update_state
mfb_update_ex(struct mfb_window *window, void *buffer, unsigned width, unsigned height) {
if(window == 0x0) {
return STATE_INVALID_WINDOW;
}
SWindowData *window_data = (SWindowData *) window;
if(window_data->close) {
destroy_window_data(window_data);
return STATE_EXIT;
}
if(buffer == 0x0) {
return STATE_INVALID_BUFFER;
}
SWindowData_IOS *window_data_ios = (SWindowData_IOS *) window_data->specific;
if(window_data->buffer_width != width || window_data->buffer_height != height) {
window_data->buffer_width = width;
window_data->buffer_stride = width * 4;
window_data->buffer_height = height;
window_data->draw_buffer = realloc(window_data->draw_buffer, window_data->buffer_stride * window_data->buffer_height);
[window_data_ios->view_delegate resizeTextures];
}
memcpy(window_data->draw_buffer, buffer, window_data->buffer_width * window_data->buffer_height * 4);
return STATE_OK;
}
//-------------------------------------
mfb_update_state
mfb_update_events(struct mfb_window *window) {
if(window == 0x0) {
return STATE_INVALID_WINDOW;
}
return STATE_OK;
}
//-------------------------------------
extern double g_time_for_frame;
bool
mfb_wait_sync(struct mfb_window *window) {
if(window == 0x0) {
return false;
}
return true;
}
//-------------------------------------
bool
mfb_set_viewport(struct mfb_window *window, unsigned offset_x, unsigned offset_y, unsigned width, unsigned height) {
if(window == 0x0) {
return false;
}
SWindowData *window_data = (SWindowData *) window;
if(offset_x + width > window_data->window_width) {
return false;
}
if(offset_y + height > window_data->window_height) {
return false;
}
window_data->dst_offset_x = offset_x;
window_data->dst_offset_y = offset_y;
window_data->dst_width = width;
window_data->dst_height = height;
calc_dst_factor(window_data, window_data->window_width, window_data->window_height);
float x1 = ((float) offset_x / window_data->window_width) * 2.0f - 1.0f;
float x2 = (((float) offset_x + width) / window_data->window_width) * 2.0f - 1.0f;
float y1 = ((float) offset_y / window_data->window_height) * 2.0f - 1.0f;
float y2 = (((float) offset_y + height) / window_data->window_height) * 2.0f - 1.0f;
SWindowData_IOS *window_data_ios = (SWindowData_IOS *) window_data->specific;
window_data_ios->vertices[0].x = x1;
window_data_ios->vertices[0].y = y1;
window_data_ios->vertices[1].x = x1;
window_data_ios->vertices[1].y = y2;
window_data_ios->vertices[2].x = x2;
window_data_ios->vertices[2].y = y1;
window_data_ios->vertices[3].x = x2;
window_data_ios->vertices[3].y = y2;
return true;
}
//-------------------------------------
extern double g_timer_frequency;
extern double g_timer_resolution;
uint64_t
mfb_timer_tick() {
static mach_timebase_info_data_t timebase = { 0 };
if (timebase.denom == 0) {
(void) mach_timebase_info(&timebase);
}
uint64_t time = mach_absolute_time();
//return (time * s_timebase_info.numer) / s_timebase_info.denom;
// Perform the arithmetic at 128-bit precision to avoid the overflow!
uint64_t high = (time >> 32) * timebase.numer;
uint64_t highRem = ((high % timebase.denom) << 32) / timebase.denom;
uint64_t low = (time & 0xFFFFFFFFull) * timebase.numer / timebase.denom;
high /= timebase.denom;
return (high << 32) + highRem + low;
}
//-------------------------------------
void
mfb_timer_init() {
g_timer_frequency = 1e+9;
g_timer_resolution = 1.0 / g_timer_frequency;
}
//-------------------------------------
void
mfb_get_monitor_scale(struct mfb_window *window, float *scale_x, float *scale_y) {
(void) window;
float scale = 1.0f;
scale = [[UIScreen mainScreen] scale];
if (scale_x) {
*scale_x = scale;
if(*scale_x == 0) {
*scale_x = 1;
}
}
if (scale_y) {
*scale_y = scale;
if (*scale_y == 0) {
*scale_y = 1;
}
}
}

View File

@ -0,0 +1,9 @@
#import <MetalKit/MetalKit.h>
#include "WindowData.h"
@interface iOSView : MTKView
{
@public SWindowData *window_data;
}
@end

View File

@ -0,0 +1,84 @@
#include "iOSView.h"
#include <MiniFB_internal.h>
//-------------------------------------
@implementation iOSView
//-------------------------------------
- (BOOL) canBecomeFirstResponder {
return YES;
}
//-------------------------------------
- (void) touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
kUnused(event);
if(window_data != 0x0) {
CGPoint point;
int buttonNumber = MOUSE_BTN_0;
for(UITouch *touch in touches) {
point = [touch locationInView:self];
window_data->mouse_pos_x = point.x;
window_data->mouse_pos_y = point.y;
window_data->mouse_button_status[buttonNumber & 0x07] = true;
kCall(mouse_btn_func, buttonNumber, 0, true);
++buttonNumber;
}
}
}
//-------------------------------------
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
kUnused(event);
if(window_data != 0x0) {
CGPoint point;
int buttonNumber = MOUSE_BTN_0;
for(UITouch *touch in touches) {
point = [touch locationInView:self];
window_data->mouse_pos_x = point.x;
window_data->mouse_pos_y = point.y;
window_data->mouse_button_status[buttonNumber & 0x07] = true;
kCall(mouse_move_func, point.x, point.y);
++buttonNumber;
}
}
}
//-------------------------------------
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
kUnused(event);
if(window_data != 0x0) {
CGPoint point;
int buttonNumber = MOUSE_BTN_0;
for(UITouch *touch in touches) {
point = [touch locationInView:self];
window_data->mouse_pos_x = point.x;
window_data->mouse_pos_y = point.y;
window_data->mouse_button_status[buttonNumber & 0x07] = false;
kCall(mouse_btn_func, buttonNumber, 0, false);
++buttonNumber;
}
}
}
//-------------------------------------
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
kUnused(event);
if(window_data != 0x0) {
CGPoint point;
int buttonNumber = MOUSE_BTN_0;
for(UITouch *touch in touches) {
point = [touch locationInView:self];
window_data->mouse_pos_x = point.x;
window_data->mouse_pos_y = point.y;
window_data->mouse_button_status[buttonNumber & 0x07] = false;
kCall(mouse_btn_func, buttonNumber, 0, false);
++buttonNumber;
}
}
}
@end

View File

@ -0,0 +1,19 @@
//
// iOSViewController.h
// MiniFB
//
// Created by Carlos Aragones on 22/04/2020.
// Copyright © 2020 Carlos Aragones. All rights reserved.
//
#import <UIKit/UIKit.h>
#include "WindowData.h"
@interface iOSViewController : UIViewController
{
@public SWindowData *window_data;
}
- (id) initWithWindowData:(SWindowData *) windowData;
@end

View File

@ -0,0 +1,70 @@
//
// iOSViewController.m
// MiniFB
//
// Created by Carlos Aragones on 22/04/2020.
// Copyright © 2020 Carlos Aragones. All rights reserved.
//
#import <Metal/Metal.h>
#import <MetalKit/MetalKit.h>
#import "iOSViewController.h"
#import "iOSViewDelegate.h"
#import "iOSView.h"
#include "WindowData_IOS.h"
//-------------------------------------
@implementation iOSViewController
{
iOSView *metal_view;
//iOSViewDelegate *view_delegate;
}
//-------------------------------------
- (id) initWithWindowData:(SWindowData *) windowData {
self = [super init];
if (self) {
window_data = windowData;
}
return self;
}
//-------------------------------------
- (void) loadView {
iOSView *view = [[iOSView alloc] initWithFrame:[UIScreen mainScreen].bounds];
// Probably the window was created automatically by an storyboard or similar
if(window_data == 0x0) {
NSLog(@"WindowData is null!");
}
view->window_data = window_data;
view.userInteractionEnabled = true;
[self setView:view];
#if !__has_feature(objc_arc)
[view release];
#endif
}
//-------------------------------------
- (void) viewDidLoad
{
[super viewDidLoad];
metal_view = (iOSView *) self.view;
metal_view.device = MTLCreateSystemDefaultDevice();
metal_view.backgroundColor = UIColor.blackColor;
if(!metal_view.device) {
NSLog(@"Metal is not supported on this device");
self.view = [[UIView alloc] initWithFrame:self.view.frame];
return;
}
SWindowData_IOS *window_data_ios = (SWindowData_IOS *) window_data->specific;
window_data_ios->view_delegate = [[iOSViewDelegate alloc] initWithMetalKitView:metal_view windowData:window_data];
[window_data_ios->view_delegate mtkView:metal_view drawableSizeWillChange:metal_view.bounds.size];
metal_view.delegate = window_data_ios->view_delegate;
}
@end

View File

@ -0,0 +1,23 @@
//
// Renderer.h
// MiniFB
//
// Created by Carlos Aragones on 22/04/2020.
// Copyright © 2020 Carlos Aragones. All rights reserved.
//
#import <MetalKit/MetalKit.h>
#include "WindowData.h"
// Our platform independent renderer class.
// Implements the MTKViewDelegate protocol which allows it to accept per-frame
// update and drawable resize callbacks.
@interface iOSViewDelegate : NSObject <MTKViewDelegate>
{
}
-(nonnull instancetype) initWithMetalKitView:(nonnull MTKView *) view windowData:(nonnull SWindowData *) windowData;
- (void) resizeTextures;
@end

View File

@ -0,0 +1,254 @@
//
// Renderer.m
// MiniFB
//
// Created by Carlos Aragones on 22/04/2020.
// Copyright © 2020 Carlos Aragones. All rights reserved.
//
#import <simd/simd.h>
#import <ModelIO/ModelIO.h>
#import "iOSViewDelegate.h"
#include "WindowData_IOS.h"
#include <MiniFB.h>
#include <MiniFB_ios.h>
#include <MiniFB_internal.h>
//-------------------------------------
#define kShader(inc, src) @inc#src
//-------------------------------------
enum { MaxBuffersInFlight = 3 }; // Number of textures in flight (tripple buffered)
//--
NSString *g_shader_src = kShader(
"#include <metal_stdlib>\n",
using namespace metal;
//-------------
struct VertexOutput {
float4 pos [[position]];
float2 texcoord;
};
//-------------
struct Vertex {
float4 position [[position]];
};
//-------------
vertex VertexOutput
vertFunc(unsigned int vID[[vertex_id]], const device Vertex *pos [[ buffer(0) ]]) {
VertexOutput out;
out.pos = pos[vID].position;
out.texcoord.x = (float) (vID / 2);
out.texcoord.y = 1.0 - (float) (vID % 2);
return out;
}
//-------------
fragment float4
fragFunc(VertexOutput input [[stage_in]], texture2d<half> colorTexture [[ texture(0) ]]) {
constexpr sampler textureSampler(mag_filter::nearest, min_filter::nearest);
// Sample the texture to obtain a color
const half4 colorSample = colorTexture.sample(textureSampler, input.texcoord);
// We return the color of the texture
return float4(colorSample);
};
);
//-------------------------------------
@implementation iOSViewDelegate {
SWindowData *window_data;
SWindowData_IOS *window_data_ios;
id<MTLDevice> metal_device;
id<MTLLibrary> metal_library;
dispatch_semaphore_t semaphore;
id<MTLCommandQueue> command_queue;
id<MTLRenderPipelineState> pipeline_state;
id<MTLTexture> texture_buffer;
uint8_t current_buffer;
}
//-------------------------------------
-(nonnull instancetype) initWithMetalKitView:(nonnull MTKView *) view windowData:(nonnull SWindowData *) windowData {
self = [super init];
if (self) {
window_data = windowData;
window_data_ios = (SWindowData_IOS *) windowData->specific;
view.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
view.sampleCount = 1;
metal_device = view.device;
// Used for syncing the CPU and GPU
semaphore = dispatch_semaphore_create(MaxBuffersInFlight);
// Setup command queue
command_queue = [metal_device newCommandQueue];
[self _createShaders];
[self _createAssets];
}
return self;
}
//-------------------------------------
- (bool) _createShaders {
NSError *error = 0x0;
metal_library = [metal_device newLibraryWithSource:g_shader_src
options:[[MTLCompileOptions alloc] init]
error:&error
];
if (error || !metal_library) {
NSLog(@"Unable to create shaders %@", error);
return false;
}
id<MTLFunction> vertex_shader_func = [metal_library newFunctionWithName:@"vertFunc"];
id<MTLFunction> fragment_shader_func = [metal_library newFunctionWithName:@"fragFunc"];
if (!vertex_shader_func) {
NSLog(@"Unable to get vertFunc!\n");
return false;
}
if (!fragment_shader_func) {
NSLog(@"Unable to get fragFunc!\n");
return false;
}
// Create a reusable pipeline state
MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
pipelineStateDescriptor.label = @"MiniFB_pipeline";
pipelineStateDescriptor.vertexFunction = vertex_shader_func;
pipelineStateDescriptor.fragmentFunction = fragment_shader_func;
pipelineStateDescriptor.colorAttachments[0].pixelFormat = 80; //bgra8Unorm;
pipeline_state = [metal_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error];
if (!pipeline_state) {
NSLog(@"Failed to created pipeline state, error %@", error);
return false;
}
return true;
}
//-------------------------------------
- (void) _createAssets {
static Vertex s_vertices[4] = {
{-1.0, -1.0, 0, 1},
{-1.0, 1.0, 0, 1},
{ 1.0, -1.0, 0, 1},
{ 1.0, 1.0, 0, 1},
};
memcpy(window_data_ios->vertices, s_vertices, sizeof(s_vertices));
MTLTextureDescriptor *td;
td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:window_data->buffer_width
height:window_data->buffer_height
mipmapped:false];
// Create the texture from the device by using the descriptor
texture_buffer = [metal_device newTextureWithDescriptor:td];
}
//-------------------------------------
- (void) resizeTextures {
MTLTextureDescriptor *td;
td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:window_data->buffer_width
height:window_data->buffer_height
mipmapped:false];
// Create the texture from the device by using the descriptor
[texture_buffer release];
texture_buffer = [metal_device newTextureWithDescriptor:td];
}
//-------------------------------------
- (void) drawInMTKView:(nonnull MTKView *) view {
// Wait to ensure only MaxBuffersInFlight number of frames are getting proccessed
// by any stage in the Metal pipeline (App, Metal, Drivers, GPU, etc)
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
current_buffer = (current_buffer + 1) % MaxBuffersInFlight;
// Create a new command buffer for each render pass to the current drawable
id<MTLCommandBuffer> commandBuffer = [command_queue commandBuffer];
commandBuffer.label = @"minifb_command_buffer";
// Add completion hander which signals semaphore when Metal and the GPU has fully
// finished processing the commands we're encoding this frame. This indicates when the
// dynamic buffers filled with our vertices, that we're writing to this frame, will no longer
// be needed by Metal and the GPU, meaning we can overwrite the buffer contents without
// corrupting the rendering.
__block dispatch_semaphore_t block_sema = semaphore;
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
(void)buffer;
dispatch_semaphore_signal(block_sema);
}];
// Copy the bytes from our data object into the texture
MTLRegion region = { { 0, 0, 0 }, { window_data->buffer_width, window_data->buffer_height, 1 } };
[texture_buffer replaceRegion:region mipmapLevel:0 withBytes:window_data->draw_buffer bytesPerRow:window_data->buffer_stride];
// Delay getting the currentRenderPassDescriptor until absolutely needed. This avoids
// holding onto the drawable and blocking the display pipeline any longer than necessary
MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor;
if (renderPassDescriptor != nil) {
//renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0);
// Create a render command encoder so we can render into something
id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
renderEncoder.label = @"minifb_command_encoder";
// Set render command encoder state
[renderEncoder setRenderPipelineState:pipeline_state];
[renderEncoder setVertexBytes:window_data_ios->vertices length:sizeof(window_data_ios->vertices) atIndex:0];
//[renderEncoder setFragmentTexture:texture_buffers[current_buffer] atIndex:0];
[renderEncoder setFragmentTexture:texture_buffer atIndex:0];
// Draw the vertices of our quads
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
// We're done encoding commands
[renderEncoder endEncoding];
// Schedule a present once the framebuffer is complete using the current drawable
[commandBuffer presentDrawable:view.currentDrawable];
}
// Finalize rendering here & push the command buffer to the GPU
[commandBuffer commit];
}
//-------------------------------------
- (void) mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size {
(void) view;
// Respond to drawable size or orientation changes here
float scale = [UIScreen mainScreen].scale;
window_data->window_width = size.width * scale;
window_data->window_height = size.height * scale;
resize_dst(window_data, size.width, size.height);
kCall(resize_func, size.width, size.height);
}
@end

View File

@ -0,0 +1,589 @@
#include <Cocoa/Cocoa.h>
#if defined(USE_METAL_API)
#include <Carbon/Carbon.h>
#include <MetalKit/MetalKit.h>
#endif
#include <unistd.h>
#include <sched.h>
#include <mach/mach_time.h>
#include "OSXWindow.h"
#include "OSXView.h"
#include "OSXViewDelegate.h"
#include "WindowData_OSX.h"
#include <MiniFB.h>
#include <MiniFB_internal.h>
#include <MiniFB_enums.h>
//-------------------------------------
void init_keycodes();
//-------------------------------------
SWindowData *
create_window_data(unsigned width, unsigned height) {
SWindowData *window_data;
window_data = malloc(sizeof(SWindowData));
if(window_data == 0x0) {
NSLog(@"Cannot allocate window data");
return 0x0;
}
memset(window_data, 0, sizeof(SWindowData));
SWindowData_OSX *window_data_osx = malloc(sizeof(SWindowData_OSX));
if(window_data_osx == 0x0) {
free(window_data);
NSLog(@"Cannot allocate osx window data");
return 0x0;
}
memset(window_data_osx, 0, sizeof(SWindowData_OSX));
window_data->specific = window_data_osx;
calc_dst_factor(window_data, width, height);
window_data->buffer_width = width;
window_data->buffer_height = height;
window_data->buffer_stride = width * 4;
#if defined(USE_METAL_API)
window_data->draw_buffer = malloc(width * height * 4);
if (!window_data->draw_buffer) {
free(window_data_osx);
free(window_data);
NSLog(@"Unable to create draw buffer");
return 0x0;
}
#endif
return window_data;
}
//-------------------------------------
struct mfb_window *
mfb_open_ex(const char *title, unsigned width, unsigned height, unsigned flags) {
@autoreleasepool {
SWindowData *window_data = create_window_data(width, height);
if (window_data == 0x0) {
return 0x0;
}
SWindowData_OSX *window_data_osx = (SWindowData_OSX *) window_data->specific;
init_keycodes();
[NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
NSRect rectangle, frameRect;
NSWindowStyleMask styles = 0;
if (flags & WF_BORDERLESS) {
styles |= NSWindowStyleMaskBorderless;
}
else {
styles |= NSWindowStyleMaskClosable | NSWindowStyleMaskTitled;
}
if (flags & WF_RESIZABLE)
styles |= NSWindowStyleMaskResizable;
if (flags & WF_FULLSCREEN) {
styles = NSWindowStyleMaskFullScreen;
NSScreen *mainScreen = [NSScreen mainScreen];
NSRect screenRect = [mainScreen frame];
window_data->window_width = screenRect.size.width;
window_data->window_height = screenRect.size.height;
rectangle = NSMakeRect(0, 0, window_data->window_width, window_data->window_height);
frameRect = rectangle;
}
else if (flags & WF_FULLSCREEN_DESKTOP) {
NSScreen *mainScreen = [NSScreen mainScreen];
NSRect screenRect = [mainScreen visibleFrame];
window_data->window_width = screenRect.size.width;
window_data->window_height = screenRect.size.height;
rectangle = NSMakeRect(0, 0, window_data->window_width, window_data->window_height);
frameRect = rectangle;
}
else {
window_data->window_width = width;
window_data->window_height = height;
rectangle = NSMakeRect(0, 0, window_data->window_width, window_data->window_height);
frameRect = [NSWindow frameRectForContentRect:rectangle styleMask:styles];
}
window_data_osx->window = [[OSXWindow alloc] initWithContentRect:frameRect styleMask:styles backing:NSBackingStoreBuffered defer:NO windowData:window_data];
if (!window_data_osx->window) {
NSLog(@"Cannot create window");
if(window_data->draw_buffer != 0x0) {
free(window_data->draw_buffer);
window_data->draw_buffer = 0x0;
}
free(window_data_osx);
free(window_data);
return 0x0;
}
#if defined(USE_METAL_API)
window_data_osx->viewController = [[OSXViewDelegate alloc] initWithWindowData:window_data];
MTKView* view = [[MTKView alloc] initWithFrame:rectangle];
view.device = window_data_osx->viewController->metal_device;
view.delegate = window_data_osx->viewController;
view.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
[window_data_osx->window.contentView addSubview:view];
//[window_data->window updateSize];
#endif
[window_data_osx->window setTitle:[NSString stringWithUTF8String:title]];
[window_data_osx->window setReleasedWhenClosed:NO];
[window_data_osx->window performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) withObject:nil waitUntilDone:YES];
[window_data_osx->window setAcceptsMouseMovedEvents:YES];
[window_data_osx->window center];
window_data_osx->timer = mfb_timer_create();
[NSApp activateIgnoringOtherApps:YES];
#if defined(USE_METAL_API)
[NSApp finishLaunching];
#endif
mfb_set_keyboard_callback((struct mfb_window *) window_data, keyboard_default);
#if defined(_DEBUG) || defined(DEBUG)
#if defined(USE_METAL_API)
NSLog(@"Window created using Metal API");
#else
NSLog(@"Window created using Cocoa API");
#endif
#endif
window_data->is_initialized = true;
return (struct mfb_window *) window_data;
}
}
//-------------------------------------
static void
destroy_window_data(SWindowData *window_data) {
if(window_data == 0x0)
return;
@autoreleasepool {
SWindowData_OSX *window_data_osx = (SWindowData_OSX *) window_data->specific;
if(window_data_osx != 0x0) {
OSXWindow *window = window_data_osx->window;
[window performClose:nil];
// Flush events!
NSEvent* event;
do {
event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
if (event) {
[NSApp sendEvent:event];
}
} while (event);
[window removeWindowData];
mfb_timer_destroy(window_data_osx->timer);
memset(window_data_osx, 0, sizeof(SWindowData_OSX));
free(window_data_osx);
}
#if defined(USE_METAL_API)
if(window_data->draw_buffer != 0x0) {
free(window_data->draw_buffer);
window_data->draw_buffer = 0x0;
}
#endif
memset(window_data, 0, sizeof(SWindowData));
free(window_data);
}
}
//-------------------------------------
static void
update_events(SWindowData *window_data) {
NSEvent* event;
@autoreleasepool {
do {
event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
if (event) {
[NSApp sendEvent:event];
}
} while ((window_data->close == false) && event);
}
}
//-------------------------------------
mfb_update_state
mfb_update_ex(struct mfb_window *window, void *buffer, unsigned width, unsigned height) {
if(window == 0x0) {
return STATE_INVALID_WINDOW;
}
SWindowData *window_data = (SWindowData *) window;
if(window_data->close) {
destroy_window_data(window_data);
return STATE_EXIT;
}
if(buffer == 0x0) {
return STATE_INVALID_BUFFER;
}
SWindowData_OSX *window_data_osx = (SWindowData_OSX *) window_data->specific;
#if defined(USE_METAL_API)
if(window_data->buffer_width != width || window_data->buffer_height != height) {
window_data->buffer_width = width;
window_data->buffer_stride = width * 4;
window_data->buffer_height = height;
window_data->draw_buffer = realloc(window_data->draw_buffer, window_data->buffer_stride * window_data->buffer_height);
[window_data_osx->viewController resizeTextures];
}
memcpy(window_data->draw_buffer, buffer, window_data->buffer_stride * window_data->buffer_height);
#else
if(window_data->buffer_width != width || window_data->buffer_height != height) {
window_data->buffer_width = width;
window_data->buffer_stride = width * 4;
window_data->buffer_height = height;
}
window_data->draw_buffer = buffer;
#endif
update_events(window_data);
if(window_data->close) {
destroy_window_data(window_data);
return STATE_EXIT;
}
[[window_data_osx->window contentView] setNeedsDisplay:YES];
return STATE_OK;
}
//-------------------------------------
mfb_update_state
mfb_update_events(struct mfb_window *window) {
if(window == 0x0) {
return STATE_INVALID_WINDOW;
}
SWindowData *window_data = (SWindowData *) window;
if(window_data->close) {
destroy_window_data(window_data);
return STATE_EXIT;
}
update_events(window_data);
if(window_data->close) {
destroy_window_data(window_data);
return STATE_EXIT;
}
SWindowData_OSX *window_data_osx = (SWindowData_OSX *) window_data->specific;
[[window_data_osx->window contentView] setNeedsDisplay:YES];
return STATE_OK;
}
//-------------------------------------
extern double g_time_for_frame;
extern bool g_use_hardware_sync;
bool
mfb_wait_sync(struct mfb_window *window) {
NSEvent* event;
if(window == 0x0) {
return false;
}
SWindowData *window_data = (SWindowData *) window;
if(window_data->close) {
destroy_window_data(window_data);
return false;
}
if(g_use_hardware_sync) {
return true;
}
@autoreleasepool {
SWindowData_OSX *window_data_osx = (SWindowData_OSX *) window_data->specific;
if(window_data_osx == 0x0) {
return false;
}
double current;
uint32_t millis = 1;
while(1) {
current = mfb_timer_now(window_data_osx->timer);
if (current >= g_time_for_frame * 0.96) {
mfb_timer_reset(window_data_osx->timer);
return true;
}
else if(current >= g_time_for_frame * 0.8) {
millis = 0;
}
usleep(millis * 1000);
//sched_yield();
if(millis == 1) {
event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
if (event) {
[NSApp sendEvent:event];
if(window_data->close) {
destroy_window_data(window_data);
return false;
}
}
}
}
}
return true;
}
//-------------------------------------
bool
mfb_set_viewport(struct mfb_window *window, unsigned offset_x, unsigned offset_y, unsigned width, unsigned height) {
if(window == 0x0) {
return false;
}
SWindowData *window_data = (SWindowData *) window;
if(offset_x + width > window_data->window_width) {
return false;
}
if(offset_y + height > window_data->window_height) {
return false;
}
window_data->dst_offset_x = offset_x;
window_data->dst_offset_y = offset_y;
window_data->dst_width = width;
window_data->dst_height = height;
calc_dst_factor(window_data, window_data->window_width, window_data->window_height);
#if defined(USE_METAL_API)
float x1 = ((float) offset_x / window_data->window_width) * 2.0f - 1.0f;
float x2 = (((float) offset_x + width) / window_data->window_width) * 2.0f - 1.0f;
float y1 = ((float) offset_y / window_data->window_height) * 2.0f - 1.0f;
float y2 = (((float) offset_y + height) / window_data->window_height) * 2.0f - 1.0f;
SWindowData_OSX *window_data_osx = (SWindowData_OSX *) window_data->specific;
window_data_osx->metal.vertices[0].x = x1;
window_data_osx->metal.vertices[0].y = y1;
window_data_osx->metal.vertices[1].x = x1;
window_data_osx->metal.vertices[1].y = y2;
window_data_osx->metal.vertices[2].x = x2;
window_data_osx->metal.vertices[2].y = y1;
window_data_osx->metal.vertices[3].x = x2;
window_data_osx->metal.vertices[3].y = y2;
#endif
return true;
}
//-------------------------------------
extern short int g_keycodes[512];
void
init_keycodes() {
// Clear keys
for (unsigned int i = 0; i < sizeof(g_keycodes) / sizeof(g_keycodes[0]); ++i)
g_keycodes[i] = 0;
g_keycodes[0x1D] = KB_KEY_0;
g_keycodes[0x12] = KB_KEY_1;
g_keycodes[0x13] = KB_KEY_2;
g_keycodes[0x14] = KB_KEY_3;
g_keycodes[0x15] = KB_KEY_4;
g_keycodes[0x17] = KB_KEY_5;
g_keycodes[0x16] = KB_KEY_6;
g_keycodes[0x1A] = KB_KEY_7;
g_keycodes[0x1C] = KB_KEY_8;
g_keycodes[0x19] = KB_KEY_9;
g_keycodes[0x00] = KB_KEY_A;
g_keycodes[0x0B] = KB_KEY_B;
g_keycodes[0x08] = KB_KEY_C;
g_keycodes[0x02] = KB_KEY_D;
g_keycodes[0x0E] = KB_KEY_E;
g_keycodes[0x03] = KB_KEY_F;
g_keycodes[0x05] = KB_KEY_G;
g_keycodes[0x04] = KB_KEY_H;
g_keycodes[0x22] = KB_KEY_I;
g_keycodes[0x26] = KB_KEY_J;
g_keycodes[0x28] = KB_KEY_K;
g_keycodes[0x25] = KB_KEY_L;
g_keycodes[0x2E] = KB_KEY_M;
g_keycodes[0x2D] = KB_KEY_N;
g_keycodes[0x1F] = KB_KEY_O;
g_keycodes[0x23] = KB_KEY_P;
g_keycodes[0x0C] = KB_KEY_Q;
g_keycodes[0x0F] = KB_KEY_R;
g_keycodes[0x01] = KB_KEY_S;
g_keycodes[0x11] = KB_KEY_T;
g_keycodes[0x20] = KB_KEY_U;
g_keycodes[0x09] = KB_KEY_V;
g_keycodes[0x0D] = KB_KEY_W;
g_keycodes[0x07] = KB_KEY_X;
g_keycodes[0x10] = KB_KEY_Y;
g_keycodes[0x06] = KB_KEY_Z;
g_keycodes[0x27] = KB_KEY_APOSTROPHE;
g_keycodes[0x2A] = KB_KEY_BACKSLASH;
g_keycodes[0x2B] = KB_KEY_COMMA;
g_keycodes[0x18] = KB_KEY_EQUAL;
g_keycodes[0x32] = KB_KEY_GRAVE_ACCENT;
g_keycodes[0x21] = KB_KEY_LEFT_BRACKET;
g_keycodes[0x1B] = KB_KEY_MINUS;
g_keycodes[0x2F] = KB_KEY_PERIOD;
g_keycodes[0x1E] = KB_KEY_RIGHT_BRACKET;
g_keycodes[0x29] = KB_KEY_SEMICOLON;
g_keycodes[0x2C] = KB_KEY_SLASH;
g_keycodes[0x0A] = KB_KEY_WORLD_1;
g_keycodes[0x33] = KB_KEY_BACKSPACE;
g_keycodes[0x39] = KB_KEY_CAPS_LOCK;
g_keycodes[0x75] = KB_KEY_DELETE;
g_keycodes[0x7D] = KB_KEY_DOWN;
g_keycodes[0x77] = KB_KEY_END;
g_keycodes[0x24] = KB_KEY_ENTER;
g_keycodes[0x35] = KB_KEY_ESCAPE;
g_keycodes[0x7A] = KB_KEY_F1;
g_keycodes[0x78] = KB_KEY_F2;
g_keycodes[0x63] = KB_KEY_F3;
g_keycodes[0x76] = KB_KEY_F4;
g_keycodes[0x60] = KB_KEY_F5;
g_keycodes[0x61] = KB_KEY_F6;
g_keycodes[0x62] = KB_KEY_F7;
g_keycodes[0x64] = KB_KEY_F8;
g_keycodes[0x65] = KB_KEY_F9;
g_keycodes[0x6D] = KB_KEY_F10;
g_keycodes[0x67] = KB_KEY_F11;
g_keycodes[0x6F] = KB_KEY_F12;
g_keycodes[0x69] = KB_KEY_F13;
g_keycodes[0x6B] = KB_KEY_F14;
g_keycodes[0x71] = KB_KEY_F15;
g_keycodes[0x6A] = KB_KEY_F16;
g_keycodes[0x40] = KB_KEY_F17;
g_keycodes[0x4F] = KB_KEY_F18;
g_keycodes[0x50] = KB_KEY_F19;
g_keycodes[0x5A] = KB_KEY_F20;
g_keycodes[0x73] = KB_KEY_HOME;
g_keycodes[0x72] = KB_KEY_INSERT;
g_keycodes[0x7B] = KB_KEY_LEFT;
g_keycodes[0x3A] = KB_KEY_LEFT_ALT;
g_keycodes[0x3B] = KB_KEY_LEFT_CONTROL;
g_keycodes[0x38] = KB_KEY_LEFT_SHIFT;
g_keycodes[0x37] = KB_KEY_LEFT_SUPER;
g_keycodes[0x6E] = KB_KEY_MENU;
g_keycodes[0x47] = KB_KEY_NUM_LOCK;
g_keycodes[0x79] = KB_KEY_PAGE_DOWN;
g_keycodes[0x74] = KB_KEY_PAGE_UP;
g_keycodes[0x7C] = KB_KEY_RIGHT;
g_keycodes[0x3D] = KB_KEY_RIGHT_ALT;
g_keycodes[0x3E] = KB_KEY_RIGHT_CONTROL;
g_keycodes[0x3C] = KB_KEY_RIGHT_SHIFT;
g_keycodes[0x36] = KB_KEY_RIGHT_SUPER;
g_keycodes[0x31] = KB_KEY_SPACE;
g_keycodes[0x30] = KB_KEY_TAB;
g_keycodes[0x7E] = KB_KEY_UP;
g_keycodes[0x52] = KB_KEY_KP_0;
g_keycodes[0x53] = KB_KEY_KP_1;
g_keycodes[0x54] = KB_KEY_KP_2;
g_keycodes[0x55] = KB_KEY_KP_3;
g_keycodes[0x56] = KB_KEY_KP_4;
g_keycodes[0x57] = KB_KEY_KP_5;
g_keycodes[0x58] = KB_KEY_KP_6;
g_keycodes[0x59] = KB_KEY_KP_7;
g_keycodes[0x5B] = KB_KEY_KP_8;
g_keycodes[0x5C] = KB_KEY_KP_9;
g_keycodes[0x45] = KB_KEY_KP_ADD;
g_keycodes[0x41] = KB_KEY_KP_DECIMAL;
g_keycodes[0x4B] = KB_KEY_KP_DIVIDE;
g_keycodes[0x4C] = KB_KEY_KP_ENTER;
g_keycodes[0x51] = KB_KEY_KP_EQUAL;
g_keycodes[0x43] = KB_KEY_KP_MULTIPLY;
g_keycodes[0x4E] = KB_KEY_KP_SUBTRACT;
}
//-------------------------------------
extern double g_timer_frequency;
extern double g_timer_resolution;
uint64_t
mfb_timer_tick() {
static mach_timebase_info_data_t timebase = { 0 };
if (timebase.denom == 0) {
(void) mach_timebase_info(&timebase);
}
uint64_t time = mach_absolute_time();
//return (time * s_timebase_info.numer) / s_timebase_info.denom;
// Perform the arithmetic at 128-bit precision to avoid the overflow!
uint64_t high = (time >> 32) * timebase.numer;
uint64_t highRem = ((high % timebase.denom) << 32) / timebase.denom;
uint64_t low = (time & 0xFFFFFFFFull) * timebase.numer / timebase.denom;
high /= timebase.denom;
return (high << 32) + highRem + low;
}
//-------------------------------------
void
mfb_timer_init() {
g_timer_frequency = 1e+9;
g_timer_resolution = 1.0 / g_timer_frequency;
}
//-------------------------------------
void
mfb_get_monitor_scale(struct mfb_window *window, float *scale_x, float *scale_y) {
float scale = 1.0f;
if(window != 0x0) {
SWindowData *window_data = (SWindowData *) window;
SWindowData_OSX *window_data_osx = (SWindowData_OSX *) window_data->specific;
scale = [window_data_osx->window backingScaleFactor];
}
else {
scale = [[NSScreen mainScreen] backingScaleFactor];
}
if (scale_x) {
*scale_x = scale;
if(*scale_x == 0) {
*scale_x = 1;
}
}
if (scale_y) {
*scale_y = scale;
if (*scale_y == 0) {
*scale_y = 1;
}
}
}

View File

@ -0,0 +1,14 @@
#import <Cocoa/Cocoa.h>
#include "WindowData.h"
@interface OSXView : NSView
{
@public SWindowData *window_data;
#if defined(USE_METAL_API)
@private NSTrackingArea *tracking_area;
#endif
}
@end

View File

@ -0,0 +1,240 @@
#import "OSXView.h"
#import "OSXWindow.h"
#import "WindowData_OSX.h"
#include <MiniFB_internal.h>
//-------------------------------------
@implementation OSXView
#if defined(USE_METAL_API)
//-------------------------------------
- (void)updateTrackingAreas {
if(tracking_area != nil) {
[self removeTrackingArea:tracking_area];
[tracking_area release];
}
int opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways);
tracking_area = [[NSTrackingArea alloc] initWithRect:[self bounds]
options:opts
owner:self
userInfo:nil];
[self addTrackingArea:tracking_area];
}
#else
//-------------------------------------
- (NSRect)resizeRect {
const CGFloat resizeBoxSize = 16.0;
const CGFloat contentViewPadding = 5.5;
NSRect contentViewRect = [[self window] contentRectForFrameRect:[[self window] frame]];
NSRect resizeRect = NSMakeRect(
NSMaxX(contentViewRect) + contentViewPadding,
NSMinY(contentViewRect) - resizeBoxSize - contentViewPadding,
resizeBoxSize,
resizeBoxSize
);
return resizeRect;
}
//-------------------------------------
- (void)drawRect:(NSRect)rect {
(void)rect;
if(window_data == 0x0)
return;
SWindowData_OSX *window_data_osx = (SWindowData_OSX *) window_data->specific;
if (!window_data_osx || !window_data_osx->window || !window_data->draw_buffer)
return;
CGContextRef context = [[NSGraphicsContext currentContext] CGContext];
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGDataProviderRef provider = CGDataProviderCreateWithData(0x0,
window_data->draw_buffer,
window_data->buffer_width * window_data->buffer_height * 4,
0x0
);
CGImageRef img = CGImageCreate(window_data->buffer_width
, window_data->buffer_height
, 8
, 32
, window_data->buffer_width * 4
, space
, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little
, provider
, 0x0
, false
, kCGRenderingIntentDefault
);
const CGFloat components[] = {0.0f, 0.0f, 0.0f, 1.0f};
const CGColorRef black = CGColorCreate(space, components);
CGColorSpaceRelease(space);
CGDataProviderRelease(provider);
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) {
CGContextSetFillColorWithColor(context, black);
CGContextFillRect(context, rect);
}
// TODO: Sometimes there is a crash here
CGContextDrawImage(context,
CGRectMake(window_data->dst_offset_x, window_data->dst_offset_y, window_data->dst_width, window_data->dst_height),
img
);
CGImageRelease(img);
}
#endif
//-------------------------------------
- (BOOL)acceptsFirstMouse:(NSEvent *)event {
(void)event;
return YES;
}
//-------------------------------------
- (void)mouseDown:(NSEvent*)event {
(void)event;
if(window_data != 0x0) {
window_data->mouse_button_status[MOUSE_BTN_1] = true;
kCall(mouse_btn_func, MOUSE_BTN_1, window_data->mod_keys, true);
}
}
//-------------------------------------
- (void)mouseUp:(NSEvent*)event {
(void)event;
if(window_data != 0x0) {
window_data->mouse_button_status[MOUSE_BTN_1] = false;
kCall(mouse_btn_func, MOUSE_BTN_1, window_data->mod_keys, false);
}
}
//-------------------------------------
- (void)rightMouseDown:(NSEvent*)event {
(void)event;
if(window_data != 0x0) {
window_data->mouse_button_status[MOUSE_BTN_2] = true;
kCall(mouse_btn_func, MOUSE_BTN_2, window_data->mod_keys, true);
}
}
//-------------------------------------
- (void)rightMouseUp:(NSEvent*)event {
(void)event;
if(window_data != 0x0) {
window_data->mouse_button_status[MOUSE_BTN_2] = false;
kCall(mouse_btn_func, MOUSE_BTN_2, window_data->mod_keys, false);
}
}
//-------------------------------------
- (void)otherMouseDown:(NSEvent *)event {
(void)event;
if(window_data != 0x0) {
window_data->mouse_button_status[[event buttonNumber] & 0x07] = true;
kCall(mouse_btn_func, [event buttonNumber], window_data->mod_keys, true);
}
}
//-------------------------------------
- (void)otherMouseUp:(NSEvent *)event {
(void)event;
if(window_data != 0x0) {
window_data->mouse_button_status[[event buttonNumber] & 0x07] = false;
kCall(mouse_btn_func, [event buttonNumber], window_data->mod_keys, false);
}
}
//-------------------------------------
- (void)scrollWheel:(NSEvent *)event {
if(window_data != 0x0) {
window_data->mouse_wheel_x = [event deltaX];
window_data->mouse_wheel_y = [event deltaY];
kCall(mouse_wheel_func, window_data->mod_keys, window_data->mouse_wheel_x, window_data->mouse_wheel_y);
}
}
//-------------------------------------
- (void)mouseDragged:(NSEvent *)event {
[self mouseMoved:event];
}
//-------------------------------------
- (void)rightMouseDragged:(NSEvent *)event {
[self mouseMoved:event];
}
//-------------------------------------
- (void)otherMouseDragged:(NSEvent *)event {
[self mouseMoved:event];
}
//-------------------------------------
- (void)mouseMoved:(NSEvent *)event {
if(window_data != 0x0) {
NSPoint point = [event locationInWindow];
//NSPoint localPoint = [self convertPoint:point fromView:nil];
window_data->mouse_pos_x = point.x;
#if defined(USE_INVERTED_Y_ON_MACOS)
window_data->mouse_pos_y = point.y;
#else
window_data->mouse_pos_y = window_data->window_height - point.y;
#endif
kCall(mouse_move_func, window_data->mouse_pos_x, window_data->mouse_pos_y);
}
}
//-------------------------------------
- (void)mouseExited:(NSEvent *)event {
(void)event;
//printf("mouse exit\n");
}
//-------------------------------------
- (void)mouseEntered:(NSEvent *)event {
(void)event;
//printf("mouse enter\n");
}
//-------------------------------------
- (BOOL)canBecomeKeyView {
return YES;
}
//-------------------------------------
- (NSView *)nextValidKeyView {
return self;
}
//-------------------------------------
- (NSView *)previousValidKeyView {
return self;
}
//-------------------------------------
- (BOOL)acceptsFirstResponder {
return YES;
}
//-------------------------------------
- (void)viewDidMoveToWindow {
}
//-------------------------------------
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
@end

View File

@ -0,0 +1,35 @@
#pragma once
#if defined(USE_METAL_API)
#import <MetalKit/MetalKit.h>
#include "WindowData_OSX.h"
// Number of textures in flight (tripple buffered)
enum { MaxBuffersInFlight = 3 };
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@interface OSXViewDelegate : NSViewController<MTKViewDelegate>
{
@public SWindowData *window_data;
@public SWindowData_OSX *window_data_osx;
id<MTLDevice> metal_device;
id<MTLLibrary> metal_library;
dispatch_semaphore_t semaphore; // Used for syncing with CPU/GPU
id<MTLCommandQueue> command_queue;
id<MTLRenderPipelineState> pipeline_state;
id<MTLTexture> texture_buffers[MaxBuffersInFlight];
int current_buffer;
}
- (id) initWithWindowData:(SWindowData *) windowData;
- (void) resizeTextures;
@end
#endif

View File

@ -0,0 +1,261 @@
#include "OSXViewDelegate.h"
#if defined(USE_METAL_API)
#import <MetalKit/MetalKit.h>
extern double g_time_for_frame;
extern bool g_use_hardware_sync;
//--
bool g_target_fps_changed = true;
//-------------------------------------
void
set_target_fps_aux() {
g_target_fps_changed = true;
}
//-------------------------------------
#define kShader(inc, src) @inc#src
NSString *g_shader_src = kShader(
"#include <metal_stdlib>\n",
using namespace metal;
//-------------
struct VertexOutput {
float4 pos [[position]];
float2 texcoord;
};
//-------------
struct Vertex {
float4 position [[position]];
};
//-------------
vertex VertexOutput
vertFunc(unsigned int vID[[vertex_id]], const device Vertex *pos [[ buffer(0) ]]) {
VertexOutput out;
out.pos = pos[vID].position;
out.texcoord.x = (float) (vID / 2);
out.texcoord.y = 1.0 - (float) (vID % 2);
return out;
}
//-------------
fragment float4
fragFunc(VertexOutput input [[stage_in]], texture2d<half> colorTexture [[ texture(0) ]]) {
constexpr sampler textureSampler(mag_filter::nearest, min_filter::nearest);
// Sample the texture to obtain a color
const half4 colorSample = colorTexture.sample(textureSampler, input.texcoord);
// We return the color of the texture
return float4(colorSample);
};
);
//-------------------------------------
@implementation OSXViewDelegate
//-------------------------------------
- (id) initWithWindowData:(SWindowData *) windowData {
self = [super init];
if (self) {
window_data = windowData;
window_data_osx = (SWindowData_OSX *) windowData->specific;
metal_device = MTLCreateSystemDefaultDevice();
if (!metal_device) {
NSLog(@"Metal is not supported on this device");
return 0x0;
}
// Used for syncing the CPU and GPU
semaphore = dispatch_semaphore_create(MaxBuffersInFlight);
// Setup command queue
command_queue = [metal_device newCommandQueue];
// MacOS Mojave is ignoring view.preferredFramesPerSecond
// MacOS Big Sur is ignoring commandBuffer:presentDrawable:afterMinimumDuration:
//id<MTLCommandBuffer> commandBuffer = [command_queue commandBuffer];
//if ([commandBuffer respondsToSelector:@selector(presentDrawable:afterMinimumDuration:)]) {
// g_use_hardware_sync = true;
//}
[self _createShaders];
[self _createAssets];
}
return self;
}
//-------------------------------------
- (bool) _createShaders {
NSError *error = 0x0;
metal_library = [metal_device newLibraryWithSource:g_shader_src
options:[[MTLCompileOptions alloc] init]
error:&error
];
if (error || !metal_library) {
NSLog(@"Unable to create shaders %@", error);
return false;
}
id<MTLFunction> vertex_shader_func = [metal_library newFunctionWithName:@"vertFunc"];
id<MTLFunction> fragment_shader_func = [metal_library newFunctionWithName:@"fragFunc"];
if (!vertex_shader_func) {
NSLog(@"Unable to get vertFunc!\n");
return false;
}
if (!fragment_shader_func) {
NSLog(@"Unable to get fragFunc!\n");
return false;
}
// Create a reusable pipeline state
MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
pipelineStateDescriptor.label = @"MiniFB_pipeline";
pipelineStateDescriptor.vertexFunction = vertex_shader_func;
pipelineStateDescriptor.fragmentFunction = fragment_shader_func;
pipelineStateDescriptor.colorAttachments[0].pixelFormat = 80; //bgra8Unorm;
pipeline_state = [metal_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error];
if (!pipeline_state) {
NSLog(@"Failed to created pipeline state, error %@", error);
return false;
}
return true;
}
//-------------------------------------
- (void) _createAssets {
static Vertex s_vertices[4] = {
{-1.0, -1.0, 0, 1},
{-1.0, 1.0, 0, 1},
{ 1.0, -1.0, 0, 1},
{ 1.0, 1.0, 0, 1},
};
memcpy(window_data_osx->metal.vertices, s_vertices, sizeof(s_vertices));
MTLTextureDescriptor *td;
td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:window_data->buffer_width
height:window_data->buffer_height
mipmapped:false];
// Create the texture from the device by using the descriptor
for (size_t i = 0; i < MaxBuffersInFlight; ++i) {
texture_buffers[i] = [metal_device newTextureWithDescriptor:td];
}
}
//-------------------------------------
- (void) resizeTextures {
MTLTextureDescriptor *td;
td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:window_data->buffer_width
height:window_data->buffer_height
mipmapped:false];
// Create the texture from the device by using the descriptor
for (size_t i = 0; i < MaxBuffersInFlight; ++i) {
[texture_buffers[i] release];
texture_buffers[i] = [metal_device newTextureWithDescriptor:td];
}
}
//-------------------------------------
- (void) drawInMTKView:(nonnull MTKView *) view {
if (g_target_fps_changed) {
// MacOS is ignoring this :(
if (g_time_for_frame == 0) {
// Contrary to what is stated in the documentation,
// 0 means that it does not update. Like pause.
view.preferredFramesPerSecond = 9999;
}
else {
view.preferredFramesPerSecond = (int) (1.0 / g_time_for_frame);
}
g_target_fps_changed = false;
}
// Wait to ensure only MaxBuffersInFlight number of frames are getting proccessed
// by any stage in the Metal pipeline (App, Metal, Drivers, GPU, etc)
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
current_buffer = (current_buffer + 1) % MaxBuffersInFlight;
// Create a new command buffer for each render pass to the current drawable
id<MTLCommandBuffer> commandBuffer = [command_queue commandBuffer];
commandBuffer.label = @"minifb_command_buffer";
// Add completion hander which signals semaphore when Metal and the GPU has fully
// finished processing the commands we're encoding this frame. This indicates when the
// dynamic buffers filled with our vertices, that we're writing to this frame, will no longer
// be needed by Metal and the GPU, meaning we can overwrite the buffer contents without
// corrupting the rendering.
__block dispatch_semaphore_t block_sema = semaphore;
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
(void)buffer;
dispatch_semaphore_signal(block_sema);
}];
// Copy the bytes from our data object into the texture
MTLRegion region = { { 0, 0, 0 }, { window_data->buffer_width, window_data->buffer_height, 1 } };
[texture_buffers[current_buffer] replaceRegion:region mipmapLevel:0 withBytes:window_data->draw_buffer bytesPerRow:window_data->buffer_stride];
// Delay getting the currentRenderPassDescriptor until absolutely needed. This avoids
// holding onto the drawable and blocking the display pipeline any longer than necessary
MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor;
if (renderPassDescriptor != nil) {
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0);
// Create a render command encoder so we can render into something
id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
renderEncoder.label = @"minifb_command_encoder";
// Set render command encoder state
[renderEncoder setRenderPipelineState:pipeline_state];
[renderEncoder setVertexBytes:window_data_osx->metal.vertices length:sizeof(window_data_osx->metal.vertices) atIndex:0];
[renderEncoder setFragmentTexture:texture_buffers[current_buffer] atIndex:0];
// Draw the vertices of our quads
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
// We're done encoding commands
[renderEncoder endEncoding];
// Schedule a present once the framebuffer is complete using the current drawable
//if ([commandBuffer respondsToSelector:@selector(presentDrawable:afterMinimumDuration:)]) {
// // MacOS Big Sur is ignoring this
// [commandBuffer presentDrawable:view.currentDrawable afterMinimumDuration:g_time_for_frame];
//}
//else {
[commandBuffer presentDrawable:view.currentDrawable];
//}
}
// Finalize rendering here & push the command buffer to the GPU
[commandBuffer commit];
}
//-------------------------------------
- (void) mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size {
(void)view;
(void)size;
// resize
}
@end
#endif

View File

@ -0,0 +1,17 @@
#import <Cocoa/Cocoa.h>
#include <WindowData.h>
@interface OSXWindow : NSWindow<NSWindowDelegate>
{
NSView *childContentView;
@public SWindowData *window_data;
}
- (id)initWithContentRect:(NSRect)contentRect
styleMask:(NSWindowStyleMask)windowStyle
backing:(NSBackingStoreType)bufferingType
defer:(BOOL)deferCreation
windowData:(SWindowData *) windowData;
- (void) removeWindowData;
@end

View File

@ -0,0 +1,315 @@
#import "OSXWindow.h"
#import "OSXView.h"
#include "WindowData_OSX.h"
#include <MiniFB_internal.h>
#include <MiniFB_enums.h>
@implementation OSXWindow
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (id)initWithContentRect:(NSRect)contentRect
styleMask:(NSWindowStyleMask)windowStyle
backing:(NSBackingStoreType)bufferingType
defer:(BOOL)deferCreation
windowData:(SWindowData *) windowData
{
self = [super
initWithContentRect:contentRect
styleMask:windowStyle
backing:bufferingType
defer:deferCreation];
if (self)
{
[self setOpaque:YES];
[self setBackgroundColor:[NSColor clearColor]];
self.delegate = self;
self->window_data = windowData;
OSXView *view = (OSXView *) self->childContentView.superview;
view->window_data = windowData;
}
return self;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void) removeWindowData {
self->window_data = 0x0;
OSXView *view = (OSXView *) self->childContentView.superview;
view->window_data = 0x0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)dealloc
{
[[NSNotificationCenter defaultCenter]
removeObserver:self];
[super dealloc];
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)setContentSize:(NSSize)newSize
{
NSSize sizeDelta = newSize;
NSSize childBoundsSize = [childContentView bounds].size;
sizeDelta.width -= childBoundsSize.width;
sizeDelta.height -= childBoundsSize.height;
OSXView *frameView = [super contentView];
NSSize newFrameSize = [frameView bounds].size;
newFrameSize.width += sizeDelta.width;
newFrameSize.height += sizeDelta.height;
[super setContentSize:newFrameSize];
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)flagsChanged:(NSEvent *)event
{
if(window_data == 0x0)
return;
const uint32_t flags = [event modifierFlags];
uint32_t mod_keys = 0, mod_keys_aux = 0;
//NSEventModifierFlagHelp = 1 << 22,
//NSEventModifierFlagFunction = 1 << 23,
if(flags & NSEventModifierFlagCapsLock) {
mod_keys |= KB_MOD_CAPS_LOCK;
}
if(flags & NSEventModifierFlagShift) {
mod_keys |= KB_MOD_SHIFT;
}
if(flags & NSEventModifierFlagControl) {
mod_keys |= KB_MOD_CONTROL;
}
if(flags & NSEventModifierFlagOption) {
mod_keys |= KB_MOD_ALT;
}
if(flags & NSEventModifierFlagCommand) {
mod_keys |= KB_MOD_SUPER;
}
if(flags & NSEventModifierFlagNumericPad) {
mod_keys |= KB_MOD_NUM_LOCK;
}
if(mod_keys != window_data->mod_keys) {
short int key_code = g_keycodes[[event keyCode] & 0x1ff];
if(key_code != KB_KEY_UNKNOWN) {
mod_keys_aux = mod_keys ^ window_data->mod_keys;
if(mod_keys_aux & KB_MOD_CAPS_LOCK) {
window_data->key_status[key_code] = (mod_keys & KB_MOD_CAPS_LOCK) != 0;
kCall(keyboard_func, key_code, mod_keys, window_data->key_status[key_code]);
}
if(mod_keys_aux & KB_MOD_SHIFT) {
window_data->key_status[key_code] = (mod_keys & KB_MOD_SHIFT) != 0;
kCall(keyboard_func, key_code, mod_keys, window_data->key_status[key_code]);
}
if(mod_keys_aux & KB_MOD_CONTROL) {
window_data->key_status[key_code] = (mod_keys & KB_MOD_CONTROL) != 0;
kCall(keyboard_func, key_code, mod_keys, window_data->key_status[key_code]);
}
if(mod_keys_aux & KB_MOD_ALT) {
window_data->key_status[key_code] = (mod_keys & KB_MOD_ALT) != 0;
kCall(keyboard_func, key_code, mod_keys, window_data->key_status[key_code]);
}
if(mod_keys_aux & KB_MOD_SUPER) {
window_data->key_status[key_code] = (mod_keys & KB_MOD_SUPER) != 0;
kCall(keyboard_func, key_code, mod_keys, window_data->key_status[key_code]);
}
if(mod_keys_aux & KB_MOD_NUM_LOCK) {
window_data->key_status[key_code] = (mod_keys & KB_MOD_NUM_LOCK) != 0;
kCall(keyboard_func, key_code, mod_keys, window_data->key_status[key_code]);
}
}
}
window_data->mod_keys = mod_keys;
[super flagsChanged:event];
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)keyDown:(NSEvent *)event
{
if(window_data != 0x0) {
short int key_code = g_keycodes[[event keyCode] & 0x1ff];
window_data->key_status[key_code] = true;
kCall(keyboard_func, key_code, window_data->mod_keys, true);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)keyUp:(NSEvent *)event
{
if(window_data != 0x0) {
short int key_code = g_keycodes[[event keyCode] & 0x1ff];
window_data->key_status[key_code] = false;
kCall(keyboard_func, key_code, window_data->mod_keys, false);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
{
kUnused(replacementRange);
if(window_data != 0x0) {
NSString *characters;
NSUInteger length;
if ([string isKindOfClass:[NSAttributedString class]])
characters = [string string];
else
characters = (NSString*) string;
length = [characters length];
for (NSUInteger i = 0; i < length; i++)
{
const unichar code = [characters characterAtIndex:i];
if ((code & 0xff00) == 0xf700)
continue;
kCall(char_input_func, code);
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)mainWindowChanged:(NSNotification *)notification
{
kUnused(notification);
if(window_data != 0x0) {
if(window_data->is_active == true) {
window_data->is_active = false;
kCall(active_func, false);
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)setContentView:(NSView *)aView
{
if ([childContentView isEqualTo:aView]) {
return;
}
NSRect bounds = [self frame];
bounds.origin = NSZeroPoint;
OSXView *frameView = [super contentView];
if (!frameView)
{
frameView = [[[OSXView alloc] initWithFrame:bounds] autorelease];
[super setContentView:frameView];
}
if (childContentView)
{
[childContentView removeFromSuperview];
}
childContentView = aView;
[childContentView setFrame:[self contentRectForFrameRect:bounds]];
[childContentView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[frameView addSubview:childContentView];
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (NSView *)contentView
{
return childContentView;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (BOOL)canBecomeKeyWindow
{
return YES;
}
- (void)windowDidBecomeKey:(NSNotification *)notification
{
kUnused(notification);
if(window_data != 0x0) {
window_data->is_active = true;
kCall(active_func, true);
}
}
- (void)windowDidResignKey:(NSNotification *)notification
{
kUnused(notification);
if(window_data) {
window_data->is_active = false;
kCall(active_func, false);
}
}
- (void)windowWillClose:(NSNotification *)notification {
kUnused(notification);
if(window_data) {
window_data->close = true;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (BOOL)canBecomeMainWindow
{
return YES;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (NSRect)contentRectForFrameRect:(NSRect)windowFrame
{
windowFrame.origin = NSZeroPoint;
return NSInsetRect(windowFrame, 0, 0);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ (NSRect)frameRectForContentRect:(NSRect)windowContentRect styleMask:(NSWindowStyleMask)windowStyle
{
kUnused(windowStyle);
return NSInsetRect(windowContentRect, 0, 0);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)willClose
{
if(window_data != 0x0) {
window_data->close = true;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)windowDidResize:(NSNotification *)notification {
kUnused(notification);
if(window_data != 0x0) {
CGSize size = [self contentRectForFrameRect:[self frame]].size;
window_data->window_width = size.width;
window_data->window_height = size.height;
resize_dst(window_data, size.width, size.height);
kCall(resize_func, size.width, size.height);
}
}
@end

View File

@ -0,0 +1,27 @@
#pragma once
#include <MiniFB_enums.h>
#include <WindowData.h>
#if defined(USE_METAL_API)
#include <MetalKit/MetalKit.h>
#endif
@class OSXWindow;
@class OSXViewDelegate;
typedef struct Vertex {
float x, y, z, w;
} Vertex;
typedef struct {
OSXWindow *window;
OSXViewDelegate *viewController;
struct mfb_timer *timer;
#if defined(USE_METAL_API)
struct {
Vertex vertices[4];
} metal;
#endif
} SWindowData_OSX;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,46 @@
#pragma once
#include <MiniFB_enums.h>
#include <stdint.h>
struct wl_display;
struct wl_registry;
struct wl_compositor;
struct wl_shell;
struct wl_seat;
struct wl_keyboard;
struct wl_pointer;
struct wl_callback;
struct wl_shm;
struct wl_shm_pool;
struct wl_surface;
struct wl_shell_surface;
struct wl_buffer;
typedef struct
{
struct wl_display *display;
struct wl_registry *registry;
struct wl_compositor *compositor;
struct wl_shell *shell;
struct wl_seat *seat;
struct wl_keyboard *keyboard;
struct wl_pointer *pointer;
struct wl_cursor_theme *cursor_theme;
struct wl_cursor *default_cursor;
struct wl_surface *cursor_surface;
struct wl_shm *shm;
struct wl_shm_pool *shm_pool;
struct wl_surface *surface;
struct wl_shell_surface *shell_surface;
uint32_t seat_version;
uint32_t shm_format;
uint32_t *shm_ptr;
int fd;
struct mfb_timer *timer;
} SWindowData_Way;

View File

@ -0,0 +1,973 @@
#include <MiniFB.h>
#include <MiniFB_internal.h>
#include <WindowData.h>
#include "WindowData_Win.h"
#if defined(USE_OPENGL_API)
#include "gl/MiniFB_GL.h"
#endif
#include <stdio.h>
#include <stdlib.h>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copied (and modified) from Windows Kit 10 to avoid setting _WIN32_WINNT to a higher version
typedef enum mfb_PROCESS_DPI_AWARENESS {
mfb_PROCESS_DPI_UNAWARE = 0,
mfb_PROCESS_SYSTEM_DPI_AWARE = 1,
mfb_PROCESS_PER_MONITOR_DPI_AWARE = 2
} mfb_PROCESS_DPI_AWARENESS;
typedef enum mfb_MONITOR_DPI_TYPE {
mfb_MDT_EFFECTIVE_DPI = 0,
mfb_MDT_ANGULAR_DPI = 1,
mfb_MDT_RAW_DPI = 2,
mfb_MDT_DEFAULT = mfb_MDT_EFFECTIVE_DPI
} mfb_MONITOR_DPI_TYPE;
#define mfb_DPI_AWARENESS_CONTEXT_UNAWARE ((HANDLE) -1)
#define mfb_DPI_AWARENESS_CONTEXT_SYSTEM_AWARE ((HANDLE) -2)
#define mfb_DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE ((HANDLE) -3)
#define mfb_DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((HANDLE) -4)
#define mfb_DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED ((HANDLE) -5)
// user32.dll
typedef BOOL(WINAPI *PFN_SetProcessDPIAware)(void);
typedef BOOL(WINAPI *PFN_SetProcessDpiAwarenessContext)(HANDLE);
typedef UINT(WINAPI *PFN_GetDpiForWindow)(HWND);
typedef BOOL(WINAPI *PFN_EnableNonClientDpiScaling)(HWND);
HMODULE mfb_user32_dll = 0x0;
PFN_SetProcessDPIAware mfb_SetProcessDPIAware = 0x0;
PFN_SetProcessDpiAwarenessContext mfb_SetProcessDpiAwarenessContext = 0x0;
PFN_GetDpiForWindow mfb_GetDpiForWindow = 0x0;
PFN_EnableNonClientDpiScaling mfb_EnableNonClientDpiScaling = 0x0;
// shcore.dll
typedef HRESULT(WINAPI *PFN_SetProcessDpiAwareness)(mfb_PROCESS_DPI_AWARENESS);
typedef HRESULT(WINAPI *PFN_GetDpiForMonitor)(HMONITOR, mfb_MONITOR_DPI_TYPE, UINT *, UINT *);
HMODULE mfb_shcore_dll = 0x0;
PFN_SetProcessDpiAwareness mfb_SetProcessDpiAwareness = 0x0;
PFN_GetDpiForMonitor mfb_GetDpiForMonitor = 0x0;
//--
void
load_functions() {
if(mfb_user32_dll == 0x0) {
mfb_user32_dll = LoadLibraryA("user32.dll");
if (mfb_user32_dll != 0x0) {
mfb_SetProcessDPIAware = (PFN_SetProcessDPIAware) GetProcAddress(mfb_user32_dll, "SetProcessDPIAware");
mfb_SetProcessDpiAwarenessContext = (PFN_SetProcessDpiAwarenessContext) GetProcAddress(mfb_user32_dll, "SetProcessDpiAwarenessContext");
mfb_GetDpiForWindow = (PFN_GetDpiForWindow) GetProcAddress(mfb_user32_dll, "GetDpiForWindow");
mfb_EnableNonClientDpiScaling = (PFN_EnableNonClientDpiScaling) GetProcAddress(mfb_user32_dll, "EnableNonClientDpiScaling");
}
}
if(mfb_shcore_dll == 0x0) {
mfb_shcore_dll = LoadLibraryA("shcore.dll");
if (mfb_shcore_dll != 0x0) {
mfb_SetProcessDpiAwareness = (PFN_SetProcessDpiAwareness) GetProcAddress(mfb_shcore_dll, "SetProcessDpiAwareness");
mfb_GetDpiForMonitor = (PFN_GetDpiForMonitor) GetProcAddress(mfb_shcore_dll, "GetDpiForMonitor");
}
}
}
//--
// NOT Thread safe. Just convenient (Don't do this at home guys)
char *
GetErrorMessage() {
static char buffer[256];
buffer[0] = 0;
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, // Not used with FORMAT_MESSAGE_FROM_SYSTEM
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
buffer,
sizeof(buffer),
NULL);
return buffer;
}
//--
void
dpi_aware() {
if (mfb_SetProcessDpiAwarenessContext != 0x0) {
if(mfb_SetProcessDpiAwarenessContext(mfb_DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) == false) {
uint32_t error = GetLastError();
if(error == ERROR_INVALID_PARAMETER) {
error = NO_ERROR;
if(mfb_SetProcessDpiAwarenessContext(mfb_DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE) == false) {
error = GetLastError();
}
}
if(error != NO_ERROR) {
fprintf(stderr, "Error (SetProcessDpiAwarenessContext): %s\n", GetErrorMessage());
}
}
}
else if (mfb_SetProcessDpiAwareness != 0x0) {
if(mfb_SetProcessDpiAwareness(mfb_PROCESS_PER_MONITOR_DPI_AWARE) != S_OK) {
fprintf(stderr, "Error (SetProcessDpiAwareness): %s\n", GetErrorMessage());
}
}
else if (mfb_SetProcessDPIAware != 0x0) {
if(mfb_SetProcessDPIAware() == false) {
fprintf(stderr, "Error (SetProcessDPIAware): %s\n", GetErrorMessage());
}
}
}
//--
void
get_monitor_scale(HWND hWnd, float *scale_x, float *scale_y) {
UINT x, y;
if(mfb_GetDpiForMonitor != 0x0) {
HMONITOR monitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
mfb_GetDpiForMonitor(monitor, mfb_MDT_EFFECTIVE_DPI, &x, &y);
}
else {
const HDC dc = GetDC(hWnd);
x = GetDeviceCaps(dc, LOGPIXELSX);
y = GetDeviceCaps(dc, LOGPIXELSY);
ReleaseDC(NULL, dc);
}
if (scale_x) {
*scale_x = x / (float) USER_DEFAULT_SCREEN_DPI;
if(*scale_x == 0) {
*scale_x = 1;
}
}
if (scale_y) {
*scale_y = y / (float) USER_DEFAULT_SCREEN_DPI;
if (*scale_y == 0) {
*scale_y = 1;
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void
mfb_get_monitor_scale(struct mfb_window *window, float *scale_x, float *scale_y) {
HWND hWnd = 0x0;
if(window != 0x0) {
SWindowData *window_data = (SWindowData *) window;
SWindowData_Win *window_data_win = (SWindowData_Win *) window_data->specific;
hWnd = window_data_win->window;
}
get_monitor_scale(hWnd, scale_x, scale_y);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
long s_window_style = WS_POPUP | WS_SYSMENU | WS_CAPTION;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void init_keycodes();
uint32_t translate_mod();
mfb_key translate_key(unsigned int wParam, unsigned long lParam);
void destroy_window_data(SWindowData *window_data);
LRESULT CALLBACK
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
LRESULT res = 0;
SWindowData *window_data = (SWindowData *) GetWindowLongPtr(hWnd, GWLP_USERDATA);
SWindowData_Win *window_data_win = 0x0;
if (window_data != 0x0) {
window_data_win = (SWindowData_Win *) window_data->specific;
}
switch (message)
{
case WM_NCCREATE:
{
if(mfb_EnableNonClientDpiScaling)
mfb_EnableNonClientDpiScaling(hWnd);
return DefWindowProc(hWnd, message, wParam, lParam);
}
// TODO
//case 0x02E4://WM_GETDPISCALEDSIZE:
//{
// SIZE* size = (SIZE*) lParam;
// WORD dpi = LOWORD(wParam);
// return true;
// break;
//}
// TODO
//case WM_DPICHANGED:
//{
// const float xscale = HIWORD(wParam);
// const float yscale = LOWORD(wParam);
// break;
//}
#if !defined(USE_OPENGL_API)
case WM_PAINT:
{
if (window_data && window_data->draw_buffer && window_data_win) {
StretchDIBits(window_data_win->hdc, window_data->dst_offset_x, window_data->dst_offset_y, window_data->dst_width, window_data->dst_height, 0, 0, window_data->buffer_width, window_data->buffer_height, window_data->draw_buffer,
window_data_win->bitmapInfo, DIB_RGB_COLORS, SRCCOPY);
}
ValidateRect(hWnd, 0x0);
break;
}
#endif
case WM_DESTROY:
case WM_CLOSE:
{
if (window_data) {
window_data->close = true;
}
break;
}
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
case WM_KEYUP:
case WM_SYSKEYUP:
{
if (window_data) {
mfb_key key_code = translate_key((unsigned int)wParam, (unsigned long)lParam);
int is_pressed = !((lParam >> 31) & 1);
window_data->mod_keys = translate_mod();
if (key_code == KB_KEY_UNKNOWN)
return FALSE;
window_data->key_status[key_code] = (uint8_t) is_pressed;
kCall(keyboard_func, key_code, window_data->mod_keys, is_pressed);
}
break;
}
case WM_CHAR:
case WM_SYSCHAR:
case WM_UNICHAR:
{
if (window_data) {
if (message == WM_UNICHAR && wParam == UNICODE_NOCHAR) {
// WM_UNICHAR is not sent by Windows, but is sent by some third-party input method engine
// Returning TRUE here announces support for this message
return TRUE;
}
kCall(char_input_func, (unsigned int) wParam);
}
break;
}
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
case WM_XBUTTONUP:
case WM_LBUTTONDOWN:
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDOWN:
case WM_RBUTTONDBLCLK:
case WM_MBUTTONDOWN:
case WM_MBUTTONDBLCLK:
case WM_XBUTTONDOWN:
case WM_XBUTTONDBLCLK:
{
if (window_data) {
mfb_mouse_button button = MOUSE_BTN_0;
window_data->mod_keys = translate_mod();
int is_pressed = 0;
switch(message) {
case WM_LBUTTONDOWN:
is_pressed = 1;
case WM_LBUTTONUP:
button = MOUSE_BTN_1;
break;
case WM_RBUTTONDOWN:
is_pressed = 1;
case WM_RBUTTONUP:
button = MOUSE_BTN_2;
break;
case WM_MBUTTONDOWN:
is_pressed = 1;
case WM_MBUTTONUP:
button = MOUSE_BTN_3;
break;
default:
button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? MOUSE_BTN_5 : MOUSE_BTN_6);
if (message == WM_XBUTTONDOWN) {
is_pressed = 1;
}
}
window_data->mouse_button_status[button & 0x07] = is_pressed;
kCall(mouse_btn_func, button, window_data->mod_keys, is_pressed);
}
break;
}
case WM_MOUSEWHEEL:
if (window_data) {
window_data->mouse_wheel_y = (SHORT)HIWORD(wParam) / (float)WHEEL_DELTA;
kCall(mouse_wheel_func, translate_mod(), 0.0f, window_data->mouse_wheel_y);
}
break;
case WM_MOUSEHWHEEL:
// This message is only sent on Windows Vista and later
// NOTE: The X-axis is inverted for consistency with macOS and X11
if (window_data) {
window_data->mouse_wheel_x = -((SHORT)HIWORD(wParam) / (float)WHEEL_DELTA);
kCall(mouse_wheel_func, translate_mod(), window_data->mouse_wheel_x, 0.0f);
}
break;
case WM_MOUSEMOVE:
if (window_data) {
if (window_data_win->mouse_inside == false) {
window_data_win->mouse_inside = true;
TRACKMOUSEEVENT tme;
ZeroMemory(&tme, sizeof(tme));
tme.cbSize = sizeof(tme);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = hWnd;
TrackMouseEvent(&tme);
}
window_data->mouse_pos_x = (int)(short) LOWORD(lParam);
window_data->mouse_pos_y = (int)(short) HIWORD(lParam);
kCall(mouse_move_func, window_data->mouse_pos_x, window_data->mouse_pos_y);
}
break;
case WM_MOUSELEAVE:
if (window_data) {
window_data_win->mouse_inside = false;
}
break;
case WM_SIZE:
if (window_data) {
float scale_x, scale_y;
uint32_t width, height;
if(wParam == SIZE_MINIMIZED) {
return res;
}
get_monitor_scale(hWnd, &scale_x, &scale_y);
window_data->window_width = LOWORD(lParam);
window_data->window_height = HIWORD(lParam);
resize_dst(window_data, window_data->window_width, window_data->window_height);
#if !defined(USE_OPENGL_API)
BitBlt(window_data_win->hdc, 0, 0, window_data->window_width, window_data->window_height, 0, 0, 0, BLACKNESS);
#else
resize_GL(window_data);
#endif
if(window_data->window_width != 0 && window_data->window_height != 0) {
width = (uint32_t) (window_data->window_width / scale_x);
height = (uint32_t) (window_data->window_height / scale_y);
kCall(resize_func, width, height);
}
}
break;
case WM_SETFOCUS:
if (window_data) {
window_data->is_active = true;
kCall(active_func, true);
}
break;
case WM_KILLFOCUS:
if (window_data) {
window_data->is_active = false;
kCall(active_func, false);
}
break;
default:
{
res = DefWindowProc(hWnd, message, wParam, lParam);
}
}
return res;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct mfb_window *
mfb_open_ex(const char *title, unsigned width, unsigned height, unsigned flags) {
RECT rect = { 0 };
int x = 0, y = 0;
load_functions();
dpi_aware();
init_keycodes();
SWindowData *window_data = malloc(sizeof(SWindowData));
if (window_data == 0x0) {
return 0x0;
}
memset(window_data, 0, sizeof(SWindowData));
SWindowData_Win *window_data_win = malloc(sizeof(SWindowData_Win));
if(window_data_win == 0x0) {
free(window_data);
return 0x0;
}
memset(window_data_win, 0, sizeof(SWindowData_Win));
window_data->specific = window_data_win;
window_data->buffer_width = width;
window_data->buffer_height = height;
window_data->buffer_stride = width * 4;
s_window_style = WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX & ~WS_THICKFRAME;
if (flags & WF_FULLSCREEN) {
flags = WF_FULLSCREEN; // Remove all other flags
rect.right = GetSystemMetrics(SM_CXSCREEN);
rect.bottom = GetSystemMetrics(SM_CYSCREEN);
s_window_style = WS_POPUP & ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE | WS_SYSMENU);
DEVMODE settings = { 0 };
EnumDisplaySettings(0, 0, &settings);
settings.dmPelsWidth = GetSystemMetrics(SM_CXSCREEN);
settings.dmPelsHeight = GetSystemMetrics(SM_CYSCREEN);
settings.dmBitsPerPel = 32;
settings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
if (ChangeDisplaySettings(&settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
flags = WF_FULLSCREEN_DESKTOP;
}
}
if (flags & WF_BORDERLESS) {
s_window_style = WS_POPUP;
}
if (flags & WF_RESIZABLE) {
s_window_style |= WS_MAXIMIZEBOX | WS_SIZEBOX;
}
if (flags & WF_FULLSCREEN_DESKTOP) {
s_window_style = WS_OVERLAPPEDWINDOW;
width = GetSystemMetrics(SM_CXFULLSCREEN);
height = GetSystemMetrics(SM_CYFULLSCREEN);
rect.right = width;
rect.bottom = height;
AdjustWindowRect(&rect, s_window_style, 0);
if (rect.left < 0) {
width += rect.left * 2;
rect.right += rect.left;
rect.left = 0;
}
if (rect.bottom > (LONG) height) {
height -= (rect.bottom - height);
rect.bottom += (rect.bottom - height);
rect.top = 0;
}
}
else if (!(flags & WF_FULLSCREEN)) {
float scale_x, scale_y;
get_monitor_scale(0, &scale_x, &scale_y);
rect.right = (LONG) (width * scale_x);
rect.bottom = (LONG) (height * scale_y);
AdjustWindowRect(&rect, s_window_style, 0);
rect.right -= rect.left;
rect.bottom -= rect.top;
x = (GetSystemMetrics(SM_CXSCREEN) - rect.right) / 2;
y = (GetSystemMetrics(SM_CYSCREEN) - rect.bottom + rect.top) / 2;
}
window_data_win->wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
window_data_win->wc.lpfnWndProc = WndProc;
window_data_win->wc.hCursor = LoadCursor(0, IDC_ARROW);
window_data_win->wc.lpszClassName = title;
RegisterClass(&window_data_win->wc);
calc_dst_factor(window_data, width, height);
window_data->window_width = rect.right;
window_data->window_height = rect.bottom;
window_data_win->window = CreateWindowEx(
0,
title, title,
s_window_style,
x, y,
window_data->window_width, window_data->window_height,
0, 0, 0, 0);
if (!window_data_win->window) {
free(window_data);
free(window_data_win);
return 0x0;
}
SetWindowLongPtr(window_data_win->window, GWLP_USERDATA, (LONG_PTR) window_data);
if (flags & WF_ALWAYS_ON_TOP)
SetWindowPos(window_data_win->window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
ShowWindow(window_data_win->window, SW_NORMAL);
window_data_win->hdc = GetDC(window_data_win->window);
#if !defined(USE_OPENGL_API)
window_data_win->bitmapInfo = (BITMAPINFO *) calloc(1, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 3);
if(window_data_win->bitmapInfo == 0x0) {
free(window_data);
free(window_data_win);
return 0x0;
}
window_data_win->bitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
window_data_win->bitmapInfo->bmiHeader.biPlanes = 1;
window_data_win->bitmapInfo->bmiHeader.biBitCount = 32;
window_data_win->bitmapInfo->bmiHeader.biCompression = BI_BITFIELDS;
window_data_win->bitmapInfo->bmiHeader.biWidth = window_data->buffer_width;
window_data_win->bitmapInfo->bmiHeader.biHeight = -(LONG)window_data->buffer_height;
window_data_win->bitmapInfo->bmiColors[0].rgbRed = 0xff;
window_data_win->bitmapInfo->bmiColors[1].rgbGreen = 0xff;
window_data_win->bitmapInfo->bmiColors[2].rgbBlue = 0xff;
#else
create_GL_context(window_data);
#endif
window_data_win->timer = mfb_timer_create();
mfb_set_keyboard_callback((struct mfb_window *) window_data, keyboard_default);
#if defined(_DEBUG) || defined(DEBUG)
#if defined(USE_OPENGL_API)
printf("Window created using OpenGL API\n");
#else
printf("Window created using GDI API\n");
#endif
#endif
window_data->is_initialized = true;
return (struct mfb_window *) window_data;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
mfb_update_state
mfb_update_ex(struct mfb_window *window, void *buffer, unsigned width, unsigned height) {
MSG msg;
if (window == 0x0) {
return STATE_INVALID_WINDOW;
}
SWindowData *window_data = (SWindowData *) window;
if (window_data->close) {
destroy_window_data(window_data);
return STATE_EXIT;
}
if (buffer == 0x0) {
return STATE_INVALID_BUFFER;
}
window_data->draw_buffer = buffer;
window_data->buffer_width = width;
window_data->buffer_stride = width * 4;
window_data->buffer_height = height;
SWindowData_Win *window_data_win = (SWindowData_Win *) window_data->specific;
#if !defined(USE_OPENGL_API)
window_data_win->bitmapInfo->bmiHeader.biWidth = window_data->buffer_width;
window_data_win->bitmapInfo->bmiHeader.biHeight = -(LONG) window_data->buffer_height;
InvalidateRect(window_data_win->window, 0x0, TRUE);
SendMessage(window_data_win->window, WM_PAINT, 0, 0);
#else
redraw_GL(window_data, buffer);
#endif
while (window_data->close == false && PeekMessage(&msg, window_data_win->window, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return STATE_OK;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
mfb_update_state
mfb_update_events(struct mfb_window *window) {
MSG msg;
if (window == 0x0) {
return STATE_INVALID_WINDOW;
}
SWindowData *window_data = (SWindowData *)window;
if (window_data->close) {
destroy_window_data(window_data);
return STATE_EXIT;
}
SWindowData_Win *window_data_win = (SWindowData_Win *) window_data->specific;
while (window_data->close == false && PeekMessage(&msg, window_data_win->window, 0, 0, PM_REMOVE)) {
//if(msg.message == WM_PAINT)
// return STATE_OK;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return STATE_OK;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
extern double g_time_for_frame;
extern bool g_use_hardware_sync;
bool
mfb_wait_sync(struct mfb_window *window) {
if (window == 0x0) {
return false;
}
SWindowData *window_data = (SWindowData *)window;
if (window_data->close) {
destroy_window_data(window_data);
return false;
}
if(g_use_hardware_sync) {
return true;
}
MSG msg;
SWindowData_Win *window_data_win = (SWindowData_Win *) window_data->specific;
double current;
uint32_t millis = 1;
while (1) {
current = mfb_timer_now(window_data_win->timer);
if (current >= g_time_for_frame * 0.96) {
mfb_timer_reset(window_data_win->timer);
return true;
}
else if(current >= g_time_for_frame * 0.8) {
millis = 0;
}
Sleep(millis);
if(millis == 1 && PeekMessage(&msg, window_data_win->window, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
if (window_data->close) {
destroy_window_data(window_data);
return false;
}
}
}
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void
destroy_window_data(SWindowData *window_data) {
if (window_data == 0x0)
return;
SWindowData_Win *window_data_win = (SWindowData_Win *) window_data->specific;
#if !defined(USE_OPENGL_API)
if (window_data_win->bitmapInfo != 0x0) {
free(window_data_win->bitmapInfo);
window_data_win->bitmapInfo = 0x0;
}
#else
destroy_GL_context(window_data);
#endif
if (window_data_win->window != 0 && window_data_win->hdc != 0) {
ReleaseDC(window_data_win->window, window_data_win->hdc);
DestroyWindow(window_data_win->window);
}
window_data_win->window = 0;
window_data_win->hdc = 0;
mfb_timer_destroy(window_data_win->timer);
window_data_win->timer = 0x0;
window_data->draw_buffer = 0x0;
window_data->close = true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
uint32_t
translate_mod() {
uint32_t mods = 0;
if (GetKeyState(VK_SHIFT) & 0x8000)
mods |= KB_MOD_SHIFT;
if (GetKeyState(VK_CONTROL) & 0x8000)
mods |= KB_MOD_CONTROL;
if (GetKeyState(VK_MENU) & 0x8000)
mods |= KB_MOD_ALT;
if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x8000)
mods |= KB_MOD_SUPER;
if (GetKeyState(VK_CAPITAL) & 1)
mods |= KB_MOD_CAPS_LOCK;
if (GetKeyState(VK_NUMLOCK) & 1)
mods |= KB_MOD_NUM_LOCK;
return mods;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
extern short int g_keycodes[512];
void
init_keycodes() {
if(g_keycodes[0x00B] != KB_KEY_0) {
g_keycodes[0x00B] = KB_KEY_0;
g_keycodes[0x002] = KB_KEY_1;
g_keycodes[0x003] = KB_KEY_2;
g_keycodes[0x004] = KB_KEY_3;
g_keycodes[0x005] = KB_KEY_4;
g_keycodes[0x006] = KB_KEY_5;
g_keycodes[0x007] = KB_KEY_6;
g_keycodes[0x008] = KB_KEY_7;
g_keycodes[0x009] = KB_KEY_8;
g_keycodes[0x00A] = KB_KEY_9;
g_keycodes[0x01E] = KB_KEY_A;
g_keycodes[0x030] = KB_KEY_B;
g_keycodes[0x02E] = KB_KEY_C;
g_keycodes[0x020] = KB_KEY_D;
g_keycodes[0x012] = KB_KEY_E;
g_keycodes[0x021] = KB_KEY_F;
g_keycodes[0x022] = KB_KEY_G;
g_keycodes[0x023] = KB_KEY_H;
g_keycodes[0x017] = KB_KEY_I;
g_keycodes[0x024] = KB_KEY_J;
g_keycodes[0x025] = KB_KEY_K;
g_keycodes[0x026] = KB_KEY_L;
g_keycodes[0x032] = KB_KEY_M;
g_keycodes[0x031] = KB_KEY_N;
g_keycodes[0x018] = KB_KEY_O;
g_keycodes[0x019] = KB_KEY_P;
g_keycodes[0x010] = KB_KEY_Q;
g_keycodes[0x013] = KB_KEY_R;
g_keycodes[0x01F] = KB_KEY_S;
g_keycodes[0x014] = KB_KEY_T;
g_keycodes[0x016] = KB_KEY_U;
g_keycodes[0x02F] = KB_KEY_V;
g_keycodes[0x011] = KB_KEY_W;
g_keycodes[0x02D] = KB_KEY_X;
g_keycodes[0x015] = KB_KEY_Y;
g_keycodes[0x02C] = KB_KEY_Z;
g_keycodes[0x028] = KB_KEY_APOSTROPHE;
g_keycodes[0x02B] = KB_KEY_BACKSLASH;
g_keycodes[0x033] = KB_KEY_COMMA;
g_keycodes[0x00D] = KB_KEY_EQUAL;
g_keycodes[0x029] = KB_KEY_GRAVE_ACCENT;
g_keycodes[0x01A] = KB_KEY_LEFT_BRACKET;
g_keycodes[0x00C] = KB_KEY_MINUS;
g_keycodes[0x034] = KB_KEY_PERIOD;
g_keycodes[0x01B] = KB_KEY_RIGHT_BRACKET;
g_keycodes[0x027] = KB_KEY_SEMICOLON;
g_keycodes[0x035] = KB_KEY_SLASH;
g_keycodes[0x056] = KB_KEY_WORLD_2;
g_keycodes[0x00E] = KB_KEY_BACKSPACE;
g_keycodes[0x153] = KB_KEY_DELETE;
g_keycodes[0x14F] = KB_KEY_END;
g_keycodes[0x01C] = KB_KEY_ENTER;
g_keycodes[0x001] = KB_KEY_ESCAPE;
g_keycodes[0x147] = KB_KEY_HOME;
g_keycodes[0x152] = KB_KEY_INSERT;
g_keycodes[0x15D] = KB_KEY_MENU;
g_keycodes[0x151] = KB_KEY_PAGE_DOWN;
g_keycodes[0x149] = KB_KEY_PAGE_UP;
g_keycodes[0x045] = KB_KEY_PAUSE;
g_keycodes[0x146] = KB_KEY_PAUSE;
g_keycodes[0x039] = KB_KEY_SPACE;
g_keycodes[0x00F] = KB_KEY_TAB;
g_keycodes[0x03A] = KB_KEY_CAPS_LOCK;
g_keycodes[0x145] = KB_KEY_NUM_LOCK;
g_keycodes[0x046] = KB_KEY_SCROLL_LOCK;
g_keycodes[0x03B] = KB_KEY_F1;
g_keycodes[0x03C] = KB_KEY_F2;
g_keycodes[0x03D] = KB_KEY_F3;
g_keycodes[0x03E] = KB_KEY_F4;
g_keycodes[0x03F] = KB_KEY_F5;
g_keycodes[0x040] = KB_KEY_F6;
g_keycodes[0x041] = KB_KEY_F7;
g_keycodes[0x042] = KB_KEY_F8;
g_keycodes[0x043] = KB_KEY_F9;
g_keycodes[0x044] = KB_KEY_F10;
g_keycodes[0x057] = KB_KEY_F11;
g_keycodes[0x058] = KB_KEY_F12;
g_keycodes[0x064] = KB_KEY_F13;
g_keycodes[0x065] = KB_KEY_F14;
g_keycodes[0x066] = KB_KEY_F15;
g_keycodes[0x067] = KB_KEY_F16;
g_keycodes[0x068] = KB_KEY_F17;
g_keycodes[0x069] = KB_KEY_F18;
g_keycodes[0x06A] = KB_KEY_F19;
g_keycodes[0x06B] = KB_KEY_F20;
g_keycodes[0x06C] = KB_KEY_F21;
g_keycodes[0x06D] = KB_KEY_F22;
g_keycodes[0x06E] = KB_KEY_F23;
g_keycodes[0x076] = KB_KEY_F24;
g_keycodes[0x038] = KB_KEY_LEFT_ALT;
g_keycodes[0x01D] = KB_KEY_LEFT_CONTROL;
g_keycodes[0x02A] = KB_KEY_LEFT_SHIFT;
g_keycodes[0x15B] = KB_KEY_LEFT_SUPER;
g_keycodes[0x137] = KB_KEY_PRINT_SCREEN;
g_keycodes[0x138] = KB_KEY_RIGHT_ALT;
g_keycodes[0x11D] = KB_KEY_RIGHT_CONTROL;
g_keycodes[0x036] = KB_KEY_RIGHT_SHIFT;
g_keycodes[0x15C] = KB_KEY_RIGHT_SUPER;
g_keycodes[0x150] = KB_KEY_DOWN;
g_keycodes[0x14B] = KB_KEY_LEFT;
g_keycodes[0x14D] = KB_KEY_RIGHT;
g_keycodes[0x148] = KB_KEY_UP;
g_keycodes[0x052] = KB_KEY_KP_0;
g_keycodes[0x04F] = KB_KEY_KP_1;
g_keycodes[0x050] = KB_KEY_KP_2;
g_keycodes[0x051] = KB_KEY_KP_3;
g_keycodes[0x04B] = KB_KEY_KP_4;
g_keycodes[0x04C] = KB_KEY_KP_5;
g_keycodes[0x04D] = KB_KEY_KP_6;
g_keycodes[0x047] = KB_KEY_KP_7;
g_keycodes[0x048] = KB_KEY_KP_8;
g_keycodes[0x049] = KB_KEY_KP_9;
g_keycodes[0x04E] = KB_KEY_KP_ADD;
g_keycodes[0x053] = KB_KEY_KP_DECIMAL;
g_keycodes[0x135] = KB_KEY_KP_DIVIDE;
g_keycodes[0x11C] = KB_KEY_KP_ENTER;
g_keycodes[0x037] = KB_KEY_KP_MULTIPLY;
g_keycodes[0x04A] = KB_KEY_KP_SUBTRACT;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
mfb_key
translate_key(unsigned int wParam, unsigned long lParam) {
if (wParam == VK_CONTROL) {
MSG next;
DWORD time;
if (lParam & 0x01000000)
return KB_KEY_RIGHT_CONTROL;
time = GetMessageTime();
if (PeekMessageW(&next, 0x0, 0, 0, PM_NOREMOVE))
if (next.message == WM_KEYDOWN || next.message == WM_SYSKEYDOWN || next.message == WM_KEYUP || next.message == WM_SYSKEYUP)
if (next.wParam == VK_MENU && (next.lParam & 0x01000000) && next.time == time)
return KB_KEY_UNKNOWN;
return KB_KEY_LEFT_CONTROL;
}
if (wParam == VK_PROCESSKEY)
return KB_KEY_UNKNOWN;
return (mfb_key) g_keycodes[HIWORD(lParam) & 0x1FF];
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool
mfb_set_viewport(struct mfb_window *window, unsigned offset_x, unsigned offset_y, unsigned width, unsigned height) {
SWindowData *window_data = (SWindowData *) window;
SWindowData_Win *window_data_win = 0x0;
float scale_x, scale_y;
if(window_data == 0x0) {
return false;
}
if (offset_x + width > window_data->window_width) {
return false;
}
if (offset_y + height > window_data->window_height) {
return false;
}
window_data_win = (SWindowData_Win *) window_data->specific;
get_monitor_scale(window_data_win->window, &scale_x, &scale_y);
window_data->dst_offset_x = (uint32_t) (offset_x * scale_x);
window_data->dst_offset_y = (uint32_t) (offset_y * scale_y);
window_data->dst_width = (uint32_t) (width * scale_x);
window_data->dst_height = (uint32_t) (height * scale_y);
calc_dst_factor(window_data, window_data->window_width, window_data->window_height);
#if !defined(USE_OPENGL_API)
window_data_win = (SWindowData_Win *) window_data->specific;
BitBlt(window_data_win->hdc, 0, 0, window_data->window_width, window_data->window_height, 0, 0, 0, BLACKNESS);
#endif
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
extern double g_timer_frequency;
extern double g_timer_resolution;
uint64_t
mfb_timer_tick() {
int64_t counter;
QueryPerformanceCounter((LARGE_INTEGER *) &counter);
return counter;
}
void
mfb_timer_init() {
uint64_t frequency;
QueryPerformanceFrequency((LARGE_INTEGER *) &frequency);
g_timer_frequency = (double) ((int64_t) frequency);
g_timer_resolution = 1.0 / g_timer_frequency;
}

View File

@ -0,0 +1,19 @@
#pragma once
#include <MiniFB_enums.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
typedef struct {
HWND window;
WNDCLASS wc;
HDC hdc;
#if defined(USE_OPENGL_API)
HGLRC hGLRC;
uint32_t text_id;
#else
BITMAPINFO *bitmapInfo;
#endif
struct mfb_timer *timer;
bool mouse_inside;
} SWindowData_Win;

View File

@ -0,0 +1,28 @@
#pragma once
#include <MiniFB_enums.h>
#include <stdint.h>
#include <X11/Xlib.h>
#if defined(USE_OPENGL_API)
#include <GL/glx.h>
#endif
typedef struct {
Window window;
Display *display;
int screen;
GC gc;
#if defined(USE_OPENGL_API)
GLXContext context;
uint32_t text_id;
#else
XImage *image;
void *image_buffer;
XImage *image_scaler;
uint32_t image_scaler_width;
uint32_t image_scaler_height;
#endif
struct mfb_timer *timer;
} SWindowData_X11;

View File

@ -0,0 +1,865 @@
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/XKBlib.h>
#include <X11/keysym.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
// I cannot find a way to get dpi under VirtualBox
//#include <X11/Xresource.h>
//#include <X11/extensions/Xrandr.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <MiniFB.h>
#include <MiniFB_internal.h>
#include "WindowData.h"
#include "WindowData_X11.h"
#if defined(USE_OPENGL_API)
#include <gl/MiniFB_GL.h>
#endif
static Atom s_delete_window_atom;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void init_keycodes(SWindowData_X11 *window_data_x11);
extern void
stretch_image(uint32_t *srcImage, uint32_t srcX, uint32_t srcY, uint32_t srcWidth, uint32_t srcHeight, uint32_t srcPitch,
uint32_t *dstImage, uint32_t dstX, uint32_t dstY, uint32_t dstWidth, uint32_t dstHeight, uint32_t dstPitch);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct mfb_window *
mfb_open_ex(const char *title, unsigned width, unsigned height, unsigned flags) {
int depth, i, formatCount, convDepth = -1;
XPixmapFormatValues* formats;
XSetWindowAttributes windowAttributes;
XSizeHints sizeHints;
Visual* visual;
SWindowData *window_data = (SWindowData *) malloc(sizeof(SWindowData));
if (!window_data) {
return 0x0;
}
memset(window_data, 0, sizeof(SWindowData));
SWindowData_X11 *window_data_x11 = (SWindowData_X11 *) malloc(sizeof(SWindowData_X11));
if (!window_data_x11) {
free(window_data);
return 0x0;
}
memset(window_data_x11, 0, sizeof(SWindowData_X11));
window_data->specific = window_data_x11;
window_data_x11->display = XOpenDisplay(0);
if (!window_data_x11->display) {
free(window_data);
free(window_data_x11);
return 0x0;
}
init_keycodes(window_data_x11);
window_data_x11->screen = DefaultScreen(window_data_x11->display);
visual = DefaultVisual(window_data_x11->display, window_data_x11->screen);
formats = XListPixmapFormats(window_data_x11->display, &formatCount);
depth = DefaultDepth(window_data_x11->display, window_data_x11->screen);
Window defaultRootWindow = DefaultRootWindow(window_data_x11->display);
for (i = 0; i < formatCount; ++i)
{
if (depth == formats[i].depth)
{
convDepth = formats[i].bits_per_pixel;
break;
}
}
XFree(formats);
// We only support 32-bit right now
if (convDepth != 32)
{
XCloseDisplay(window_data_x11->display);
return 0x0;
}
int screenWidth = DisplayWidth(window_data_x11->display, window_data_x11->screen);
int screenHeight = DisplayHeight(window_data_x11->display, window_data_x11->screen);
windowAttributes.border_pixel = BlackPixel(window_data_x11->display, window_data_x11->screen);
windowAttributes.background_pixel = BlackPixel(window_data_x11->display, window_data_x11->screen);
windowAttributes.backing_store = NotUseful;
int posX, posY;
int windowWidth, windowHeight;
window_data->window_width = width;
window_data->window_height = height;
window_data->buffer_width = width;
window_data->buffer_height = height;
window_data->buffer_stride = width * 4;
calc_dst_factor(window_data, width, height);
if (flags & WF_FULLSCREEN_DESKTOP) {
posX = 0;
posY = 0;
windowWidth = screenWidth;
windowHeight = screenHeight;
}
else {
posX = (screenWidth - width) / 2;
posY = (screenHeight - height) / 2;
windowWidth = width;
windowHeight = height;
}
window_data_x11->window = XCreateWindow(
window_data_x11->display,
defaultRootWindow,
posX, posY,
windowWidth, windowHeight,
0,
depth,
InputOutput,
visual,
CWBackPixel | CWBorderPixel | CWBackingStore,
&windowAttributes);
if (!window_data_x11->window)
return 0x0;
XSelectInput(window_data_x11->display, window_data_x11->window,
KeyPressMask | KeyReleaseMask
| ButtonPressMask | ButtonReleaseMask | PointerMotionMask
| StructureNotifyMask | ExposureMask
| FocusChangeMask
| EnterWindowMask | LeaveWindowMask
);
XStoreName(window_data_x11->display, window_data_x11->window, title);
if (flags & WF_BORDERLESS) {
struct StyleHints {
unsigned long flags;
unsigned long functions;
unsigned long decorations;
long inputMode;
unsigned long status;
} sh = {
.flags = 2,
.functions = 0,
.decorations = 0,
.inputMode = 0,
.status = 0,
};
Atom sh_p = XInternAtom(window_data_x11->display, "_MOTIF_WM_HINTS", True);
XChangeProperty(window_data_x11->display, window_data_x11->window, sh_p, sh_p, 32, PropModeReplace, (unsigned char*)&sh, 5);
}
if (flags & WF_ALWAYS_ON_TOP) {
Atom sa_p = XInternAtom(window_data_x11->display, "_NET_WM_STATE_ABOVE", False);
XChangeProperty(window_data_x11->display, window_data_x11->window, XInternAtom(window_data_x11->display, "_NET_WM_STATE", False), XA_ATOM, 32, PropModeReplace, (unsigned char *)&sa_p, 1);
}
if (flags & WF_FULLSCREEN) {
Atom sf_p = XInternAtom(window_data_x11->display, "_NET_WM_STATE_FULLSCREEN", True);
XChangeProperty(window_data_x11->display, window_data_x11->window, XInternAtom(window_data_x11->display, "_NET_WM_STATE", True), XA_ATOM, 32, PropModeReplace, (unsigned char*)&sf_p, 1);
}
sizeHints.flags = PPosition | PMinSize | PMaxSize;
sizeHints.x = 0;
sizeHints.y = 0;
sizeHints.min_width = width;
sizeHints.min_height = height;
if (flags & WF_RESIZABLE) {
sizeHints.max_width = screenWidth;
sizeHints.max_height = screenHeight;
}
else {
sizeHints.max_width = width;
sizeHints.max_height = height;
}
s_delete_window_atom = XInternAtom(window_data_x11->display, "WM_DELETE_WINDOW", False);
XSetWMProtocols(window_data_x11->display, window_data_x11->window, &s_delete_window_atom, 1);
#if defined(USE_OPENGL_API)
if(create_GL_context(window_data) == false) {
return 0x0;
}
#else
window_data_x11->image = XCreateImage(window_data_x11->display, CopyFromParent, depth, ZPixmap, 0, 0x0, width, height, 32, width * 4);
#endif
XSetWMNormalHints(window_data_x11->display, window_data_x11->window, &sizeHints);
XClearWindow(window_data_x11->display, window_data_x11->window);
XMapRaised(window_data_x11->display, window_data_x11->window);
XFlush(window_data_x11->display);
window_data_x11->gc = DefaultGC(window_data_x11->display, window_data_x11->screen);
window_data_x11->timer = mfb_timer_create();
mfb_set_keyboard_callback((struct mfb_window *) window_data, keyboard_default);
#if defined(_DEBUG) || defined(DEBUG)
printf("Window created using X11 API\n");
#endif
window_data->is_initialized = true;
return (struct mfb_window *) window_data;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int translate_key(int scancode);
int translate_mod(int state);
int translate_mod_ex(int key, int state, int is_pressed);
static void
processEvent(SWindowData *window_data, XEvent *event) {
switch (event->type) {
case KeyPress:
case KeyRelease:
{
mfb_key key_code = (mfb_key) translate_key(event->xkey.keycode);
int is_pressed = (event->type == KeyPress);
window_data->mod_keys = translate_mod_ex(key_code, event->xkey.state, is_pressed);
window_data->key_status[key_code] = is_pressed;
kCall(keyboard_func, key_code, (mfb_key_mod) window_data->mod_keys, is_pressed);
}
break;
case ButtonPress:
case ButtonRelease:
{
mfb_mouse_button button = (mfb_mouse_button) event->xbutton.button;
int is_pressed = (event->type == ButtonPress);
window_data->mod_keys = translate_mod(event->xkey.state);
// Swap mouse right and middle for parity with other platforms:
// https://github.com/emoon/minifb/issues/65
switch (button) {
case Button2:
button = Button3;
break;
case Button3:
button = Button2;
break;
}
switch (button) {
case Button1:
case Button2:
case Button3:
window_data->mouse_button_status[button & 0x07] = is_pressed;
kCall(mouse_btn_func, button, (mfb_key_mod) window_data->mod_keys, is_pressed);
break;
case Button4:
window_data->mouse_wheel_y = 1.0f;
kCall(mouse_wheel_func, (mfb_key_mod) window_data->mod_keys, 0.0f, window_data->mouse_wheel_y);
break;
case Button5:
window_data->mouse_wheel_y = -1.0f;
kCall(mouse_wheel_func, (mfb_key_mod) window_data->mod_keys, 0.0f, window_data->mouse_wheel_y);
break;
case 6:
window_data->mouse_wheel_x = 1.0f;
kCall(mouse_wheel_func, (mfb_key_mod) window_data->mod_keys, window_data->mouse_wheel_x, 0.0f);
break;
case 7:
window_data->mouse_wheel_x = -1.0f;
kCall(mouse_wheel_func, (mfb_key_mod) window_data->mod_keys, window_data->mouse_wheel_x, 0.0f);
break;
default:
window_data->mouse_button_status[(button - 4) & 0x07] = is_pressed;
kCall(mouse_btn_func, (mfb_mouse_button) (button - 4), (mfb_key_mod) window_data->mod_keys, is_pressed);
break;
}
}
break;
case MotionNotify:
window_data->mouse_pos_x = event->xmotion.x;
window_data->mouse_pos_y = event->xmotion.y;
kCall(mouse_move_func, event->xmotion.x, event->xmotion.y);
break;
case ConfigureNotify:
{
window_data->window_width = event->xconfigure.width;
window_data->window_height = event->xconfigure.height;
resize_dst(window_data, event->xconfigure.width, event->xconfigure.height);
#if defined(USE_OPENGL_API)
resize_GL(window_data);
#else
SWindowData_X11 *window_data_x11 = (SWindowData_X11 *) window_data->specific;
if(window_data_x11->image_scaler != 0x0) {
window_data_x11->image_scaler->data = 0x0;
XDestroyImage(window_data_x11->image_scaler);
window_data_x11->image_scaler = 0x0;
window_data_x11->image_scaler_width = 0;
window_data_x11->image_scaler_height = 0;
}
XClearWindow(window_data_x11->display, window_data_x11->window);
#endif
kCall(resize_func, window_data->window_width, window_data->window_height);
}
break;
case EnterNotify:
case LeaveNotify:
break;
case FocusIn:
window_data->is_active = true;
kCall(active_func, true);
break;
case FocusOut:
window_data->is_active = false;
kCall(active_func, false);
break;
case DestroyNotify:
window_data->close = true;
return;
break;
case ClientMessage:
{
if ((Atom)event->xclient.data.l[0] == s_delete_window_atom) {
window_data->close = true;
return;
}
}
break;
}
}
static void
processEvents(SWindowData *window_data) {
XEvent event;
SWindowData_X11 *window_data_x11 = (SWindowData_X11 *) window_data->specific;
while ((window_data->close == false) && XPending(window_data_x11->display)) {
XNextEvent(window_data_x11->display, &event);
processEvent(window_data, &event);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void destroy_window_data(SWindowData *window_data);
mfb_update_state
mfb_update_ex(struct mfb_window *window, void *buffer, unsigned width, unsigned height) {
if (window == 0x0) {
return STATE_INVALID_WINDOW;
}
SWindowData *window_data = (SWindowData *) window;
if (window_data->close) {
destroy_window_data(window_data);
return STATE_EXIT;
}
if (buffer == 0x0) {
return STATE_INVALID_BUFFER;
}
#if !defined(USE_OPENGL_API)
SWindowData_X11 *window_data_x11 = (SWindowData_X11 *) window_data->specific;
bool different_size = false;
#endif
if(window_data->buffer_width != width || window_data->buffer_height != height) {
window_data->buffer_width = width;
window_data->buffer_stride = width * 4;
window_data->buffer_height = height;
#if !defined(USE_OPENGL_API)
different_size = true;
#endif
}
#if !defined(USE_OPENGL_API)
if (different_size || window_data->buffer_width != window_data->dst_width || window_data->buffer_height != window_data->dst_height) {
if (window_data_x11->image_scaler_width != window_data->dst_width || window_data_x11->image_scaler_height != window_data->dst_height) {
if (window_data_x11->image_scaler != 0x0) {
window_data_x11->image_scaler->data = 0x0;
XDestroyImage(window_data_x11->image_scaler);
}
if (window_data_x11->image_buffer != 0x0) {
free(window_data_x11->image_buffer);
window_data_x11->image_buffer = 0x0;
}
int depth = DefaultDepth(window_data_x11->display, window_data_x11->screen);
window_data_x11->image_buffer = malloc(window_data->dst_width * window_data->dst_height * 4);
if(window_data_x11->image_buffer == 0x0) {
return STATE_INTERNAL_ERROR;
}
window_data_x11->image_scaler_width = window_data->dst_width;
window_data_x11->image_scaler_height = window_data->dst_height;
window_data_x11->image_scaler = XCreateImage(window_data_x11->display, CopyFromParent, depth, ZPixmap, 0, 0x0, window_data_x11->image_scaler_width, window_data_x11->image_scaler_height, 32, window_data_x11->image_scaler_width * 4);
}
}
if (window_data_x11->image_scaler != 0x0) {
stretch_image((uint32_t *) buffer, 0, 0, window_data->buffer_width, window_data->buffer_height, window_data->buffer_width,
(uint32_t *) window_data_x11->image_buffer, 0, 0, window_data->dst_width, window_data->dst_height, window_data->dst_width);
window_data_x11->image_scaler->data = (char *) window_data_x11->image_buffer;
XPutImage(window_data_x11->display, window_data_x11->window, window_data_x11->gc, window_data_x11->image_scaler, 0, 0, window_data->dst_offset_x, window_data->dst_offset_y, window_data->dst_width, window_data->dst_height);
}
else {
window_data_x11->image->data = (char *) buffer;
XPutImage(window_data_x11->display, window_data_x11->window, window_data_x11->gc, window_data_x11->image, 0, 0, window_data->dst_offset_x, window_data->dst_offset_y, window_data->dst_width, window_data->dst_height);
}
XFlush(window_data_x11->display);
#else
redraw_GL(window_data, buffer);
#endif
processEvents(window_data);
return STATE_OK;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
mfb_update_state
mfb_update_events(struct mfb_window *window) {
if (window == 0x0) {
return STATE_INVALID_WINDOW;
}
SWindowData *window_data = (SWindowData *) window;
if (window_data->close) {
destroy_window_data(window_data);
return STATE_EXIT;
}
SWindowData_X11 *window_data_x11 = (SWindowData_X11 *) window_data->specific;
XFlush(window_data_x11->display);
processEvents(window_data);
return STATE_OK;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
extern double g_time_for_frame;
extern bool g_use_hardware_sync;
bool
mfb_wait_sync(struct mfb_window *window) {
if (window == 0x0) {
return false;
}
SWindowData *window_data = (SWindowData *) window;
if (window_data->close) {
destroy_window_data(window_data);
return false;
}
if(g_use_hardware_sync) {
return true;
}
SWindowData_X11 *window_data_x11 = (SWindowData_X11 *) window_data->specific;
XFlush(window_data_x11->display);
XEvent event;
double current;
uint32_t millis = 1;
while(1) {
current = mfb_timer_now(window_data_x11->timer);
if (current >= g_time_for_frame * 0.96) {
mfb_timer_reset(window_data_x11->timer);
return true;
}
else if(current >= g_time_for_frame * 0.8) {
millis = 0;
}
usleep(millis * 1000);
//sched_yield();
if(millis == 1 && XEventsQueued(window_data_x11->display, QueuedAlready) > 0) {
XNextEvent(window_data_x11->display, &event);
processEvent(window_data, &event);
if(window_data->close) {
destroy_window_data(window_data);
return false;
}
}
}
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void
destroy_window_data(SWindowData *window_data) {
if (window_data != 0x0) {
if (window_data->specific != 0x0) {
SWindowData_X11 *window_data_x11 = (SWindowData_X11 *) window_data->specific;
#if defined(USE_OPENGL_API)
destroy_GL_context(window_data);
#else
if (window_data_x11->image != 0x0) {
window_data_x11->image->data = 0x0;
XDestroyImage(window_data_x11->image);
XDestroyWindow(window_data_x11->display, window_data_x11->window);
XCloseDisplay(window_data_x11->display);
}
#endif
mfb_timer_destroy(window_data_x11->timer);
memset(window_data_x11, 0, sizeof(SWindowData_X11));
free(window_data_x11);
}
memset(window_data, 0, sizeof(SWindowData));
free(window_data);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
extern short int g_keycodes[512];
static int
translateKeyCodeB(int keySym) {
switch (keySym)
{
case XK_KP_0: return KB_KEY_KP_0;
case XK_KP_1: return KB_KEY_KP_1;
case XK_KP_2: return KB_KEY_KP_2;
case XK_KP_3: return KB_KEY_KP_3;
case XK_KP_4: return KB_KEY_KP_4;
case XK_KP_5: return KB_KEY_KP_5;
case XK_KP_6: return KB_KEY_KP_6;
case XK_KP_7: return KB_KEY_KP_7;
case XK_KP_8: return KB_KEY_KP_8;
case XK_KP_9: return KB_KEY_KP_9;
case XK_KP_Separator:
case XK_KP_Decimal: return KB_KEY_KP_DECIMAL;
case XK_KP_Equal: return KB_KEY_KP_EQUAL;
case XK_KP_Enter: return KB_KEY_KP_ENTER;
}
return KB_KEY_UNKNOWN;
}
static int translateKeyCodeA(int keySym) {
switch (keySym)
{
case XK_Escape: return KB_KEY_ESCAPE;
case XK_Tab: return KB_KEY_TAB;
case XK_Shift_L: return KB_KEY_LEFT_SHIFT;
case XK_Shift_R: return KB_KEY_RIGHT_SHIFT;
case XK_Control_L: return KB_KEY_LEFT_CONTROL;
case XK_Control_R: return KB_KEY_RIGHT_CONTROL;
case XK_Meta_L:
case XK_Alt_L: return KB_KEY_LEFT_ALT;
case XK_Mode_switch: // Mapped to Alt_R on many keyboards
case XK_ISO_Level3_Shift: // AltGr on at least some machines
case XK_Meta_R:
case XK_Alt_R: return KB_KEY_RIGHT_ALT;
case XK_Super_L: return KB_KEY_LEFT_SUPER;
case XK_Super_R: return KB_KEY_RIGHT_SUPER;
case XK_Menu: return KB_KEY_MENU;
case XK_Num_Lock: return KB_KEY_NUM_LOCK;
case XK_Caps_Lock: return KB_KEY_CAPS_LOCK;
case XK_Print: return KB_KEY_PRINT_SCREEN;
case XK_Scroll_Lock: return KB_KEY_SCROLL_LOCK;
case XK_Pause: return KB_KEY_PAUSE;
case XK_Delete: return KB_KEY_DELETE;
case XK_BackSpace: return KB_KEY_BACKSPACE;
case XK_Return: return KB_KEY_ENTER;
case XK_Home: return KB_KEY_HOME;
case XK_End: return KB_KEY_END;
case XK_Page_Up: return KB_KEY_PAGE_UP;
case XK_Page_Down: return KB_KEY_PAGE_DOWN;
case XK_Insert: return KB_KEY_INSERT;
case XK_Left: return KB_KEY_LEFT;
case XK_Right: return KB_KEY_RIGHT;
case XK_Down: return KB_KEY_DOWN;
case XK_Up: return KB_KEY_UP;
case XK_F1: return KB_KEY_F1;
case XK_F2: return KB_KEY_F2;
case XK_F3: return KB_KEY_F3;
case XK_F4: return KB_KEY_F4;
case XK_F5: return KB_KEY_F5;
case XK_F6: return KB_KEY_F6;
case XK_F7: return KB_KEY_F7;
case XK_F8: return KB_KEY_F8;
case XK_F9: return KB_KEY_F9;
case XK_F10: return KB_KEY_F10;
case XK_F11: return KB_KEY_F11;
case XK_F12: return KB_KEY_F12;
case XK_F13: return KB_KEY_F13;
case XK_F14: return KB_KEY_F14;
case XK_F15: return KB_KEY_F15;
case XK_F16: return KB_KEY_F16;
case XK_F17: return KB_KEY_F17;
case XK_F18: return KB_KEY_F18;
case XK_F19: return KB_KEY_F19;
case XK_F20: return KB_KEY_F20;
case XK_F21: return KB_KEY_F21;
case XK_F22: return KB_KEY_F22;
case XK_F23: return KB_KEY_F23;
case XK_F24: return KB_KEY_F24;
case XK_F25: return KB_KEY_F25;
// Numeric keypad
case XK_KP_Divide: return KB_KEY_KP_DIVIDE;
case XK_KP_Multiply: return KB_KEY_KP_MULTIPLY;
case XK_KP_Subtract: return KB_KEY_KP_SUBTRACT;
case XK_KP_Add: return KB_KEY_KP_ADD;
// These should have been detected in secondary keysym test above!
case XK_KP_Insert: return KB_KEY_KP_0;
case XK_KP_End: return KB_KEY_KP_1;
case XK_KP_Down: return KB_KEY_KP_2;
case XK_KP_Page_Down: return KB_KEY_KP_3;
case XK_KP_Left: return KB_KEY_KP_4;
case XK_KP_Right: return KB_KEY_KP_6;
case XK_KP_Home: return KB_KEY_KP_7;
case XK_KP_Up: return KB_KEY_KP_8;
case XK_KP_Page_Up: return KB_KEY_KP_9;
case XK_KP_Delete: return KB_KEY_KP_DECIMAL;
case XK_KP_Equal: return KB_KEY_KP_EQUAL;
case XK_KP_Enter: return KB_KEY_KP_ENTER;
// Last resort: Check for printable keys (should not happen if the XKB
// extension is available). This will give a layout dependent mapping
// (which is wrong, and we may miss some keys, especially on non-US
// keyboards), but it's better than nothing...
case XK_a: return KB_KEY_A;
case XK_b: return KB_KEY_B;
case XK_c: return KB_KEY_C;
case XK_d: return KB_KEY_D;
case XK_e: return KB_KEY_E;
case XK_f: return KB_KEY_F;
case XK_g: return KB_KEY_G;
case XK_h: return KB_KEY_H;
case XK_i: return KB_KEY_I;
case XK_j: return KB_KEY_J;
case XK_k: return KB_KEY_K;
case XK_l: return KB_KEY_L;
case XK_m: return KB_KEY_M;
case XK_n: return KB_KEY_N;
case XK_o: return KB_KEY_O;
case XK_p: return KB_KEY_P;
case XK_q: return KB_KEY_Q;
case XK_r: return KB_KEY_R;
case XK_s: return KB_KEY_S;
case XK_t: return KB_KEY_T;
case XK_u: return KB_KEY_U;
case XK_v: return KB_KEY_V;
case XK_w: return KB_KEY_W;
case XK_x: return KB_KEY_X;
case XK_y: return KB_KEY_Y;
case XK_z: return KB_KEY_Z;
case XK_1: return KB_KEY_1;
case XK_2: return KB_KEY_2;
case XK_3: return KB_KEY_3;
case XK_4: return KB_KEY_4;
case XK_5: return KB_KEY_5;
case XK_6: return KB_KEY_6;
case XK_7: return KB_KEY_7;
case XK_8: return KB_KEY_8;
case XK_9: return KB_KEY_9;
case XK_0: return KB_KEY_0;
case XK_space: return KB_KEY_SPACE;
case XK_minus: return KB_KEY_MINUS;
case XK_equal: return KB_KEY_EQUAL;
case XK_bracketleft: return KB_KEY_LEFT_BRACKET;
case XK_bracketright: return KB_KEY_RIGHT_BRACKET;
case XK_backslash: return KB_KEY_BACKSLASH;
case XK_semicolon: return KB_KEY_SEMICOLON;
case XK_apostrophe: return KB_KEY_APOSTROPHE;
case XK_grave: return KB_KEY_GRAVE_ACCENT;
case XK_comma: return KB_KEY_COMMA;
case XK_period: return KB_KEY_PERIOD;
case XK_slash: return KB_KEY_SLASH;
case XK_less: return KB_KEY_WORLD_1; // At least in some layouts...
default: break;
}
return KB_KEY_UNKNOWN;
}
void
init_keycodes(SWindowData_X11 *window_data_x11) {
size_t i;
int keySym;
// Clear keys
for (i = 0; i < sizeof(g_keycodes) / sizeof(g_keycodes[0]); ++i)
g_keycodes[i] = KB_KEY_UNKNOWN;
// Valid key code range is [8,255], according to the Xlib manual
for (i=8; i<=255; ++i) {
// Try secondary keysym, for numeric keypad keys
keySym = XkbKeycodeToKeysym(window_data_x11->display, i, 0, 1);
g_keycodes[i] = translateKeyCodeB(keySym);
if (g_keycodes[i] == KB_KEY_UNKNOWN) {
keySym = XkbKeycodeToKeysym(window_data_x11->display, i, 0, 0);
g_keycodes[i] = translateKeyCodeA(keySym);
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int
translate_key(int scancode) {
if (scancode < 0 || scancode > 255)
return KB_KEY_UNKNOWN;
return g_keycodes[scancode];
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int
translate_mod(int state) {
int mod_keys = 0;
if (state & ShiftMask)
mod_keys |= KB_MOD_SHIFT;
if (state & ControlMask)
mod_keys |= KB_MOD_CONTROL;
if (state & Mod1Mask)
mod_keys |= KB_MOD_ALT;
if (state & Mod4Mask)
mod_keys |= KB_MOD_SUPER;
if (state & LockMask)
mod_keys |= KB_MOD_CAPS_LOCK;
if (state & Mod2Mask)
mod_keys |= KB_MOD_NUM_LOCK;
return mod_keys;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int
translate_mod_ex(int key, int state, int is_pressed) {
int mod_keys = 0;
mod_keys = translate_mod(state);
switch (key)
{
case KB_KEY_LEFT_SHIFT:
case KB_KEY_RIGHT_SHIFT:
if (is_pressed)
mod_keys |= KB_MOD_SHIFT;
else
mod_keys &= ~KB_MOD_SHIFT;
break;
case KB_KEY_LEFT_CONTROL:
case KB_KEY_RIGHT_CONTROL:
if (is_pressed)
mod_keys |= KB_MOD_CONTROL;
else
mod_keys &= ~KB_MOD_CONTROL;
break;
case KB_KEY_LEFT_ALT:
case KB_KEY_RIGHT_ALT:
if (is_pressed)
mod_keys |= KB_MOD_ALT;
else
mod_keys &= ~KB_MOD_ALT;
break;
case KB_KEY_LEFT_SUPER:
case KB_KEY_RIGHT_SUPER:
if (is_pressed)
mod_keys |= KB_MOD_SUPER;
else
mod_keys &= ~KB_MOD_SUPER;
break;
}
return mod_keys;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool
mfb_set_viewport(struct mfb_window *window, unsigned offset_x, unsigned offset_y, unsigned width, unsigned height) {
SWindowData *window_data = (SWindowData *) window;
if (offset_x + width > window_data->window_width) {
return false;
}
if (offset_y + height > window_data->window_height) {
return false;
}
window_data->dst_offset_x = offset_x;
window_data->dst_offset_y = offset_y;
window_data->dst_width = width;
window_data->dst_height = height;
calc_dst_factor(window_data, window_data->window_width, window_data->window_height);
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void
mfb_get_monitor_scale(struct mfb_window *window, float *scale_x, float *scale_y) {
float x = 96.0, y = 96.0;
if(window != 0x0) {
//SWindowData *window_data = (SWindowData *) window;
//SWindowData_X11 *window_data_x11 = (SWindowData_X11 *) window_data->specific;
// I cannot find a way to get dpi under VirtualBox
// XrmGetResource "Xft.dpi", "Xft.Dpi"
// XRRGetOutputInfo
// DisplayWidthMM, DisplayHeightMM
// All returning invalid values or 0
}
if (scale_x) {
*scale_x = x / 96.0f;
if(*scale_x == 0) {
*scale_x = 1.0f;
}
}
if (scale_y) {
*scale_y = y / 96.0f;
if (*scale_y == 0) {
*scale_y = 1.0f;
}
}
}

41
src/main.zig Normal file
View File

@ -0,0 +1,41 @@
const std = @import("std");
const minifb = @import("minifb");
const Width = 800;
const Height = 600;
pub fn main() anyerror!void {
std.log.info("All your codebase are belong to us.", .{});
var win = minifb.Window.open("Hello minifb-zig", Width, Height) catch unreachable;
var color: u32 = 0xff000000;
const deltaColor: u32 = 0x00010101;
var increasing = true;
var rawBuffer: [Width*Height]u32 = undefined;
const buffer = rawBuffer[0..rawBuffer.len];
std.mem.set(u32, buffer, color);
while (win.waitSync()) {
if (increasing) {
if (color < 0xffffffff) {
color += deltaColor;
} else {
increasing = false;
}
} else {
if (color > 0xff000000) {
color -= deltaColor;
} else {
increasing = true;
}
}
std.mem.set(u32, buffer, color);
var i: u32 = 0;
while (i < Width) {
buffer[i] = 0xffff0000;
i += 1;
}
_ = win.update(buffer) catch unreachable;
}
}