Initial commit
This commit is contained in:
commit
6120807f92
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
zig-cache/
|
||||||
|
zig-out/
|
32
build.zig
Normal file
32
build.zig
Normal 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
72
lib/minifb/build.zig
Normal 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
45
lib/minifb/src/minifb.zig
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
91
lib/minifb/upstream/include/MiniFB.h
Normal file
91
lib/minifb/upstream/include/MiniFB.h
Normal 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
|
136
lib/minifb/upstream/include/MiniFB_cpp.h
Normal file
136
lib/minifb/upstream/include/MiniFB_cpp.h
Normal 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
|
185
lib/minifb/upstream/include/MiniFB_enums.h
Normal file
185
lib/minifb/upstream/include/MiniFB_enums.h
Normal 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);
|
||||||
|
|
7
lib/minifb/upstream/include/MiniFB_ios.h
Normal file
7
lib/minifb/upstream/include/MiniFB_ios.h
Normal 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);
|
598
lib/minifb/upstream/src/MiniFB_common.c
Normal file
598
lib/minifb/upstream/src/MiniFB_common.c
Normal 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";
|
||||||
|
}
|
69
lib/minifb/upstream/src/MiniFB_cpp.cpp
Normal file
69
lib/minifb/upstream/src/MiniFB_cpp.cpp
Normal 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);
|
||||||
|
}
|
113
lib/minifb/upstream/src/MiniFB_internal.c
Normal file
113
lib/minifb/upstream/src/MiniFB_internal.c
Normal 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
|
27
lib/minifb/upstream/src/MiniFB_internal.h
Normal file
27
lib/minifb/upstream/src/MiniFB_internal.h
Normal 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
|
37
lib/minifb/upstream/src/MiniFB_linux.c
Normal file
37
lib/minifb/upstream/src/MiniFB_linux.c
Normal 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
|
||||||
|
|
115
lib/minifb/upstream/src/MiniFB_timer.c
Normal file
115
lib/minifb/upstream/src/MiniFB_timer.c
Normal 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;
|
||||||
|
}
|
49
lib/minifb/upstream/src/WindowData.h
Normal file
49
lib/minifb/upstream/src/WindowData.h
Normal 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;
|
473
lib/minifb/upstream/src/android/AndroidMiniFB.c
Normal file
473
lib/minifb/upstream/src/android/AndroidMiniFB.c
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
9
lib/minifb/upstream/src/android/WindowData_Android.h
Normal file
9
lib/minifb/upstream/src/android/WindowData_Android.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <MiniFB_internal.h>
|
||||||
|
|
||||||
|
//-------------------------------------
|
||||||
|
typedef struct {
|
||||||
|
struct android_app *app;
|
||||||
|
struct mfb_timer *timer;
|
||||||
|
} SWindowData_Android;
|
368
lib/minifb/upstream/src/gl/MiniFB_GL.c
Normal file
368
lib/minifb/upstream/src/gl/MiniFB_GL.c
Normal 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, ¤tInterval);
|
||||||
|
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
|
13
lib/minifb/upstream/src/gl/MiniFB_GL.h
Normal file
13
lib/minifb/upstream/src/gl/MiniFB_GL.h
Normal 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
|
16
lib/minifb/upstream/src/ios/WindowData_IOS.h
Normal file
16
lib/minifb/upstream/src/ios/WindowData_IOS.h
Normal 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;
|
272
lib/minifb/upstream/src/ios/iOSMiniFB.m
Normal file
272
lib/minifb/upstream/src/ios/iOSMiniFB.m
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
lib/minifb/upstream/src/ios/iOSView.h
Normal file
9
lib/minifb/upstream/src/ios/iOSView.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#import <MetalKit/MetalKit.h>
|
||||||
|
#include "WindowData.h"
|
||||||
|
|
||||||
|
@interface iOSView : MTKView
|
||||||
|
{
|
||||||
|
@public SWindowData *window_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
84
lib/minifb/upstream/src/ios/iOSView.m
Normal file
84
lib/minifb/upstream/src/ios/iOSView.m
Normal 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
|
19
lib/minifb/upstream/src/ios/iOSViewController.h
Normal file
19
lib/minifb/upstream/src/ios/iOSViewController.h
Normal 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
|
70
lib/minifb/upstream/src/ios/iOSViewController.m
Normal file
70
lib/minifb/upstream/src/ios/iOSViewController.m
Normal 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
|
23
lib/minifb/upstream/src/ios/iOSViewDelegate.h
Normal file
23
lib/minifb/upstream/src/ios/iOSViewDelegate.h
Normal 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
|
||||||
|
|
254
lib/minifb/upstream/src/ios/iOSViewDelegate.m
Normal file
254
lib/minifb/upstream/src/ios/iOSViewDelegate.m
Normal 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
|
589
lib/minifb/upstream/src/macosx/MacMiniFB.m
Normal file
589
lib/minifb/upstream/src/macosx/MacMiniFB.m
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
lib/minifb/upstream/src/macosx/OSXView.h
Normal file
14
lib/minifb/upstream/src/macosx/OSXView.h
Normal 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
|
||||||
|
|
240
lib/minifb/upstream/src/macosx/OSXView.m
Normal file
240
lib/minifb/upstream/src/macosx/OSXView.m
Normal 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
|
35
lib/minifb/upstream/src/macosx/OSXViewDelegate.h
Normal file
35
lib/minifb/upstream/src/macosx/OSXViewDelegate.h
Normal 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
|
261
lib/minifb/upstream/src/macosx/OSXViewDelegate.m
Normal file
261
lib/minifb/upstream/src/macosx/OSXViewDelegate.m
Normal 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
|
17
lib/minifb/upstream/src/macosx/OSXWindow.h
Normal file
17
lib/minifb/upstream/src/macosx/OSXWindow.h
Normal 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
|
315
lib/minifb/upstream/src/macosx/OSXWindow.m
Normal file
315
lib/minifb/upstream/src/macosx/OSXWindow.m
Normal 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
|
27
lib/minifb/upstream/src/macosx/WindowData_OSX.h
Normal file
27
lib/minifb/upstream/src/macosx/WindowData_OSX.h
Normal 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;
|
1030
lib/minifb/upstream/src/wayland/WaylandMiniFB.c
Normal file
1030
lib/minifb/upstream/src/wayland/WaylandMiniFB.c
Normal file
File diff suppressed because it is too large
Load Diff
46
lib/minifb/upstream/src/wayland/WindowData_Way.h
Normal file
46
lib/minifb/upstream/src/wayland/WindowData_Way.h
Normal 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;
|
973
lib/minifb/upstream/src/windows/WinMiniFB.c
Normal file
973
lib/minifb/upstream/src/windows/WinMiniFB.c
Normal 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;
|
||||||
|
}
|
19
lib/minifb/upstream/src/windows/WindowData_Win.h
Normal file
19
lib/minifb/upstream/src/windows/WindowData_Win.h
Normal 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;
|
28
lib/minifb/upstream/src/x11/WindowData_X11.h
Normal file
28
lib/minifb/upstream/src/x11/WindowData_X11.h
Normal 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;
|
865
lib/minifb/upstream/src/x11/X11MiniFB.c
Normal file
865
lib/minifb/upstream/src/x11/X11MiniFB.c
Normal 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
41
src/main.zig
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user